수학

2. Shanks' Algorithm

thisisuserr 2024. 8. 22. 22:59

이산 로그 문제(Discrete Logarithm Problem, DLP)

어떠한 원시근 $g$와 $y$에 대해, $y \equiv g^k \pmod{p}$를 만족하는 최소의 자연수 $k$를 찾는 문제이다.

아직까지 해결하기 어려운 문제로 남아 암호학 등에 주로 사용된다.

 

대충 간략하게 브루트포스 알고리즘으로 구성하면, 다음과 같다.

def discreteLog(y, g, mod):
  cur = 1
  cnt = 0
  while True:
    cur = (cur * g) % mod
    cnt += 1
    if cur == y:
      return cnt

print(discreteLog(3, 2, 11))

 

당연하지만 mod가 더럽게 커지면, 사실상 못 쓰는 코드다.

 

Shanks' Algorithm

sqrt decomposition의 핵심 아이디어는 구간을 제곱근 길이의 제곱근 개수로 쪼개서 산술기하 최소 구하는 것처럼 만드는 것이였다.

딱히 연관성은 없지만, 제곱근으로 분할하는 아이디어를 생각해 보자.

 

어떠한 수 $p$에 대하여, $p$ 미만의 임의의 음이 아닌 정수 $x$에 대해

$0 \le a, b \le \sqrt{p} $이고 $a \sqrt{p} + b$을 만족하는 음이 아닌 정수 $a, b$가 존재한다.

 

먼저 $S = \{1, g, g^2, g^3, ... , g^{\sqrt{p} }$를 생각하자.

만약 $n \times g^{-k \sqrt{p} } \pmod{p}$가 어떤 집합 $S$에 존재한다면,

다음을 만족하는 $0 \le k, c \le \sqrt{p}$인 $k, c$가 존재한다.

 

$g^c \equiv n \times g^{-k \sqrt{p}} \pmod{p}$

이제 양변에 $g^{k \sqrt{p}}$을 곱하면, 이산 로그 $x = k\sqrt{p} + c$를 얻는다.

 

만약 해당 알고리즘으로 찾을 수 없다고 하면,

$0 \le k < p$인 모든 $k$에 대해 $g^k \equiv n$이 성립하지 않는다는 뜻이다.

그러나 원시근의 성질에 의해 이 모든 값들은 다 합동이 아니기 때문에, 모순이 발생한다.

 

시간 복잡도를 분석해 보면, 저 $g^{-k\sqrt{p}}$ $\sqrt{p}$개를 계산하는 데 시간복잡도는 $O(\log{p})$이다.

총 배열 $\sqrt{p}$개, 길이 $\sqrt{p}$의 배열에서 존재 여부 판별시간 $O( \log{p} )$

따라서 총 $O(\sqrt{p} \log{p} )$의 시간으로 구할 수 있게 된다.

 

아래는 다음 내용을 C++으로 구현한 것이다.

#include <iostream>
#include <set>
#include <cmath>

using namespace std;
typedef long long int LL;

LL mod;

LL pow(LL base, LL exp)
{
  if(exp == 0) return 1;
  if(exp == 1) return base;
  if(exp % 2 == 1) return pow(base, exp - 1) * base % mod;

  LL half = pow(base, exp / 2);
  return half * half % mod;
}

/* base ^ res = target (mod p) */
LL discreteLog(LL base, LL target)
{
  set<LL> s;
  LL sqrtP = ceil(sqrt(mod));
  LL inv_sqrtP = pow(pow(base, sqrtP), mod - 2); //FlT

  //Baby Step, 0 ~ sqrtP
  LL tmp = 1;
  for(LL i = 0; i < sqrtP; i++)
    {
      s.insert(tmp);
      tmp = (tmp * base) % mod;
    }

  //Giant Step
  tmp = 1;
  for(LL i = 0; i < sqrtP; i++)
    {
      LL cur = target * tmp % mod;
      if(s.find(cur) != s.end()) //이산로그 존재
      {
        tmp = 1;
        for(LL j = 0; j < sqrtP; j++)
          {
            if(tmp == cur) return i * sqrtP + j;
            tmp = (tmp * base) % mod;
          }
      }
      tmp = (tmp * inv_sqrtP) % mod;
    }
  return -1; //error
}

'수학' 카테고리의 다른 글

1. 위수와 원시근  (0) 2024.08.13