【20170919】2017暑假北京学习 day 2 - 4 BSGS算法 求 ^ ≡ (mod ) 最小整数解

一、引入

  先丢个题目出来——

  已知 a,b,m,求 ? ^ ? ≡  ? (mod ?) ( m <=10^8 ) 的最小正整数解。

  1)立刻想到线性枚举 x,然后运算求解,时间复杂度O(m*logm)TLE。。。

  2)枚举  x=1,2,3,……,m-2,m-1,m,m+1,m+2,.....    (当x>=m+1时,所得结果在 mod m 意义下与 x=1,2,3,... 重复)

   求出

    s1=a^1  (mod m)

    s2=a^2  (mod m)

    s3=a^3  (mod m)

     …  

    sm=a^m (mod m)

  对 s[ ] 进行排序and二分查找b,得出最小解x,

  时间复杂度为O(m*logm*logm)TLE。。。

还有没有更好的方法呢?

 

二、BSGS算法(又称“大步小步”算法,俗称“北上广深”算法)

  用途:

    求 ? ^ ? ≡  ? (mod ?)  的最小正整数解。

  时间复杂度:O( sqrt(m) * logm )

  空间复杂度:O( sqrt(m) )

  算法思路:

    依据本文 { 一、2)} 的思路,我们将s1,s2,....sm排成一个每行元素个数为 k=√m 的矩阵   

    a^1      a^2        a^3       …        a^k

 

    a^(k+1)    a^(k+2)     a^(k+3)   …        a^2k

 

    …       …        …      …       …

 

     …       …        …      …        a^(k^2)

 

    我们发现,每一纵行的数据拥有这样的特性:

      本纵行第 i 行的数据 a ^ ( i*k + 1 ),与本纵行第一横行的数据 a ^ 1  之比为 a ^ ( i*k )

——据此,我们可以通过 第一行的某个数据 (a ^ j) 乘上  [ a^ ( i*k ) ]  推出  其下一纵行的所有数据 a ^ ( i*k + j )

 

    类似于本文 { 一、2)} ,

    我们可以仅对 第一横行元素 进行排序O(logn)即仅对a^1,a^2,....,a^k进行排序,

    那么答案 b 在第一横行对应的数值是 { b / [ a^ ( i*k ) ] }

 

    然后根据上一行所总结出的规律,从上至下,对每一行进行二分查找【 O( k * logk ) 】即可。

 

  思路总结:

    Step 1:计算 a^x,按照k=√m分组置入矩阵

 

    Step 2:在 a^1 ~ a^k 中,二分查找b

 

    Step 3:如果第i-1行找不到b,则继续在a^1~a^k中,二分查找 { b / [ a^ ( i*k ) ] }  <==>  在a ^ [ ( i - 1 )*k  + 1 ] ~ a ^( i*k )中寻找b

 

 

  注意事项:

    1. 因为在计算{ b / [ a ^ ( i*k ) ] }时用到了除法,所以在mod m的意义下应该用逆元

 

    2. 算法执行过程中只计算存储a^1~a^k。空间复杂度降至O( sqrt(m) )。

 

  代码:

 1 /*BSGS算法*/
 2 #include<stdio.h>
 3 #include<math.h>
 4 #include<stdlib.h> 
 5 
 6 int a,b,m;
 7 int sqrtM;
 8 int *array;//存放 a^x 的数组 
 9 
10 int QuickPow(int base,int index);
11 //快速幂函数,此处不详细写了 
12 int FindinMod(int head,int tail,int goal,int mod);//========== 
13 //在mod情况下二分查找的函数,下次编辑的时候补充 
14 //若找到则返回目标所在下标 i,找不到则返回 0 。 
15 int main()
16 {
17     scanf("%d%d%d",&a,&b,&m);
18     sqrtM=sqrt(m);
19     array=(int *)malloc(sizeof(int)*sqrtM+1);//动态申请数组 
20     
21     int i;
22     for(i=1;i<=sqrtM;i++)
23         array[i]=QuickPow(a,i)%m;
24     
25     int seat;
26     for(i=1;i<=sqrtM;i++)
27     {
28         seat=FindinMod(1,sqrtM,b/( QuickPow(a,i*sqrtM) )); 
29         if(seat)
30         {
31             printf("%d\n",(i-1)*sqrtM+seat);//输出指数x 
32             break;
33         }
34     }
35     return 0;
36 }

 

转载于:https://www.cnblogs.com/CXSheng/p/7552973.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值