实验一
1.1 求最大公因数
#include <stdio.h>
int gcd1(int a,int b){
return b==0?a:gcd1(b,a%b);
}
int gcd2(int a,int b){
int r;
while(b!=0){
r=a%b;
a=b;
b=r;
}
return a;
}
int main(){
int a,b;
scanf("%d%d",&a,&b);
int x=gcd2(a,b);
printf("%d\n",x);
printf("%d",a*b/x);
}
1.2 求s、t,满足(a,b)=sa+tb(扩展欧几里得算法)
https://oi-wiki.org/math/number-theory/gcd/
#include <stdio.h>
void extended_gcd1(int a,int b,int *x0,int *y0){
int r=0,q=0;
int x1=0,y1=1,x2=0,y2=0;
*x0=1;*y0=0;
while(b!=0){
q=a/b;
r=a-q*b;
x2=*x0-x1*q;
y2=*y0-y1*q;
a=b;b=r;
*x0=x1;*y0=y1;
x1=x2;y1=y2;
}
}
void extended_gcd2(int a,int b,int *x,int *y){
if(b==0){
*x=1;
*y=0;
return ;
}
extended_gcd2(b,a%b,y,x);
*y-=a/b*(*x);
}
int main(){
int a,b;
scanf("%d%d",&a,&b);
int a1=a,b1=b;
int x0,y0;
extended_gcd2(a,b,&x0,&y0);
printf("%d=%d*",a,a1);
if(x0<0)
printf("(%d)",x0);
else printf("%d",x0);
printf("+%d*",b1);
if(y0<0)
printf("(%d)",y0);
else printf("%d",y0);
}
1.3 求二元一次不定方程的解 a x + b y = c ax+by=c ax+by=c
这个可以求一个特解
#include <stdio.h>
int gcd(int a,int b){
return b==0?a:gcd(b,a%b);
}
void extended_gcd(int a,int b,int *x,int *y){
if(b==0){
*x=1;
*y=0;
return ;
}
extended_gcd(b,a%b,y,x);
*y-=a/b*(*x);
}
int main(){
int a,b,c,a1,b1,g,x0,y0;
scanf("%d%d%d",&a,&b,&c);
int m=gcd(gcd(a,b),c);
if(m!=1){ //方程化简,等号两边约掉公因数
a=a/m;
b=b/m;
c=c/m;
}
g=gcd(a,b);
if(c%g==0){
printf("方程有解\n");
a1=a/g;
b1=b/g;
extended_gcd(a,b,&x0,&y0);
printf("%d %d\n",x0,y0);
printf("x=%d-(%dt), y=%d+(%dt)",x0*c,b1,y0*c,a1);
}
else {
printf("无解");
}
}
1.4 厄拉多塞筛法求素数
import java.util.*;
public class Main{
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
int[] primeList=new int[n+1];
boolean[] isPrimeList=new boolean[n+1];
for(int i=2;i<n;i++) //初试化都为true,但是0和1为false
isPrimeList[i]=true;
int p=0;
for(int i=2;i<=n;i++){
if(isPrimeList[i])
primeList[p++]=i;
//因为2到i-1的倍数已经筛过了,这里直接从i的倍数开始
if(i*i<=n)
for(int j=i*i;j<=n;j+=i)
isPrimeList[j]=false;
}
for(int i=0;i<p;i++){
System.out.print(primeList[i]+" ");
}
}
}
实验二
2.1 给定a、m和n,计算 a m ( m o d n ) a^m(mod\ n) am(mod n)(模重复平方算法)
将m写成二进制:
m
=
m
0
+
m
1
∗
2
+
m
2
∗
2
2
+
.
.
.
+
m
k
∗
2
k
m=m_0+m_1*2+m_2*2^2+...+m_k*2^k
m=m0+m1∗2+m2∗22+...+mk∗2k。其中
m
∈
{
0
,
1
}
,
i
=
0
,
1
,
.
.
,
k
m\in \{0,1\}, i=0,1,..,k
m∈{0,1},i=0,1,..,k,则有
a
m
≡
a
m
0
(
a
2
)
m
1
.
.
.
(
a
2
k
−
1
)
m
k
−
1
(
a
2
k
)
m
k
(
m
o
d
n
)
a^m\equiv a^{m_0}(a^2)^{m_1}...(a^{2^{k-1}})^{m_{k-1}}(a^{2^k})^{m_k}(mod\ n)
am≡am0(a2)m1...(a2k−1)mk−1(a2k)mk(mod n)
#include <stdio.h>
int Square_and_Multiply(int a,int m,int n){
int a1=a;
int res=1;
while(m>0){
if(m%2==1)
res=res*a1%n;
a1=a1*a1%n;
m/=2;
}
return res;
}
int main(){
int a,m,n; //幂次m,模n
scanf("%d%d%d",&a,&m,&n);
printf("%d",Square_and_Multiply(a,m,n));
}
2.2 设计算法求解一次同余式 a x ≡ b ( m o d m ) ax\equiv b(mod\ m) ax≡b(mod m)
#include <stdio.h>
int gcd(int a,int b){
return b==0?a:gcd(b,a%b);
}
//扩展欧几里得算法
void extended_gcd(int a,int b,int *x,int *y){
if(b==0){
*x=1;
*y=0;
return ;
}
extended_gcd(b,a%b,y,x);
*y-=a/b*(*x);
}
int main(){
int a,b,m;
scanf("%d%d%d",&a,&b,&m);
int d=gcd(a,m);
int a1,b1,m1;
int s,t;
int x0;
if(b%d==0){
a1=a/d;
b1=b/d;
m1=m/d;
printf("同余式的解数为%d\n",d);
extended_gcd(a,m,&s,&t);
x0=s*b1%m;
printf("同余式的一个特解为x0=%d\n",x0);
printf("同余式的全部解为:\n");
for(int i=0;i<d;i++){
int t=x0+m1*i%m;
printf("x%d=%d(mod %d)\n",i+1,t,m);
}
}
else {
printf("同余式无解\n");
return 0;
}
}
RSA公钥密码算法
RSA密码机制
- 选择两个大素数p和q,计算 n = p q n=pq n=pq。
- 随机选取整数e和d,满足 e d ≡ 1 ( m o d φ ( n ) ) ed\equiv1(mod \varphi(n)) ed≡1(modφ(n)),其中 φ ( n ) \varphi(n) φ(n)为n的欧拉函数。
- 发布e和n为公钥,d为私钥。
- 设明文为m,加密函数为 c ≡ E ( m ) ≡ m e ( m o d n ) c\equiv E(m) \equiv m^e(mod \ n) c≡E(m)≡me(mod n),其中 1 < m , c < n 1<m,c<n 1<m,c<n。
- 解密函数为 m ≡ D ( c ) ≡ c d ( m o d n ) m\equiv D(c) \equiv c^d(mod \ n) m≡D(c)≡cd(mod n)
证明
由于
c
≡
m
e
(
m
o
d
n
)
c\equiv m^e(mod \ n)
c≡me(mod n),因此
c
d
≡
m
e
d
(
m
o
d
n
)
c^d\equiv m^{ed}(mod \ n)
cd≡med(mod n)
即证
m
e
d
≡
m
(
m
o
d
n
)
m^{ed} \equiv m(mod\ n)
med≡m(mod n)
当
(
m
,
n
)
=
1
(m,n)=1
(m,n)=1时,由欧拉定理知
m
φ
(
n
)
≡
1
(
m
o
d
n
)
m^{\varphi (n)}\equiv 1(mod \ n)
mφ(n)≡1(mod n)
而由条件2知
e
d
≡
1
(
m
o
d
φ
(
n
)
)
ed\equiv1(mod\ \varphi(n))
ed≡1(mod φ(n))
即存在整数k,使得
e
d
=
k
φ
(
n
)
+
1
ed=k\varphi(n)+1
ed=kφ(n)+1
因此
m
e
d
≡
m
k
φ
(
n
)
+
1
≡
m
(
m
o
d
n
)
m^{ed}\equiv m^{k\varphi(n)+1}\equiv m(mod\ n)
med≡mkφ(n)+1≡m(mod n)
当
(
m
,
n
)
≠
1
(m,n)\not =1
(m,n)=1时,由于
n
=
p
q
n=pq
n=pq,因此
(
m
,
n
)
=
p
或
(
m
,
n
)
=
q
(m,n)=p \ 或 \ (m,n)=q
(m,n)=p 或 (m,n)=q
即p|m,或q|m。若p|m,则显然
m
e
d
≡
m
k
φ
(
n
)
+
1
≡
m
≡
0
(
m
o
d
p
)
m^{ed}\equiv m^{k\varphi(n)+1}\equiv m\equiv0(mod\ p)
med≡mkφ(n)+1≡m≡0(mod p)
若
p
∤
m
p \not | m
p∣m,则由费马小定理知
m
p
−
1
≡
1
(
m
o
d
p
)
m^{p-1}\equiv 1(mod \ p)
mp−1≡1(mod p)
于是
m
k
φ
(
n
)
+
1
≡
m
k
(
p
−
1
)
(
q
−
1
)
+
1
≡
m
(
m
o
d
p
)
m^{k\varphi(n)+1}\equiv m^{k(p-1)(q-1)+1}\equiv m(mod\ p)
mkφ(n)+1≡mk(p−1)(q−1)+1≡m(mod p)
因此,对任意
m
,
m
e
d
≡
m
k
φ
(
n
)
+
1
≡
m
(
m
o
d
p
)
m,m^{ed}\equiv m^{k\varphi(n)+1}\equiv m(mod\ p)
m,med≡mkφ(n)+1≡m(mod p)成立。
同理可证,对任意
m
,
m
e
d
≡
m
k
φ
(
n
)
+
1
≡
m
(
m
o
d
q
)
m,m^{ed}\equiv m^{k\varphi(n)+1}\equiv m(mod\ q)
m,med≡mkφ(n)+1≡m(mod q)成立。因此
m
e
d
≡
m
(
m
o
d
n
)
m^{ed}\equiv m(\mod \ n)
med≡m(mod n)成立。
代码实现
实验三
3.1 欧拉函数的计算
#include <stdio.h>
#include <math.h>
int eular(int n){
int res=n;
for(int i=2;i*i<=n;i++){ //判断n是否为质数
if(n%i==0)
res=res/i*(i-1); //res=res*(1-1/i) 先进行除法防止溢出
while(n%i==0){
n/=i;
}
}
if(n>1)
res=res/n*(n-1);
return res;
}
int main(){
int n;
scanf("%d",&n);
printf("%d",eular(n));
return 0;
}
3.2 勒让德符号的计算
#include <stdio.h>
#include <math.h>
int L_FAST(int a,int p){
int s,a1=a;
if(a==0)
return 0;
else if(a==1)
return 1;
else {
int e=0;
while(a%2==0){
a=a/2;
e++;
}
if(e%2==0)
s=1;
else {
if(p%8==1||p%8==7)
s=1;
if(p%8==3||p%8==5)
s=-1;
}
if(p%4==3&&a1%4==3)
s=-s;
int p1=p%a1;
if(a1==1)
return s;
else return s*L_FAST(p1,a1);
}
}
int main(){
int a,p,L;
printf("请依次输入整数a,奇素数p>=3,且0<=a<p:");
scanf("%d %d",&a,&p);
printf("Legendre符号为:%d\n",L_FAST(a,p));
}
3.3 求原根
import java.util.Scanner;
public class Main{
private static boolean[] primes = new boolean[32170];
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
setPrime();
for (int i = 2; i < n; i++)
if (quickPow(i, n - 1, n) == 1)
if (isPrimeRoot(i, n - 1)) {
System.out.println(i);
return;
}
}
//判断是否为原根
private static boolean isPrimeRoot(int g, int P) {
for (int i = 2; i < P; i++) {
if (!primes[i] && quickPow(P, 1, i) == 0)
if (quickPow(g, P / i, P + 1) == 1)
return false;
while (P % i == 0) P /= i;
}
return true;
}
//筛法求素数
private static void setPrime() {
for (int i = 2; i < 32170; i++)
if (!primes[i])
for (int j = i * 2; j < 32170; j += i)
primes[j] = true;
}
//快速幂
private static int quickPow(int a, int b, int c) {
int ans = 1;
for (; b != 0; b /= 2) {
if (b % 2 == 1)
ans = (ans * a) % c;
a = (a * a) % c;
}
return ans;
}
}