注:本篇均为博主个人的理解,如有错误,敬请斧正。
BSGS是用来解决这样一个问题: a x = b ( m o d c ) a^x=b(mod\ c) ax=b(mod c),求最小的非负整数x。
BSGS是一种O( c \sqrt{c} c)的算法。它有解的条件是模数 c c c为质数并且底数 a a a与模数 c c c互质。
它的过程如下:
令 x = i ∗ m − j x=i*m-j x=i∗m−j, − j -j −j而不是 + j +j +j可以避免用扩欧,如果是 + j +j +j的话要用到扩欧。其中 m = ⌈ c ⌉ m=\lceil\sqrt{c}\rceil m=⌈c⌉,则有 a i ∗ m − j = b ( m o d c ) a^{i*m-j}=b(mod\ c) ai∗m−j=b(mod c)。
− j -j −j的用处就在这里,如果是 + j +j +j,那么我们要把 a i ∗ m a^{i*m} ai∗m看作一个整体 D D D,那么有 D ∗ a j = b ( m o d c ) D*a^j=b(mod\ c) D∗aj=b(mod c),可以用扩展欧几里得求出 a j a^j aj。但是-j的话我们可以把上面的式子移项,可以得到: ( a m ) i = b ∗ a j ( m o d c ) (a^m)^i=b*a^j(mod\ c) (am)i=b∗aj(mod c)。其中 a a a, b b b, c c c都是已知的, m m m可以由 c c c算出来,那么只有式子中 i i i和 j j j是未知的,那么我们考虑枚举 i i i和 j j j。
那么我们会面临这样的问题:为什么将m取为 ⌈ c ⌉ \lceil\sqrt{c}\rceil ⌈c⌉?i和j要枚举到什么时候结束?
我们考虑模意义下会出现循环节,所以不需要无线枚举下去,那么多长一定会出现循环节呢?(我觉得有点类似pollard_rho的那个rho)我们发现BSGS有解的条件与费马小定理的适用条件一样,这是因为找循环节时用到了费马小定理来证明。
我们考虑证明 a x m o d ( c − 1 ) = a x ( m o d c ) a ^ {x \ mod (c-1)}=a^x(mod\ c) ax mod(c−1)=ax(mod c)
a x − n ∗ ( c − 1 ) = a x ( m o d c ) a^{x-n*(c-1)}=a^x(mod\ c) ax−n∗(c−1)=ax(mod c)
a x a n ∗ ( c − 1 ) = a x ( m o d c ) \frac{a^x}{a^{n*(c-1)}}=a^x(mod\ c) an∗(c−1)ax=ax(mod c)
a x [ a ( c − 1 ) ] n = a x ( m o d c ) \frac{a^x}{[a^{(c-1)}]^n}=a^x(mod\ c) [a(c−1)]nax=ax(mod c)
由费马小定理可知:当 c c c为质数并且 a a a与 c c c互质时 a c − 1 = 1 ( m o d c ) a^{c-1}=1(mod\ c) ac−1=1(mod c)所以
[ a ( c − 1 ) ] n = 1 ( m o d c ) [a^{(c-1)}]^n=1(mod\ c) [a(c−1)]n=1(mod c)
于是就有了
a x = a x ( m o d c ) {a^x}={a^x}(mod\ c) ax=ax(mod c)证明完毕。
那么我们可以得知 x x x只需枚举到 c c c即可,由于 m = ⌈ c ⌉ m=\lceil\sqrt{c}\rceil m=⌈c⌉,所以i和j最大也是 ⌈ c ⌉ \lceil\sqrt{c}\rceil ⌈c⌉。
那么,我们开始从 0 0 0到 m m m枚举 j j j,用哈希表记录每一个 b ∗ a j ( m o d c ) b*a^j(mod\ c) b∗aj(mod c),这里允许后面的 j j j覆盖前面的 j j j,因为 x = i ∗ m − j x=i*m-j x=i∗m−j,所以 j j j越大, x x x越小,而我们又要求最小的 x x x,所以要这么操作。
接下来我们开始从 1 1 1到 m m m枚举 i i i,计算 ( a m ) i ( m o d c ) (a^m)^i(mod\ c) (am)i(mod c),在哈希表中查找,如果与表中的某个哈希值相同,那么我就计算出现在的 x = i ∗ m − j x=i*m-j x=i∗m−j,就是答案。因为 i i i每增大 1 1 1, x x x就会增大 m m m,而 j j j最大就是 m m m,所以只要 i i i最小就好。而因为我们减了 j j j,所以要从 1 1 1开始枚举 i i i,否则会出现负数。
接下来是代码(poj2417) `
#include
#include
#include
#include
#include
#include
#include
PS:第一次用markdown,感觉一开始用真的好难受,但是看上去的效果不错。多亏Mys_C_K教我,不然可能一天都发不出博客,在此感谢Mys_C_K。
题单:
poj2417 Discrete Logging
洛谷3846可爱的质数
洛谷2485计算器
洛谷3306SDOI随机数生成器(我有写题解)