자료형 관련 질문입니다.

fopenfclose의 이미지

Background
재민이는 프로그램 코딩을 할 때, math.h를 활용하여 다양한 수학 함수를 활용하고 있다. 이런 점이 못마땅한 seaguy 샘이 재민이에게 더 이상 math.h 함수를 사용하지 않고 연산을 하도록 다음과 같은 문제를 제시하였다.

n^m을 계산하는 문제이다. 일반적으로 pow()함수를 이용하면 간단하게 계산할 수 있지만 사용할 수가 없으니 이제는 다른 방법으로 문제를 해결해야 한다.

재민이를 도와 n^m을 계산하는 프로그램을 작성해보자.
Input
첫 번째 줄에 정수 n, m이 입력으로 주어진다. (단, 0 <= n, m <= 100,000)
Output
n^m의 결과를 출력한다. 단, 수가 너무 크므로 1,000,000,007로 나눈 값을 출력한다.
IO Example

입력
3 2

출력
9

의 문제를 풀고 있는데

저는 아래와 같이 소스를 작성했습니다.

#include <iostream>
 
using namespace std;
 long long pownew(int n, int m);
 int main(int argc, char * argv[])
 {
  int n, m;
  cin >> n >> m;
  cout << pownew(n, m) % 1000000007;
  return 0;
 }
 
long long pownew(int n, int m)
 {
  if (m == 1)
   return n;
  long long v = pownew(n, m / 2);
  if (!(m % 2))
   return v * v;
  else
   return v * v * n;
 }

사이트에서 결과를 돌려보니 3개의 테스트 케이스까진 통과인데

4번째 테스트 케이스에선 411081351 이게 답이라고 뜨면서 틀렸다고 나옵니다.

3번째 까진 맞다가 4번째에선 틀린것이 좀 이상한데 코드에서 로직이 잘못된 건지

아니면 자료형이 잘못되서 결과가 잘못나오는건지 모르겠습니다.

정수형에서 가장 큰 범위를 갖고 있는 자료형이 long long 으로 알고 있는데,, 어떻 자료형을 써야 하나요?? 큰 범위를 커버하려면요

익명 사용자의 이미지

기초적인 정수론, 특히 모듈러 산술을 공부해 두는 편이 좋습니다.
pownew 함수는 long long만을 가지고 끝까지 계산한 뒤 마지막에 한 번 1000000007으로 나머지 연산하려고 하셨던 거 같은데요.
계산 과정에서 숫자가 100,000^100,000까지 올라가버릴 수 있기 때문에 long long가 어지간히 커도 계산 중간에 정수 오버플로우가 발생하고 값이 손실됩니다.

해결 방법은 간단합니다.
long long이 1000000006 * 1000000006 = 1000000012000000036 정도는 커버할 수 있기 때문에,(이 숫자는 60비트 수로군요.)
매번 곱셈을 할 때마다 1000000007으로 나누어주어 1000000006 이하의 수로 만들어버리면 정수 오버플로우가 절대 일어나지 않습니다.
그런 식으로 계산해도 최종 결과값은 동일하다는 게 모듈러 산술이 보장하는 것이고요.

그런데 매번 곱셈마다 1000000007으로 나누어주는 건 코드가 지저분해지고, 자칫 깜빡해서 빠트리기 쉽지요. 게다가 그런 실수는 나중에 찾기가 무척 어려울 수도 있어서, 저는 보통 코드가 조금 길어지더라도 안전한 방법을 선택해요. 뭐 예컨대 아래와 같습니다.

#include <iostream>
using namespace std;
 
// 타이핑을 줄이기 위한 typedef
typedef unsigned long long int ULLI;
typedef unsigned int UI;
 
// 모듈러 산술을 위한 클래스
// 연산자 오버로딩을 이용하여 modulo로 나누는 것을 빠트리지 않도록 강제합니다.
// 모든 멤버함수가 인라인 처리되므로 성능 손실은 거의 없습니다.
template <ULLI modulo>
class ModularArith {
	ULLI _i;
public:
	ModularArith(ULLI i) :_i(i % modulo) {
		return;
	}
	ModularArith<modulo> &operator*=(const ModularArith<modulo> &rhs) {
		_i *= rhs._i;
		_i %= modulo;
		return *this;
	}
	ModularArith<modulo> operator*(const ModularArith<modulo> &rhs) const {
		return ModularArith<modulo>(_i * rhs._i % modulo);
	}
	operator ULLI(void) const {
		return _i;
	}
};
 
// 매직 넘버 1000000007은 한 번만 사용
typedef ModularArith<1000000007> M;
 
// 질문자님의 코드를 (타입 및 코딩 스타일을 제외하고는) 거의 그대로 옮겼습니다.
M pownew(M n, UI m);
// 이건 보너스. 재귀 호출 없이 만들어볼 수 있지 않을까 싶어서 한번 해봤어요. :)
M pownew2(M n, UI m);
 
int main(void) {
	UI n, m;
	cin >> n >> m;
	cout << pownew(n, m) << endl;
	cout << pownew2(n, m) << endl;
	return 0;
}
 
M pownew(M n, UI m) {
	if (m == 1)
		return n;
	M v = pownew(n, m / 2);
	if (m % 2)
		return v*v*n;
	else
		return v*v;
}
 
M pownew2(M n, UI m) {
	M ret(1);
	for (UI i = 1; m; i<<=1, n *= n) {
		if (i & m) {
			ret *= n;
			m ^= i;
		}
	}
	return ret;
}

질문자님의 코드를 그대로 옮기기만 하는 건 재미가 없어서, 제 방식대로도 한 번 풀어 봤습니다.(pownew2)
한 번 참고해보세요. 1) 재귀 호출을 피하고, 2) * 연산자 대신 *= 연산자를 사용하는 등 조금 더 개선되었다고 볼 여지가 있습니다.

덧. 소스코드에서 &lt;은 <으로, &gt;은 >으로 바꿔주세요. kldp <code> 태그의 고질적인 문제인데 언제쯤 고쳐질지 원...

익명 사용자의 이미지

마이크로 최적화의 여지는 광활하고...
그런 데 쓸데없이 자기 시간 쏟아붇는 사람도 많은 법이죠.

M pownew3(M n, UI m) {
	M ret(1);
	while (m) {
		if (m & 1)
			ret *= n;
		m >>= 1;
		n *= n;
	}
	return ret;
}

이러면 아주 약간 나아질지도 모릅니다. 물론 나아진다고 해봤자 루프 하나당 몇 클럭 사이클 정도일 것이고, 하루종일 돌려 봤자 제가 이 코드 짜는 데 걸린 시간만큼이라도 아낄 수 있을까 말까 뭐 그렇겠죠 ㅎ.

뭐, 그래도 재미로 하는 거 아니겠습니까.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.