信息学竞赛中数论常见问题.docx
- 文档编号:25918352
- 上传时间:2023-06-16
- 格式:DOCX
- 页数:21
- 大小:21.95KB
信息学竞赛中数论常见问题.docx
《信息学竞赛中数论常见问题.docx》由会员分享,可在线阅读,更多相关《信息学竞赛中数论常见问题.docx(21页珍藏版)》请在冰豆网上搜索。
信息学竞赛中数论常见问题
Prime
概述:
>1的数,除了1和本身没有其他因子.
1既不是素数也不是合数,0和所有的负整数同样如此.
100以内的素数
{2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97}
(1)
#definen1000003
intprime[n];
//要判断的范围是多大,就要设置多大的数组,对应于某个数,看它下标对应的数是1,那么是素数,否那么,不是素数
voidgetPrime(){
inti,j;
intbound=sqrt((double)n);
for(i=2;i for(i=2;i for(j=i+i;j prime[j]=0; } } 打表 (2) intprim[50],pn=0; boolpp[101]; voidpre() { inti,j; for(i=2;i<=100;i++) { if(! pp[i])prim[pn++]=i; for(j=0;j { pp[prim[j]*i]=1; if(i%prim[j]==0)break; }} } Pn是1至100范围内素数的个数,prim[i]一次存放这pn个素数.这种方法的好处是存储素数时节省空间,输出素数时节省时间 2.直接判断 boolisPrime2(intx){ if(x<=1)returnfalse; if(x==2)returntrue; if(x%2==0)returnfalse; doublebound=sqrt((double)x); for(inti=3;i<=bound;i+=2){ if(x%i==0){ returnfalse; } } returntrue; } GCD(最大公约数)---欧几里得算法 a,b为正整数,设集合A={x*a+y*b|x,y是整数},那么A中最小正元素是gcd(a,b) longkgcd(longa,longb) { if(a==0)returnb; if(b==0)returna; if(! (a&1)&&! (b&1))returnkgcd(a>>1,b>>1)<<1; elseif(! (b&1))returnkgcd(a,b>>1); elseif(! (a&1))returnkgcd(a>>1,b); elsereturnkgcd(abs(a-b),min(a,b)); } LCM(最小公倍数) LCM(a,b)=a*b/GCD(a,b) 实际上最好写成a/GCD(a,b)*b longlcm(longa,longb) { longc,d,sw; c=(a>=b)? a: b; d=(a<=b)? a: b; while(c%d! =0) { sw=c%d; c=d; d=sw; } return(a/d)*b; } 求多个数的lcm,需要将res初始化为1 相关题目: 1.LeastCommonMultiple 2.wolfandrabbit 扩展欧几里得(可以求解模线性方程) a*x+b*y=gcd(a,b)一系列解是x=x+b,y=y-a intextgcd(inta,intb,int&x,int&y){ if(b==0){ x=1; y=0; returna; } intd=extgcd(b,a%b,x,y); intt=x; x=y; y=t-a/b*y; returnd; } a*x+n*y=b==ax=b(modn)模线性方程 定理: 方程ax=b(modn)对于未知量x有解,当且仅当gcd(a,n)|b. 令d=gcd(a,n). 设为x',y'为所求满足ax'+ny'=gcd(a,n). 那么原方程有一解x0=x'*(b/d)modn. 定理: 假设ax=b(modn)有解,x0是该方程的任意一个解,那么该方程模n恰有d个不同的解,分别为: xi=(x0+i*(n/d))modn(其中i=1,2,...d-1). //a*x=b(%n) voidmodeq(__int64a,__int64b,__int64n) { __int64e,d,x,y,t; d=extgcd(a,n,x,y); if(b%d) printf("Impossible\n"); else{ e=(x*(b/d))%n+n; t=n/d; t=t>0? t: -t; e%=t; if(e<0) e+=t; printf("%I64d\n",e); } }题目: 1.2669 假设n=p1e1p2e2…prer,那么 n的因数个数为 (1+e1)*(1+e2)*……(1+er) n所有因数的和为(1+p1+p12+…+p1e1)*(1+p2+p22+…+p2e2)*…*(1+pr+pr2+…+prer) 数的各位之和 intsum(intnumber) { intsum=0; while(number! =0) { sum+=number%10; number/=10; } returnsum; }//不知道数有几位,但是可以每次都取个位 质因数分解 对于i,从2到sqrt((double)m),假设m%i==0,那么i是m的一个质因数, 然后m/i=m,i=2,重新判断,直至最后,剩下的那个数同样是一个质因数,要加上的. intzhi(intn,int*x){ intcount=0; inti=2; while(i<=(int)sqrt((double)n)){ if(n%i==0){ x[count++]=i; n=n/i; }else{ i++; } } x[count++]=n; returncount; }//返回质因数的个数,数组x是所有的因子 //n是要分解的数,pn是100范围内质数的个数,prim是100范围内的所有质数,q存储分解出来的质数相应的个数 intzhi_num(intn,intpn,int*prim,int*q) { for(intj=0;j { if(n%prim[j]==0) { __int64temp=0; while(n%prim[j]==0) { temp++;//相应的prim[j]的个数 n/=prim[j]; } q[j]+=temp; } } } 调用: prim[25]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97}; intq[25]={0}; intpn=25; zhi_num(20,pn,prim,q); 欧拉函数 欧拉函数值是小于或等于n的数中与n互质的数的个数Phi(N)同样如此 公式: φ(x)=x(1-1/p1)(1-1/p2)(1-1/p3)(1-1/p4)…..(1-1/pn), 其中p1,p2……pn为x的所有质因数,x是不为0的整数。 性质有: 1.φ (1)=1〔唯一和1互质的数就是1本身〕。 2.假设n是素数,那么φ(n)=n-1; 3.假设n是合数,那么φ(n) 4.当n为奇数时,φ(2n)=φ(n), 5.假设a,b互质,那么,φ(a*b)=φ(a)*φ(b); 代码: inteuler(intx){ inti,res=x; for(i=2;i<(int)sqrt(x*1.0)+1;i++){ if(x%i==0){ res=res/i*(i-1); while(x%i==0)x/=i; } } if(x>1)res=res/x*(x-1); returnres; } 中国剩余定理 不管互质或者是不互质,只要在输入时进展互质处理,即可.而且,是_int64,显然通用性更强 下面程序的输入输出格式: 3//每组有几组数据 32//除数余数 53 72 #include usingnamespacestd; __int64x,y,t; __int64extend_gcd(__int64a,__int64b)//returngcd(a,b)得到x,y { if(b==0) { x=1; y=0; returna; } else { __int64temp=extend_gcd(b,a%b); t=x;x=y; y=t-a/b*y; returntemp; } } intmain() { __int64a1,a2,r1,r2,c,gcd,temp; boolyes; intn; freopen("G: //in.txt","r",stdin); while(scanf("%d",&n)! =EOF)//每组有一组数据 { yes=false; scanf("%I64d%I64d",&a1,&r1);//a是除数,r是余数 for(inti=0;i { scanf("%I64d%I64d",&a2,&r2); if(yes) continue; c=r2-r1; gcd=extend_gcd(a1,a2); if(c%gcd! =0) { yes=true; continue; } temp=a2/gcd; x=(c/gcd*x%temp+temp)%temp; r1=a1*x+r1; a1=a1*a2/gcd; } if(yes) printf("-1\n"); else printf("%I64d\n",r1); } return0; } 闰年是否并返回一年天数 /*判断是否闰年*/ boolisleap(int&year) { if(year%4==0&&year%100! =0||year%400==0)returntrue; elsereturnfalse; } /*返回一年的最大天数*/ intmaxday(int&year) { if(isleap(year))return366; elsereturn365; } Days是指间隔某个日期是多少天,应该均可以的,只是最终结果可能有所变化的. stringgetweek(int&days) { returnweek[days%7]; } 一个关于日历的题目 #include #include usingnamespacestd; stringweek[]={"Saturday","Sunday","Monday","Tuesday","Wednesday","Thursday","Friday"}; intday[]={0,31,28,31,30,31,30,31,31,30,31,30,31}; /*判断是否闰年*/ boolisleap(int&year) { if(year%4==0&&year%100! =0||year%400==0)returntrue; elsereturnfalse; } /*返回一年的最大天数*/ intmaxday(int&year) { if(isleap(year))return366; elsereturn365; } /*得到年份*/ intgetyear(int&days) { intyear=2000; while(days>maxday(year)) { days-=maxday(year); year++; } returnyear; } /*得到月份*/ intgetmonth(intyear,int&days) { intmonth=1; if(isleap(year))day[2]=29; elseday[2]=28; while(days>day[month]) { days-=day[month]; month++; } returnmonth; } /*得到天数*/ intgetday(int&days) { returndays; } /*得到星期*/ stringgetweek(int&days) { returnweek[days%7]; } /*主函数*/ intmain() { intdays; inty,m,d; stringwk; while(cin>>days&&days! =-1) { wk=getweek(days); days++; y=getyear(days); m=getmonth(y,days); d=getday(days); cout< if(m<10)cout<<0; cout< if(d<10)cout<<0; cout< } //system("pause"); return0; } 生成两个大的素数P,Q,乘起来得N=P*Q.然后算出N的欧拉函数Phi(N)=(P-1)*(Q-1).然后我们取一个范围在[1,phi(N)]中且与phi(N)互质的正整数E.它就是所谓的公钥。 得到公钥之后,我们再算出E关于phi(N)的逆元D,即E*Dmodphi(N)=1.这个D就是私钥。 在得到这些数据以后,P,Q被丢弃,E,N做为公钥被公开,D做为私钥被解密人私人保存。 假设有一个明文M,那么它所对应的密文就是C=M^EmodN. 假设我们如今得到一个密文C,那么它所对应的明文就是M=C^DmodN 也就是说,任何人都可以用公钥对数据进展加密,但是只有拥有私钥的人才可以对数据进展解密。 将N分解成P*Q的乘积。 那么就可以直接利用公式phi(N)=(P-1)*(Q-1)绕开暴力求解欧拉函数的过程,从而实现RSA的破解。 这道题就是模拟这个破解过程,下面来说说详细的做法: 1.首先用miller-rabin,pollard_rho做大整数的质因数分解,得到两个素数P,Q,pollard_rho的复杂度在N^0.25次方,那么一个64位的整数要计算的次数为2^64^0.25=2^16=65536次,可以瞬间出解。 2.求出phi(N)=(P-1)*(Q-1) 3.然后用ext_gcd求出E关于phi(N)的逆元。 4.用得到的私钥对数据C进展解密即可。 PS: 对这题而言,仅仅完成上述步骤还是不够的。 因为N到达2^62次方,即使是使用无符号longlong,也很容易因为出乘法操作而溢出。 这也是网上说要防止使用扩展欧几里德的原因。 其实实现的时候,我们可以自己写一个特殊的乘法〔内部用加法实现〕,由于使用的无符号的longlong,第64位刚好可以用来保存两个数加过之后的进位位,再模除又可以保证其在2^62范围内,即可防止溢出 #include #include #include #include #include usingnamespacestd; #definebigint__int64//这道题目不能用无符号数,不然wa //欧几里德,求最大公约数 bigintgcd(biginta,bigintb) { while(b) { bigintc=a%b; a=b; b=c; } returna; } //a*b%n bigintproduct_mod(biginta,bigintb,bigintn) { biginttmp=0; while(b) { if(b&1) { tmp+=a; if(tmp>=n)tmp-=n; } a<<=1; if(a>=n)a-=n; b>>=1; } returntmp; } //a^m%n bigintpower_mod(biginta,bigintm,bigintn) { biginttmp=1; a%=n; while(m) { if(m&1)tmp=product_mod(tmp,a,n); a=product_mod(a,a,n); m>>=1; } returntmp; } intpri[]={2,3,5,7,11,13,17,19,23,29}; //Miller_Rabin大素数判断 boolMiller_Rabin(bigintn) {//n,s.取s个随机数值a,进展a是n为和数的证明判断 if(n<2)returnfalse; if(n==2)returntrue; if(! (n&1))returnfalse; bigintk=0,i,j,m,a; m=n-1; while(! (m&1))m>>=1,k++; for(i=0;i<10;i++) { if(pri[i]>=n)returntrue; a=power_mod(pri[i],m,n);//幂取模 if(a==1)continue; for(j=0;j { if(a==n-1)break; a=product_mod(a,a,n); } if(j==k)returnfalse; } returntrue; } //pollard_rho随机质因数分解 bigintpollard_rho(bigintC,bigintN)//返回一个平凡因子 { bigintI,X,Y,K,D; I=1; X=Y=rand()%N; K=2; do { I++; D=gcd(N+Y-X,N);//这里为了防止负数,先加上一个N if(D>1&&D if(I==K)Y=X,K<<=1; X=(product_mod(X,X,N)+N-C)%N;//随机一个增量来求X,Y,使得gcd(Y-X,N)不是非平凡因子(不是1,跟N) } while(Y! =X); returnN; } //二分,分解N的质因数,这里返回最小质因子,假设想要其他的话,可以用个数组存起来 bigintrho(bigintN) { if(Miller_Rabin(N)) returnN; bigintT=N; while(T>=N) T=pollard_rho(rand()%(N-1)+1,N); bigintA=rho(T); bigintB=rho(N/T); returnA A: B; } //扩展欧几里德,求ax+by==gcd(a,b)中的x,y,其中d==gcd(a,b) voidGcd(biginta,bigintb,bigint&d,bigint&x,bigint&y) { if(! b) { d=a,x=1,y=0; return; } Gcd(b,a%b,d,y,x); y-=x*(a/b); } //求a关于n的逆 bigintinv(biginta,bigintn) { bigintd,x,y; Gcd(a,n,d,x,y); if(d==1)return(x%n+n)%n; elsereturn-1; } bigintP,Q;//N=P*Q bigintT;//T=(P-1)*(Q-1) bigintC,E,N; intmain() { freopen("G: //in","r",stdin); srand((unsignedint)time(NULL)); //的是密文,公钥,N(=P*Q). while(scanf("%I64d%I64d%I64d",&C,&E,&N)! =EOF) { P=rho(N);//找两个大素数,使得P*Q=N.如今是N,去求这两个大素数 Q=N/P; T=(P-1)*(Q-1);//其实T就是phi[N],N的欧拉函数,因为P,Q都是素数,所以(P-1)*(Q-1)就是他的欧拉函数 bigintD=inv(E,T);//求E关于T的逆元,因为这里是gcd(E,T)==1,而又要E*D%T==1 bigintM=power_mod(C,D,N);//M=(C^D)%N,假设是求C,就是C=(M^E)%N printf("%I64d\n",M); } }
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 信息学 竞赛 数论 常见问题