高次同余笔记(三):离散对数和原根
来源:互联网 发布:淘宝提高信誉度 编辑:程序博客网 时间:2024/04/29 17:48
我们来看这个方程:
a,b,c在int内,c是质数。
求x在[0,c-1]内所有的解。
这个怎么搞?
那我们换个方程:
这个方程 的解很明显是
但是我们换个角度,因为开根号这个操作数论里面不好搞。
这样有
通过这个式子可以算出
这样x就是一个指数式子,不含根号了。
但是e这个东西数论里面没有啊。
注意到这个底数的选取是任意的,那么随便选一个底数记作G。
这样有
但是这个底数的选取也是有要求的,起码这个底数使得x属于[1,c-1]的时候G^x%c要取遍[1,c-1]的所有数。不然换成原来的指数式子是有问题的。
比如下面一个表,当G=3,c=7的时候:
- x 1 2 3 4 5 6
- G^x 3 2 6 4 5 1
然后,符合条件的G被称为模c的原根。
这样取对数的操作就是离散对数了。
然后我们再看logGb这个怎么求。
x=logGb->G^x=b(mod c)
这个是什么东西?
不就是之前讲过的baby-step-giant-step吗?
这样方程中的a已知,logGb可求,c已知,这样原方程被转化成一个线性同余式子,用exgcd可求logGx。
知道了logGx后,x=G^logGx,用快速幂可以求出x。
如果上面那个线性同余有多解,那么最后的x也是有多解的。
思路就是这样。
下面来说原根怎么求。
G是模c的原根的充要条件是G^(c-1) = 1 (mod c)在G < c的时候当且仅当指数为c-1的时候成立。
这样容易想到枚举一个G,对于每一个G,枚举i从0到c-1,计算G^(c-1),如果前面就出现1了那么G不是模c的原根。
有一个优化,就是i的枚举不需要从0到c-1,只需要枚举(c-1)/c的一个质因子,对于每个质因子都这么枚举就快了。
扩展是当c为合数的时候,这个我还没弄懂怎么CRT合并,哪位大神来讲解一下。
贴一个HDU3930
#include<iostream>#include<cstdio>#include<algorithm>#include<cmath>#include<map>#define ll long longusing namespace std;const int N=1000010;ll fac[70],ans[N],a,b,c;bool isprime[N];int prime[N/12],pcnt,fcnt,cas;inline void getprime(int n){ for(int i=2;i<=n;i++) { if(!isprime[i])prime[pcnt++]=i; for(int j=0;j<pcnt&&prime[j]*i<=n;j++) { isprime[prime[j]*i]=true; if(i%prime[j]==0)break; } }}inline void divide(ll n){ fcnt=0; ll t=sqrt(n); for(int i=0;prime[i]<=t&&i<pcnt;++i) if(n%prime[i]==0) { fac[fcnt++]=prime[i]; while(n%prime[i]==0)n/=prime[i]; } if(n>1)fac[fcnt++]=n;}inline ll mul(ll n,ll m,ll p){ ll ans=0; while(m) { if(m%2)ans=(ans+n)%p; n=2*n%p; m/=2; } return ans;}inline ll pow(ll n,ll m,ll p){ ll ans=1%p; while(m) { if(m%2)ans=mul(ans,n,p); n=mul(n,n,p); m/=2; } return ans;}ll primitive_root(ll p){ divide(p-1); for(int r=2;;++r) { bool flag=1; for(int i=0;i<fcnt;++i) { ll t=(p-1)/fac[i]; if(pow(r,t,p)==1) { flag=0; break; } } if(flag)return r; }}void exgcd(ll a,ll b,ll &d,ll &x,ll &y){ if(!b){x=1;d=a;y=0;} else { exgcd(b,a%b,d,y,x); y-=a/b*x; }}ll BSGS(ll a,ll b,ll p){ map<ll,int>mp; ll m=ceil(sqrt(p)),d=1,val=1,g,x,y; for(int i=0;i<m;++i) { mp.insert(make_pair(val,i)); val=val*a%p; } for(int i=0;i<m;++i) { exgcd(d,p,g,x,y); x=(b/g*x%p+p)%(p/g); if(mp.count(x))return i*m+mp[x]; d=d*val%p; } return -1;}void work(ll a,ll b,ll c){ ll rt=primitive_root(c); ll logrtb=BSGS(rt,b,c); ll d,x,y; exgcd(a,c-1,d,x,y); if(logrtb%d){puts("-1");return;} ll t1=logrtb/d,t2=(c-1)/d; ans[0]=(x*t1%t2+t2)%t2; for(int i=1;i<d;++i) ans[i]=ans[i-1]+t2; for(int i=0;i<d;++i) ans[i]=pow(rt,ans[i],c); sort(ans,ans+d); for(int i=0;i<d;++i) printf("%I64d\n",ans[i]);}int main(){ getprime(N); while(~scanf("%I64d%I64d%I64d",&a,&c,&b)) { printf("case%d:\n",++cas); work(a,b,c); }}
- 高次同余笔记(三):离散对数和原根
- POJ 3243 离散对数-高次同余方程求解
- SGU 261 Discrete Roots(原根+高次同余方程+线性同余方程)
- poj 3243 Clever Y(离散对数-高次同余方程)
- 离散对数和原根
- 高次同余问题
- (离散对数与原根)
- 原根与离散对数
- BSGS+原根+离散对数
- 高次同余笔记(一):baby-step-giant-step算法
- 高次同余笔记(二):extended-baby-step-giant-step算法
- hdu 2815 Mod Tree(高次同余方程)
- 高次同余方程(Baby-Step Giant-Step)
- ACM_高次同余方程
- hdu3930 broot 离散k次根 ,离散对数
- 本原根和离散对数
- POJ 2417 Discrete Logging(高次同余方程-Baby-Step,Giant-Step)
- 高次同余方程式的解数及解法
- 文档之产品的软件设计方案的编写框架
- [Android]_[ACtivity生命周期]
- 树莓派入门(三)
- Android开发规范基础
- 阿里云服务器phpwind一键配置问题
- 高次同余笔记(三):离散对数和原根
- BC #63 div(2) #2
- Spring MVC 详解
- isInterleave
- 由chmod改系统文件权限引ssh登录报ssh_exchange_identification: read: Connection reset by peer无法登陆问题
- 数据库分表使用思考
- 树莓派入门(四)
- 等待锁和等待条件、临界区实现、读写锁实现
- PHP的单引号里不能包含变量