본문 바로가기

학업 정리

거듭제곱수에 대한 나머지 연산

두 양수 A, B에 대해, A^B라는 큰 거듭제곱수를 생각해보자. 예를 들면 66^77 말이다.

이렇게 큰 수에 대해 나머지 연산(modulo, %)을 빠르게 하는 방법은 다음의 특징을 이용한다.

 

(a * b) % n = ((a % n) * (b % n)) % n

 

예제

66^77의 13에 대한 나머지 연산은 다음과 같다.

66^77 % 13

= ((66^2 % 13)^38 * 66) % 13

= (1^38 * 66) % 13

= 66 % 13

= 1

 

66^77의 89에 대한 나머지 연산은 다음과 같다.

66^77 % 89

= ((66^2 % 89)^38 * 66) % 89

= (84^38 * 66) % 89

= ((84^2 % 89)^19 * 66) % 89

= (25^19 * 66) % 89

= ((((25^2 % 89)^9 * 25) % 89) * 66) % 89

= (((2^9 * 25) % 89) * 66) % 89

= (73 * 66) % 89

= 12

 

 

C언어 예제

반복문을 이용하는 방법과 재귀적 호출을 이용하는 방법으로 구현해보았다.

#include<stdio.h>

int exponentation_modulo_loop(int b, int e, int n)
{
	int m = 1;

	// handle exception
	if (b <= 0 || e < 0 || n <= 0)
	{
		printf("error\n");
		exit(-1);
	}

	if (e == 0) return 1;

	b = b % n;
	while (e > 1)
	{
		if (e % 2 == 1)
			m = (m * b) % n;
		b = (b * b) % n;
		e /= 2;
	}

	return (b * m) % n;
}

int exponentation_modulo_recursion(int b, int e, int n)
{
	// handle exception
	if (b <= 0 || e < 0 || n <= 0)
	{
		printf("error\n");
		exit(-1);
	}

	b = b % n;

	if (e == 0) return 1;
	if (e == 1) return b;

	b = exponentation_modulo_recursion(b * b, e / 2, n) * ((e & 1) == 1 ? b : 1);
	return b % n;
}

int main(void)
{
	int b, e, n;
	
	printf("Input positive numbers b, e, and n for calculate [ b^e %% n ]\n");
	scanf("%d %d %d", &b, &e, &n);
	printf("%d^%d %% %d = %d\n", b, e, n, exponentation_modulo_recursion(b, e, n));
	printf("%d^%d %% %d = %d\n", b, e, n, exponentation_modulo_loop(b, e, n));
}

단 int형을 다루도록 되어있기 때문에 n이 46341(INT_MAX의 제곱근보다 큰 정수의 최소값)보다 큰 경우 b * b 부분에서 오버플로우가 일어날 가능성이 있다.

 

 

참고: https://en.wikipedia.org/wiki/Modular_exponentiation?fbclid=IwAR3xb0pU2ze5yLkuiEGrpPnDzt6_HFEUwz2FtfaTcXt7Pe3oztEzlvcxCpg