📝
Baby-step Giant-step メモ
一般に
問題設定
- 有限巡回群
(位数 (n))(G = \langle g \rangle) - 要素
(h \in G) - 典型的には 離散対数問題:
を満たす整数g^x = h (x )を求める[1]0 \le x < n
アルゴリズムの流れ
-
ステップ数の設定
m = \lceil \sqrt{n} \rceil
-
Baby-step
-
についてj = 0, 1, \dots, m-1 を計算し、b_j = g^j (群要素)をキーに「対応するb_j 」をハッシュテーブルに保存するj
-
-
Giant-step
- (大ステップ量)
の逆元をg^m として計算しておくg^{-m} -
についてi = 0, 1, \dots, m-1 を計算し、baby-step のテーブルにt_i = h \cdot (g^{-m})^i があるか探すt_i - もしテーブル上で
と一致するt_i が見つかったらb_j よりt_i = g^j \Rightarrow h = g^j \cdot g^{\,m \cdot i} = g^{\,j + m \,i} が解(離散対数)となるx = j + m\,i
- もしテーブル上で
- (大ステップ量)
計算量
O(\sqrt{n})
例題
ABC186
有限巡回な加法群。
解法
を解くことを考える。
まず、
このうえで、
そうでない時、
Baby-step Giant-step
-
ステップ数の設定
M = \lceil \sqrt{N} \rceil
-
Baby-step
-
について、j = 0, 1, \dots, M-1 を計算し、b_j = jK をキーに対応するb_j をハッシュテーブルに保存するj
-
-
Giant-step
- (大ステップ量)
を計算する-MK \mod N -
について、i=0, 1, \dots, M-1 を計算し、Baby-stepで計算したテーブル上にあるか確認する。-S+i(-MK) - このうち最小の
が答えx = j + iM
- このうち最小の
- (大ステップ量)
サンプルコード
一部抜粋(Python)
def solve(N, S, K):
g = math.gcd(math.gcd(N, S), K)
N //= g
S //= g
K //= g
if math.gcd(K, N) != 1: # 解なし
return -1
M = math.ceil(math.sqrt(N))
# Baby-step
table = dict()
for j in range(M):
bj = (j * K) % N
table[bj] = j
# Giant-step
g_step = (- M * K) % N
for i in range(M):
ti = (- S + i * g_step) % N
if ti in table:
return table[ti] + i * M
return -1
ABC270
最初
解法
先にエッジケースを処理する。
そうでなく、
それ以外を Baby-step Giant-step で考える。
まず、上記の
を変形して、
から、
とできる。
次に、これを
と書けるように、
から、
の
Baby-step Giant-step
(
-
ステップ数の設定
m = \lceil \sqrt{P} \rceil
-
Baby-step
-
について、j = 0, 1, \dots, m-1 を計算し、b_j = f^j(S) をキーに対応するb_j をハッシュテーブルに保存する。この時、同一キーに対してできるだけ小さいj を保持することに注意する。j
-
-
Giant-step
- (大ステップ量)上記のように
を事前計算していると、各f^{-m} に対して順にi をf^{-mi}(X) で求めることができる。O(1) -
について、i=0, 1, \dots, m-1 を計算し、Baby-stepで計算したテーブル上にあるか確認する。f^{-mi}(G) - このうち最小の
が答えx = j + im
- このうち最小の
- (大ステップ量)上記のように
サンプルコード
一部抜粋(Python)
def solve(P, A, B, S, G):
if S == G:
return 0
if A == 0:
if B == G:
return 1
else:
return -1
def f(X):
return (A * X + B) % P
# ステップ数
M = math.ceil(math.sqrt(P))
inv_A = pow(A, -1, P)
c = (- B * inv_A) % P # (-B) / A (mod P)
C, D = 1, 0
for _ in range(M):
C = (inv_A * C) % P
D = (inv_A * D + c) % P
def f_inv_m(X):
return (C * X + D) % P
# Baby-step
table = dict()
X = S
for j in range(M):
if X not in table:
table[X] = j
X = f(X)
# Giant-step
crr = G
for i in range(M):
if crr in table:
return i * M + table[crr]
crr = f_inv_m(crr)
return -1
ABC222
参考
- https://atcoder.jp/contests/abc270/editorial/4965
- https://drken1215.hatenablog.com/entry/2020/12/20/015100
-
なら解がないことに注意 ↩︎h \notin \langle g \rangle
Discussion