BSGS学习笔记 数论

16 篇文章 0 订阅
3 篇文章 0 订阅

注:本篇均为博主个人的理解,如有错误,敬请斧正。

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=imj − 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) aimj=b(mod c)
− j -j j的用处就在这里,如果是 + j +j +j,那么我们要把 a i ∗ m a^{i*m} aim看作一个整体 D D D,那么有 D ∗ a j = b ( m o d   c ) D*a^j=b(mod\ c) Daj=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=baj(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(c1)=ax(mod c)

a x − n ∗ ( c − 1 ) = a x ( m o d   c ) a^{x-n*(c-1)}=a^x(mod\ c) axn(c1)=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(c1)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(c1)]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) ac1=1(mod c)所以
[ a ( c − 1 ) ] n = 1 ( m o d   c ) [a^{(c-1)}]^n=1(mod\ c) [a(c1)]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) baj(mod c),这里允许后面的 j j j覆盖前面的 j j j,因为 x = i ∗ m − j x=i*m-j x=imj,所以 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=imj,就是答案。因为 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 <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <map>
using namespace std;

long long a,b,c;//计算a^x=b(mod c)的最小x 
map<long long,long long> mp;
long long gcd(long long x,long long y)
{
	return y?gcd(y,x%y):x;
}
long long ksm(long long x,long long y,long long mod)
{
	long long res=1;
	while(y)
	{
		if(y&1)
		res=(res*x)%mod;
		x=(x*x)%mod;
		y>>=1;
	}
	return res;
}
int main()
{
	while(~scanf("%lld%lld%lld",&c,&a,&b))
	{
		mp.clear();
		if(gcd(a,c)!=1)//如果a与c不互质,一定无解 
		{
			printf("no solution\n");
			continue;
		}
		long long m=(long long)ceil(sqrt(c)),ans,pd=0;//pd记录是否有解 
		for(int j=0;j<=m;++j)//枚举b*a^j存入哈希表(map) 
		{
			if(j==0)
			{
				ans=b%c;
				mp[ans]=j;
				continue;
			}
			ans=(ans*a)%c;
			mp[ans]=j;//用后来的覆盖前面的,因为j越大,-j越小 
		}
		long long x=ksm(a,m,c);//先计算a^m,因为之后要计算(a^m)^i 
		ans=1;
		for(int i=1;i<=m;++i)
		{
			ans=(ans*x)%c;
			if(mp[ans])//i越小,求出的x越小(j<=m,但是i每+1,x就要+m) 
			{
				x=i*m-mp[ans];
				printf("%lld\n",(x%c+c)%c);
				pd=1;
				break;
			}
		}
		if(!pd)
		printf("no solution\n");
	}
	return 0;
}

PS:第一次用markdown,感觉一开始用真的好难受,但是看上去的效果不错。多亏Mys_C_K教我,不然可能一天都发不出博客,在此感谢Mys_C_K。
题单:
poj2417 Discrete Logging
洛谷3846可爱的质数
洛谷2485计算器
洛谷3306SDOI随机数生成器(我有写题解)

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值