Cipolla算法学习小记
转自:http://blog.csdn.net/doyouseeman/article/details/52033204
简介
Cipolla算法是解决二次剩余强有力的工具,一个脑洞大开的算法。
认真看懂了,其实是一个很简单的算法,不过会感觉得出这个算法的数学家十分的机智。
基础数论储备
二次剩余
首先来看一个式子x2≡n(modp),我们现在给出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);
}
欧拉判别准则
先来回顾一下欧拉定理xφ(p)≡1(modp)
因为这里的p是奇质数,所以xp−1≡1(modp)
对xp−1进行开方操作,在虚数域中xp−12≡±1(modp),如果等于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,然后对a2−n进行开方操作(就是计算他勒让德符号的值),直到他们的勒让德符号为-1为止(就是开不了方为止)。
就是找到一个a满足(a2−n)p−12=−1
先不要管为什么,后面会讲,我们现在就默默的去膜拜Cipolla的脑洞很大。
while(1){
a=rand()%p;
w=(a*a-n+p)%p;
if(qsm(w,(p-1)/2)==p-1)break;
}
因为是随机找a,那么会不会要找很久。
答案:不会!
∙定理1:x2≡n(modp)中有p−12个n能使方程有解
⇒证明定理1:x2≡n(modp),如果存在不同的数u,v,使他们带入x后可以使方程有解,那么很显然满足u2−v2|p,所以满足(u+v)(u−v)|p,因为
u2−v2|p所以p不可能是(u-v)的倍数,所以(u+v)|p,那么这样的数对在p中存在的数量为p−12
根据定理1,随机找a的期望为2。
建立一个复数域
说是建立,其实根本不用程序去打,说是建立复数域只是跟方便理解。
在平常学的复数域中,有一个i,满足i2=−1。
我们也建立一个类似的域,因为我们要对于a2−n开方,而a2−n有不是p的二次剩余,所以我们定义ω=a2−n−−−−−√。那么现在的ω也像i一样,满足ω2=a2−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=a2−n,而不像正常复数的i2=−1。在这个域中的表示方式类似正常的复数:a+bω
得出答案
我们要求的是x2≡n(modp),x的值
我们现在知道了a和ω之后,就能得出答案了。
答案=(a+ω)p+12
真的吗?真的!但是这个答案不是由实数和虚数组成的吗?
根据拉格朗日定理,可以得出虚数处的系数一定为0。
为什么会有两个答案,比如4√=±2,x2≡(p−x)2(modp)很显然,因为把后面拆开x2≡x2−2px+p2(modp),把p都约去,所以x2≡(p−x)2(modp)。
证明原理
再搞出一些定理方便说明。
定理
∙定理2:(a+b)p≡ap+bp(modp)
⇒证明定理2:根据二项式定理:
(a+b)p≡∑pi=0Cipap−ibi(modp)
≡∑pi=0p!(p−i)!i!ap−ibi(modp)
可以发现,只有当i=0或i=p的时候式子没有p的因子,所以中间有p的因子的都可以省略,所以(a+b)p≡ap+bp(modp)
∙定理3:ωp≡−ω(modp)
⇒证明定理3:ωp
=ωp−1∗ω
=(ω2)p−12∗ω
=(a2−n)p−12∗ω——因为ω2=a2−n
=−ω——因为满足(a2−n)p−12=−1
∙定理4:ap≡a(modp)
⇒证明定理3:ap根据费马小定理
≡ap−1≡1(modp)
所以ap≡a∗ap−1≡a(modp)
推导
问题:x2≡n(modp)求解x
Cipolla:x≡(a+ω)p+12(modp)的实数
直接把式子转化:
(a+ω)p+12(modp)
≡((a+ω)p+1)12(modp)
≡((a+ω)p(a+ω))12(modp)
≡((ap+ωp)(a+ω))12(modp)根据定理2
≡((a−ω)(a+ω))12(modp)根据定理3和定理4
≡(a2−ω2)12(modp)根据定理3和定理4
≡(a2−(a2−n))12(modp)满足ω2=a2−n
≡n12(modp)
所以(a+ω)p+12≡n12≡n√(modp)
这脑洞太大了!!!!!!!!!!!!!!
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll w,a,n,p;
struct CN
{
ll x,y;
CN operator * (const CN &a)const
{
CN z;
z.x=(x*a.x%p+y*a.y%p*w%p)%p;
z.y=(x*a.y%p+y*a.x%p)%p;
return z;
}
}u;
ll qsm(ll x,ll y)
{
ll ans=;
while(y)
{
if(y&)ans=ans*x%p;
x=x*x%p;y>>=;
}
return ans;
}
CN Cqsm(CN x,ll y)
{
CN z;z.x=;z.y=;
while(y)
{
if(y&)z=z*x;
x=x*x;y>>=;
}
return z;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%lld%lld",&n,&p);
n%=p;
if(p==)
{
printf("1\n");
continue;
}
if(qsm(n,(p-)/)==p-){
puts("No root");
continue;
}
while()
{
a=rand()%p;
w=(a*a%p-n+p)%p;
if(qsm(w,(p-)/)==p-)break;
}
u.x=a;u.y=;
u=Cqsm(u,(p+)/);
ll ans1=u.x,ans2=p-ans1;
if(ans1>ans2)swap(ans1,ans2);
if(ans1==ans2){
printf("%lld\n",ans1);
}
else{
printf("%lld %lld\n",ans1,ans2);
}
}
return ;
}
Cipolla算法学习小记的更多相关文章
- 二次剩余Cipolla算法学习笔记
对于同余式 \[x^2 \equiv n \pmod p\] 若对于给定的\(n, P\),存在\(x\)满足上面的式子,则乘\(n\)在模\(p\)意义下是二次剩余,否则为非二次剩余 我们需要计算的 ...
- Gcd&Exgcd算法学习小记
Preface 对于许多数论问题,都需要涉及到Gcd,求解Gcd,常常使用欧几里得算法,以前也只是背下来,没有真正了解并证明过. 对于许多求解问题,可以列出贝祖方程:ax+by=Gcd(a,b),用E ...
- Cipolla算法学习笔记
学习了一下1个$\log$的二次剩余.然后来水一篇博客. 当$p$为奇素数的时候,并且$(n, p) \equiv 1 \pmod{p}$,用Cipolla算法求出$x^2 \equiv n \pmo ...
- Manacher 算法学习小记
概要 一个字符串有多少个回文的字串?最多有 \(O(n^2)\) 级别个.但 Manacher 算法却可以用 \(O(n)\) 的时间复杂度解决这个问题.同时 Manacher 算法实现非常简单. 一 ...
- 二次剩余定理及Cipolla算法入门到自闭
二次剩余定义: 在维基百科中,是这样说的:如果q等于一个数的平方模 n,则q为模 n 意义下的二次剩余.例如:x2≡n(mod p).否则,则q为模n意义下的二次非剩余. Cipolla算法:一个解决 ...
- mongodb入门学习小记
Mongodb 简单入门(个人学习小记) 1.安装并注册成服务:(示例) E:\DevTools\mongodb3.2.6\bin>mongod.exe --bind_ip 127.0.0.1 ...
- DSP算法学习-过采样技术
DSP算法学习-过采样技术 彭会锋 2015-04-27 23:23:47 参考论文: 1 http://wr.lib.tsinghua.edu.cn/sites/default/files/1207 ...
- javascript学习小记(一)
大四了,课少了许多,突然之间就不知道学什么啦.整天在宿舍混着日子,很想学习就是感觉没有一点头绪,昨天看了电影激战.这种纠结的情绪让我都有点喘不上气啦!一点要找点事情干了,所以决定找个东西开始学习.那就 ...
- 算法学习之C语言基础
算法学习,先熟悉一下C语言哈!!! #include <conio.h> #include<stdio.h> int main(){ printf(+); getch(); ; ...
随机推荐
- Python练习-一个简单的生成器
今天我们学习了生成器,怎么理解生成器呢,其实就是使用函数的方式自己建立一个迭代器 # 编辑者:闫龙 #做一个简单的生成器 def EasyGene(*args): #建立一个生成器方法并传递多个参数 ...
- MongoDB警告信息
更多内容推荐微信公众号,欢迎关注: MongoDB警告信息: 1. WARNING: Using the XFS filesystem is strongly recommended with the ...
- JavaScript 优雅的实现方式包含你可能不知道的知识点
有些东西很好用,但是你未必知道:有些东西你可能用过,但是你未必知道原理. 实现一个目的有多种途径,俗话说,条条大路通罗马.很多内容来自平时的一些收集以及过往博客文章底下的精彩评论,收集整理拓展一波,发 ...
- 使用solrJ管理索引——(十四)
a) 什么是solrJ solrj是访问Solr服务的java客户端,提供索引和搜索的请求方法,SolrJ通常在嵌入在业务系统中,通过SolrJ的API接口操作Solr服务,如下图:
- Shell编写8点建议
这八个建议,来源于键者几年来编写 shell 脚本的一些经验和教训.事实上开始写的时候还不止这几条,后来思索再三,去掉几条无关痛痒的,最后剩下八条.毫不夸张地说,每条都是精挑细选的,虽然有几点算是老生 ...
- MyEclipse自带且常用的快捷键和自己定义的快捷键方法步骤
1.MyEclipse自带且常用的快捷键 内容提示(补全): Alt+/ 导包快捷键: Ctrl+Shift+o 格式化代码: Ctrl+Shift+f 行代码位置上下调换: Alt ...
- JAVA随笔(三)
私有是针对类的,而不是对象. static 函数,其实是类函数.之前一直不太理解每个类中的static main是什么意思,为什么main中不能直接调用非静态的变量:因为main是 类函数,不是属于某 ...
- 洛谷P2886牛继电器
传送门啦 倍增 $ Floyd $ 注意结构体里二维数组不能开到 $ 2000 $ #include <iostream> #include <cstdio> #include ...
- linux下Ctrl命令组合
1.键盘组合键操作 ctrl-c 发送 SIGINT 信号给前台进程组中的所有进程.常用于终止正在运行的程序. ctrl-z 发送 SIGTSTP 信号给前台进程组中的所有进程,常用于挂起一个进程. ...
- kickstart配置LINUX无人值守选项--rootpw
linux kickstart rootpw密码可以使用明文,也可以使用加密过的值(密码为:IPPBXADMINROOT) 注意:在这里要使用加密过的值,否则安全性就太低了 rootpw --iscr ...