转自:http://blog.csdn.net/doyouseeman/article/details/52033204

简介

Cipolla算法是解决二次剩余强有力的工具,一个脑洞大开的算法。 
认真看懂了,其实是一个很简单的算法,不过会感觉得出这个算法的数学家十分的机智。

基础数论储备

二次剩余

首先来看一个式子),我们现在给出n,要求求得x的值。如果可以求得,n为mod p的二次剩余,其实就是n在mod p意义下开的尽方。Cipolla就是一个用来求得上式的x的一个算法。

勒让德符号

勒让德符号是判断一个数是否为p的二次剩余的一个有力工具,p一定要为奇质数。(n,p)表示n为关于p的勒让德符号。其实就是判断n是否为p的二次剩余。 

 

看起来好像是一大段废话,勒让德只是站在巨人的肩膀上总结了一下而已。 
其实勒让德还总结了一些性质,不过一般只用得到欧拉判别准则,不够勒让德符号是个积性函数可能还有点用。 
但还是不知道如何判断n是否为p的二次剩余,往下看欧拉判别准则

 ll Legendre(ll a, ll p)  
  {  
       return qsm(a, (p-1)/2, p);  
  } 

欧拉判别准则

先来回顾一下欧拉定理),如果等于1就肯定开的了方,为-1一定开不了。所以x是否为n的二次剩余就用这个欧拉判别准则。

if(qsm(n,(p-1)/2)==p-1){
    printf("No root\n"); 
    continue;   
}

-1在mod p意义下为p-1。

算法流程

给出n和p 
就算我们n关于p的勒让德符号为1,那么要怎样取开n的方呢? 
现在是脑洞大开的时候。

找一个数a

我们随机一个数a,然后对1 
先不要管为什么,后面会讲,我们现在就默默的去膜拜Cipolla的脑洞很大。

while(1){
    a=rand()%p;
    w=(a*a-n+p)%p;
    if(qsm(w,(p-1)/2)==p-1)break;
}

因为是随机找a,那么会不会要找很久。 
答案:不会! 
2 
根据定理1,随机找a的期望为2。

建立一个复数域

说是建立,其实根本不用程序去打,说是建立复数域只是跟方便理解。 
在平常学的复数域中,有一个n,这样我们就定义了一个新的域。

struct CN{
    ll x,y;
    CN friend operator *(CN x,CN y){
        CN z;
        z.x=(x.x*y.x%p+x.y*y.y%p*w%p)%p;
        z.y=(x.x*y.y%p+x.y*y.x%p)%p;
        return z;    
    }
}u,v;

像正常打复数运算一样我们定义一下运算符。可以发现z.x那个地方后面是*w而不是*1,因为现在的域单位复数为ω

得出答案

我们要求的是2 
真的吗?真的!但是这个答案不是由实数和虚数组成的吗? 
根据拉格朗日定理,可以得出虚数处的系数一定为0。

 

为什么会有两个答案,比如)

证明原理

再搞出一些定理方便说明。

定理

)

推导

问题:) 
这脑洞太大了!!!!!!!!!!!!!!

代码

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 ll w,a,n,p;
 5 struct CN
 6 {
 7     ll x,y;
 8     CN operator * (const CN &a)const
 9     {
10         CN z;
11         z.x=(x*a.x%p+y*a.y%p*w%p)%p;
12         z.y=(x*a.y%p+y*a.x%p)%p;
13         return z;
14     }
15 }u;
16 ll qsm(ll x,ll y)
17 {
18     ll ans=1;
19     while(y)
20     {
21         if(y&1)ans=ans*x%p;
22         x=x*x%p;y>>=1;
23     }
24     return ans;
25 }
26 CN Cqsm(CN x,ll y)
27 {
28     CN z;z.x=1;z.y=0;
29     while(y)
30     {
31         if(y&1)z=z*x;
32         x=x*x;y>>=1;
33     }
34     return z;
35 }
36 int main()
37 {
38      int T;
39     scanf("%d",&T);
40     while(T--)
41     {
42         scanf("%lld%lld",&n,&p);
43         n%=p;
44         if(p==2)
45         {
46             printf("1\n");
47             continue;
48         }
49         if(qsm(n,(p-1)/2)==p-1){
50             puts("No root");
51             continue;
52         }
53         while(1)
54         {
55             a=rand()%p;
56             w=(a*a%p-n+p)%p;
57             if(qsm(w,(p-1)/2)==p-1)break;
58         }
59         u.x=a;u.y=1;
60         u=Cqsm(u,(p+1)/2);
61         ll ans1=u.x,ans2=p-ans1;
62         if(ans1>ans2)swap(ans1,ans2);
63         if(ans1==ans2){
64             printf("%lld\n",ans1);
65         }
66         else{
67             printf("%lld %lld\n",ans1,ans2);
68         }
69     }
70     return 0;
71 }

 

相关文章:

  • 2021-11-16
  • 2021-08-27
  • 2021-11-15
  • 2021-12-13
  • 2021-08-28
  • 2022-12-23
  • 2021-05-25
猜你喜欢
  • 2021-07-13
  • 2021-07-04
  • 2021-05-24
  • 2022-12-23
  • 2021-08-24
  • 2022-02-03
相关资源
相似解决方案