BOJ

[C++] 백준 1629 - 곱셈

LOONATIC 2021. 11. 2. 19:25

https://www.acmicpc.net/problem/1629

 

1629번: 곱셈

첫째 줄에 A, B, C가 빈 칸을 사이에 두고 순서대로 주어진다. A, B, C는 모두 2,147,483,647 이하의 자연수이다.

www.acmicpc.net

 

1. 입력

2,147,483,647 이하의 자연수 A B C

 

2. 출력

A를 B번 곱한 수를 C로 나눈 나머지(\(A ^ B\) mod C)

 

3. 풀이

문제의 핵심은 \(A ^ B\)를 구하는 것이다. 다만 자료형의 크기 때문에 일정 수 이상이 되면 오버플로가 발생하므로, C로 나눈 나머지를 구하는 모듈러 연산이 추가로 요구된다.

 

단순한 반복문이나 재귀함수를 이용한 구현을 유도하는 문제는 당연히 아니다. 문제와 같은 거듭제곱 후 모듈러 연산은 대표적으로 RSA 알고리즘과 같은 암호학 분야에서 많이 사용되는데, 암호 알고리즘에 이러한 단순 구현을 적용하기에는 무리가 있다.

 

여러 방법이 있겠으나 나는 다음 두 가지를 이용했다.

1. 모든 자연수는 \(2 ^ n\)(n = 자연수)의 합으로 표현될 수 있다.
2. \(x ^ {N+M} = x ^ N \times x ^ M\)이다.

 

두 가지를 종합해 보면, y가 자연수라면 y는 \(2^n\)(n = 자연수)의 합으로 표현될 수 있으며, \(x^y\)는 \(x^{2^n}\)들의 곱으로 표현될 수 있다는 결론을 도출할 수 있다.

 

예를 들어 자연수 27은 16 + 8 + 2 + 1, 즉 \(2 ^ 4 + 2 ^ 3 + 2 ^ 1 + 2 ^ 0\)으로 표현될 수 있다.

 

즉, 자연수 27(\(00011011_{(2)}\))은 다음 이진수의 합으로 나타낼 수 있다.

 

\[ \begin{align}00010000_{(2)} \\
  + 00001000_{(2)} \\
  + 00000010_{(2)} \\
  + 00000001_{(2)} \\
\hline  = 00011011_{(2)} \end{align} \]

 

 \(x ^ {27}\)은 \(x^{2^4} \times x^{2^3} \times x^{2^1} \times x^{2^0}\)으로 표현될 수 있으므로, \(x^{27}\)은 \(x\)를 위의 각 이진수만큼 거듭제곱한 값의 곱과 같다.

 

 

정리하여 위 1629 문제에 대입해 보면, B를 2진수로 변환했을 때 1을 출력하는 비트의 각 자릿수를 n이라고 하면, \(2^n\)만큼 A를 거듭제곱 한 값을 모두 곱하면 \(A^B\)가 나온다는 것을 알 수 있다. 즉 B를 2진수로 변환한 뒤 각 자릿수가 1인지 확인하고, 그 자릿수에 해당하는 차수만큼 A를 거듭제곱 해주면 되는 것이다.

 

 

종합해 보면, 구현할 것은 다음과 같이 정리된다.

 

1. \(B_{(2)}\)를 한 칸 오른쪽으로 bit shift하고, 1과 AND 연산한다.

1-1. 참일 경우 value에 \(A\)를 곱한다.

2. \(A\)에 \(A^2\)을 대입한다.

3. B가 0이 될 때까지 2~3을 반복한다.

 

이제 구현할 것이 정리됐으니 구현할 일만 남았는데, 문제에서는 오버플로 방지를 위해 모듈러 연산을 요구하고 있으므로 2-1과 3에 모듈러 연산을 추가해야 한다.

 

또한 입력값은 2,147,483,647 이하의 값을 전제로 하고 있으므로, 변수 및 반환값 타입에서 오버플로가 발생하지 않도록 각별히 유의해야 한다. (아무리 생각해도 맞을 것 같은 문제를 제출해도 틀렸다는 메세지가 계속해서 나와서 처음부터 다시 살펴보니, 반환 타입을 typedef로 재정의 하는 과정에서 일부 변수의 타입을 수정하지 않아 오버플로가 발생해 생기는 문제였다.)

 

최종적으로 아래와 같이 구현했다.

 

#include <iostream>
#include <cmath>

typedef unsigned int RETURN_TYPE;
RETURN_TYPE PowAndMod(RETURN_TYPE x, RETURN_TYPE y, RETURN_TYPE m);

int main()
{
    RETURN_TYPE n, e, m;
    std::cin >> n >> e >> m;

    RETURN_TYPE result = PowAndMod(n, e, m);
    std::cout << result;
}

RETURN_TYPE PowAndMod(RETURN_TYPE x, RETURN_TYPE y, RETURN_TYPE m)
{
	RETURN_TYPE value = 1;

	while (y > 0)
	{
		if (y & 1)
			value = value * x % m;
		x = x * x % m;
		y >>= 1;
	}

	return value;
}

 

'BOJ' 카테고리의 다른 글

[C++] 백준 9184 - 신나는 함수 실행  (0) 2021.11.03
[C++] 백준 1904 - 01타일  (0) 2021.11.03