首先说一下我遇到的问题:
DH密钥交换中 X % P= B^a % P (P为素数)
已知X,P,B求一组(或者一个)a,存入数据库的问题。
得先求出最小的一个:
归结到数论的“baby step gaint step”
1.小步
将B^1.B^2...存入HASH表【上边界之后说,可以直接选P,但是复杂度比较高】
2.大步
降低运算的复杂度选一个上边界m = (int)ceil(sqrt((double)(p - 1))),并同时作为大步的步长
3.令a = i*m + j
走大步B^(-i*m)去HASH表中查看数值是否存在,如果存在则找到最小值a【我在是现在中用的是数组+排序+二分查找】
在求B^(-i*m)的时候要用费马小定理B^(P-1) %P = 1 %P,简单的套路证明:http://www.zybang.com/question/482b889ecae431c36ec2109bb0247461.html
找周期:
首先算出的周期是P-1,但是在验证的时候对于小素数不成立,这个问题还得想想,大家可以给一些建议
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define LL __int64
#define N 1000000
using namespace std;
/*
*author by delia
*/
struct Node{
int idx;
LL val;
}baby[N];
bool cmp(Node n1,Node n2){
return n1.val!=n2.val?n1.val<n2.val:n1.idx<n2.idx;
}
//先根据费马定理得到-1次方的值,然后再求出m次的值
LL PowMod(LL a,LL b,LL MOD){
LL ret=1;
a%=MOD;
while(b){
if(b&1)
ret=((LL)ret*a)%MOD;
a=((LL)a*a)%MOD;
b>>=1;
}
return ret;
}
//BinSearch(current size, target value)
int BinSearch(int m,LL num){
int low=0,high=m-1,mid;
while(low<=high){
mid=(low+high)>>1;
if(baby[mid].val==num)
return baby[mid].idx;
if(baby[mid].val<num)
low=mid+1;
else
high=mid-1;
}
return -1;
}
int main(){
LL p,b,n;
while(scanf("%lld%lld%lld",&p,&b,&n)!=EOF){
//这样来取的原因是要平衡复杂度
int m = (int)ceil(sqrt((double)(p - 1)));
//小步:1.初始化数组0位元素 2.之后按b累计幂
baby[0].idx=0;
baby[0].val=1;
for(int i=1;i<m;i++){
baby[i].idx=i;
baby[i].val=((LL)baby[i-1].val*b)%p; //b^i
}
//形成新的有序&没有相同值的数组(id,val)
sort(baby,baby+m,cmp);
int cnt=1;
for(int i=1;i<m;i++)
if(baby[i].val!=baby[cnt-1].val)
baby[cnt++]=baby[i];
//大步:找出基本跨步大小m = b^(-m),累计比较是否有有相应的小步
LL bm=PowMod(PowMod(b,p-2,p),m,p);
int ans=-1;
LL tmp=n;
for(int j=0;j<m;j++){
int pos=BinSearch(cnt,tmp);
if(pos!=-1){
ans=j*m+pos;
break;
}
tmp=((LL)tmp*bm)%p;
}
if(ans<0)
puts("no solution");
else{
printf("a值::%d\n",ans);
//寻找最小周期
//初步推断出来的最小为P-1
int t = 0;
LL val1 = (PowMod(b,ans+1,p))%p;
LL val2 = n % p;
while(val1!=val2){
t++;
val1 = (val1*b)%p;
}
t++;
printf("对于素数%lld,最小的周期是%d\n",p,t);
}
}
return 0;
}