POJ 2417 Discrete Logging

本文介绍了解决离散对数问题的一种有效算法——Baby Step Giant Step (BSGS) 方法,并提供了详细的算法实现步骤及代码示例。该算法通过预处理和查找表的方式,在给定素数P、底数B和目标值N的情况下,寻找满足B^L ≡ N (mod P) 的最小L值。

 

Time Limit: 5000MS Memory Limit: 65536KB 64bit IO Format: %I64d & %I64u

Description

Given a prime P, 2 <= P < 2 31, an integer B, 2 <= B < P, and an integer N, 1 <= N < P, compute the discrete logarithm of N, base B, modulo P. That is, find an integer L such that 
    B
L
 == N (mod P)

Input

Read several lines of input, each containing P,B,N separated by a space.

Output

For each line print the logarithm on a separate line. If there are several, print the smallest; if there is none, print "no solution".

Sample Input

5 2 1
5 2 2
5 2 3
5 2 4
5 3 1
5 3 2
5 3 3
5 3 4
5 4 1
5 4 2
5 4 3
5 4 4
12345701 2 1111111
1111111121 65537 1111111111

Sample Output

0
1
3
2
0
3
1
2
0
no solution
no solution
1
9584351
462803587

Hint

The solution to this problem requires a well known result in number theory that is probably expected of you for Putnam but not ACM competitions. It is Fermat's theorem that states 
   B
(P-1)
 == 1 (mod P)

for any prime P and some other (fairly rare) numbers known as base-B pseudoprimes. A rarer subset of the base-B pseudoprimes, known as Carmichael numbers, are pseudoprimes for every base between 2 and P-1. A corollary to Fermat's theorem is that for any m 
   B
(-m)
 == B
(P-1-m)
 (mod P) .

Source

 

 

式子是这个:BL == N (mod P) 求最小的L

把L分解:L=i*m+j  其中m=sqrt(P)向上取整

原式化为 B^(i*m)*B^j==N(MOD P)

再化为B^j===N*B^(-i*m)(MOD P)

先预处理出B^1  B^2 ... B^(m-1)

之后求出B^-m,枚举i,如果存在B^(-i*m)存在于HASH表中,说明存在解L=i*m+j

说了这么多,其实我也不怎么懂2333

————————

隔了一段时间回来,发现代码看不懂了,这和上面写的不一样啊?

研究了一下,发现确实不一样

按照上面写的解释,算N*B^(-i*m)很明显需要用到乘法逆元,这就比较麻烦。如何规避这个问题?

把L分解成L=i*m-j (反正m和j都是待求的,就当m比上面的分解法多了1吧)

  ↑这样原式化为B^(i*m) / B^j == N (MOD P)

        B^(i*m) == N * B^j (MOD P)

  这样就不需要求逆元咯。下面的代码就是这种写法

————————

 

代码基本靠抄

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<cmath>
 6 using namespace std;
 7 const int mop=100007;
 8 int hash[mop];
 9 int head[mop],nxt[mop],id[mop],cnt;
10 void add(int x,int y){//hash
11     int m=x%mop;
12     hash[++cnt]=x;nxt[cnt]=head[m];id[cnt]=y;head[m]=cnt;
13     return;
14 }
15 int find(int x){
16     int m=x%mop;
17     int i,j;
18     for(i=head[m];i!=-1;i=nxt[i]){
19         if(hash[i]==x)return id[i];
20     }
21     return -1;
22 }
23 int BSGS(int a,int b,int n){//a^x mod n =b
24     memset(head,-1,sizeof head);
25     cnt=0;
26     if(b==1)return 0;
27     int m=sqrt((double)n),j;
28     long long x=1,p=1;
29     for(int i=0;i<m;i++,p=p*a%n)add(p*b%n,i);//预处理b^i 
30     for(long long i=m; ;i+=m){
31         x=x*p%n;
32         if((j=find(x))!=-1)return i-j;
33         if(i>n)break;
34     }
35     return -1;
36 }
37 int main(){
38     int p,b,n;
39     while(scanf("%d%d%d",&p,&b,&n)!=EOF){
40         int ans=BSGS(b,n,p);
41         if(ans==-1)printf("no solution\n");//无解 
42         else printf("%d\n",ans);
43     }
44     return 0;
45 }

 

转载于:https://www.cnblogs.com/SilverNebula/p/5668380.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值