基本概念
性质
代码实现
欧几里得除法
class Int
{
int value;
Int(int v)
{
value=v;
}
}
public class Euclid
{
static int gcd(int a,int b)
{
return b==0 ? a : gcd(b,a%b);
}
//利用对象传递参数
static int extend_gcd(int a,int b,Int x,Int y)
{
if(b==0)
{
x.setValue(1);
y.setValue(0);
return a;
}
else
{
int r=extend_gcd(b,a%b,y,x);
y.value-=a/b*x.value;
return r;
}
}
}
素数筛
public class Prime
{
ArrayList<Integer> ans;
int tot;
Prime()
{
ans=new ArrayList<>();
tot=0;
}
//复杂度O(NlogN)
void getPrimeMethodOne(int n)
{
boolean[] valid= new boolean[n];
for(int i=2;i<n;i++)
valid[i]=true;
for(int i=2;i<n;i++)
{
if(n<i*i)
break;
for(int j=i*i;j<n;j+=i)
valid[j]=false;
}
for(int i=2;i<n;i++)
if(valid[i])
{
ans.add(i);
tot++;
}
}
//复杂度O(N)
void getPrimeMethodTwo(int n)
{
boolean[] valid= new boolean[n];
for(int i=0;i<n;i++)
valid[i]=true;
for(int i=2;i<n;i++)
{
if(valid[i])
{
tot++;
ans.add(i);
}
for(int j=0;((j<=tot)&&(i*ans.get(j)<n));j++)
{
valid[i*ans.get(j)]=false;
if(i%ans.get(j)==0)
break;
}
}
}
}
质因数分解
static void factor(int n,ArrayList<Integer>ans, ArrayList<Integer>exp)
{
int tmp=(int)((double)Math.sqrt(n)+1);
int tot=0;
int now=n;
for(int i=2;i<=tmp;i++)
{
if(now%i==0)
{
ans.add(i);
exp.add(0);
while(now%i==0)
{
int oldValue=exp.get(tot);
exp.set(tot,oldValue+1);
now/=i;
}
tot++;
}
}
if(now!=1)
{
ans.add(now);
exp.add(1);
tot++;
}
}
基本概念
假设 m ≠ 0 m≠0 m=0 。若 m ∣ a − b m|a-b m∣a−b 则称 a a a 同余于 b b b 模 m m m ,记作 a ≡ b ( m o d m ) a\equiv b(\mathrm{mod} m) a≡b(modm)。
若 m ≥ 1 , ( a , m ) = 1 m≥1,(a,m)=1 m≥1,(a,m)=1,则存在 c c c 使得 c a ≡ 1 ( m o d m ) ca\equiv1(\mathrm{mod}m) ca≡1(modm)。
设整系数多项式 f ( x ) = a n x n + … + a 1 x + a 0 f(x)=a_nx^n+…+a_1x+a_0 f(x)=anxn+…+a1x+a0。
设 n n n 是正整数, φ ( n ) φ(n) φ(n) 是1,2,…,n中和n互质的数的个数,可以证明
ϕ ( n ) = n ∏ p ∣ n ( 1 − 1 p ) \Large{\phi(n)=n \prod_{p|n} (1-\frac{1}{p})} ϕ(n)=np∣n∏(1−p1)
性质
可以用扩展 Euclid 算法求解 a a a 对模 m m m 的逆。
若 a ≡ a ′ ( m o d m ) a \equiv a'(\mathrm{mod} m) a≡a′(modm), b ≡ b ′ ( m o d m ) b \equiv b'(\mathrm{mod} m) b≡b′(modm),则有 a + b ≡ a ′ + b ′ ( m o d m ) a+b \equiv a'+b'(\mathrm{mod} m) a+b≡a′+b′(modm), a b ≡ a ′ b ′ ( m o d m ) ab \equiv a'b'(\mathrm{mod} m) ab≡a′b′(modm)。
欧拉定理(Euler定理)。
费马小定理(Fermat 小定理)。
模 m m m 的一次同余方程F 可以通过扩展 Euclid 算法进行求解。
方程有解的充要条件为 ( a , m ) ∣ b (a,m)|b (a,m)∣b 。在有解的时候,它的模 m m m 意义下不同的解的个数为 ( a , m ) (a,m) (a,m) 。
假设 x = x 0 x=x_0 x=x0 是 a x ≡ b ( m o d m ) ax\equiv b(\mathrm{mod}m) ax≡b(modm)方程的一个解,那么所有模m意义下不同的解可以表示成
x ≡ x 0 + m ( a , m ) t ( m o d m ) \Large{x\equiv x_0+\frac{m}{(a,m)}t(\mathrm{mod} m)} x≡x0+(a,m)mt(modm)
其中 t = 0 , 1 , 2 , … , ( a , m ) − 1 t=0,1,2,…,(a,m)-1 t=0,1,2,…,(a,m)−1。
特解 x 0 x_0 x0 的求法是先通过扩展 Euclid 算法求得 x 0 ′ x_0' x0′ 使得 a x 0 ′ ≡ ( a , m ) ( m o d m ) ax_0'\equiv (a,m)(\mathrm{mod}m) ax0′≡(a,m)(modm),然后只需要让 x 0 = x 0 ′ × b ( a , m ) x_0 =x_0' \times \frac{b}{(a,m)} x0=x0′×(a,m)b
孙子定理或中国剩余定理
设 m 1 , … , m k m_1,…,m_k m1,…,mk 是两两互质的正整数。
解答
定义
m = ∏ 1 k m i M i = m m i \Large{ m=\prod_{1}^{k}m_i\quad M_i=\frac{m}{m_i}} m=1∏kmiMi=mim
由于 m i m_i mi 和 M i M_i Mi 互质,所以存在 M i M_i Mi 对于模 m i m_i mi 的逆 M i − 1 M_i^{-1} Mi−1 。
方程组在模m意义下的唯一解可以表示为
x = ∑ 1 k M i ⋅ M i − 1 ⋅ a i ( m o d m ) \Large{x=\sum_{1}^{k}M_i·M_i^{-1}·a_i(\mathrm{mod} m)} x=1∑kMi⋅Mi−1⋅ai(modm)
对于更加一般的一次同余方程组,可以转化为孙子定理的标准形式求解
代码实现
单变元模线性方程
ArrayList<Integer> line_mod_equation(int a,int b,int n)
{
ArrayList<Integer> result = new ArrayList<>();
Int x=new Int();
Int y=new Int();
int d=Euclid.extend_gcd(a,n,x,y);
if(b%d==0)
{
x.value%=n;
x.value+=n;
x.value%=n;
result.add(x.value*(b/d)%(n/d));
for(int i=1;i<d;i++)
result.add((result.get(0)+i*n/d)&n);
}
return result;
}
中国剩余定理
int solveCRT(int[] a,int[] m,int n)
{
int M=1;
for(int i=0;i<n;i++)
M*=m[i];
int result=0;
for(int i=0;i<n;i++)
{
Int x=new Int();
Int y=new Int();
int tmp=M/m[i];
int d=Euclid.extend_gcd(tmp,m[i],x,y);
result=(result+tmp*x.value*a[i])%M;
}
return (result+M)%M;
}
求欧拉函数
public class Euler
{
int limit;
int[] minDiv,phi,sum;
Euler(int limit)
{
this.limit=limit;
minDiv=new int[limit];
phi=new int[limit];
sum=new int[limit];
}
void getPhi()
{
//素数筛
for(int i=1;i<limit;i++)
minDiv[i]=i;
for(int i=2;i*i<limit;i++)
{
if(minDiv[i]==i)
{
for(int j=i*i;j<limit;j+=i)
minDiv[j]=i;
}
}
//计算欧拉函数值
phi[1]=1;
for(int i=2;i<limit;i++)
{
phi[i]=phi[i/minDiv[i]];
if((i/minDiv[i])%minDiv[i]==0)
phi[i]*=minDiv[i];
else
phi[i]=minDiv[i]-1;
}
}
}
基本概念
原根
设 m m m 为正整数, ( a , m ) = 1 (a, m)=1 (a,m)=1,若 d d d 是最小的使得 a d ≡ 1 ( m o d m ) a^{d} \equiv 1(\bmod m) ad≡1(modm) 成立的正整数, 则 d d d 被称为 $ a $ 对模 $ m $ 的指数, 记作 δ m ( a ) \delta_{m}(a) δm(a) 。
若 δ m ( a ) = φ ( m ) \delta_{m}(a)=\varphi(m) δm(a)=φ(m),则称 a a a 是模 m m m 的原根。其中 φ \varphi φ 为欧拉函数。
a ϕ ( m ) ≡ 1 ( m o d m ) \Large a^{\phi(m)}\equiv 1(\bmod m) aϕ(m)≡1(modm)
设模 m m m 有原根 g g g 。
设 n ⩾ 2 n \geqslant 2 n⩾2 。同余方程 x n ≡ a ( m o d m ) x^{n} \equiv a(\bmod m) xn≡a(modm) 被称为是模 m m m 的二项同余方程。
性质
常用算法
0⩽j<q。因为 γ < φ ( m ) \gamma<\varphi(m) γ<φ(m),所以 i = O ( φ ( m ) ) i=O(\sqrt{\varphi(m)}) i=O(φ(m)) 。
代码实现
求原根
public class primitiveRoot
{
ArrayList<Integer> arr;
primitiveRoot()
{
arr=new ArrayList<>();
}
//计算快速幂
int quick_pow_mod(int a,int x,int mod)
{
if(x==0)
return 1%mod;
int tmp=quick_pow_mod(a,x>>1,mod);
tmp=tmp*tmp%mod;
if(x%2==1)
tmp=tmp*a%mod;
return tmp;
}
//测试
boolean g_test(int g,int p)
{
for(int i=0;i<arr.size();i++)
{
int q = arr.get(i);
if(quick_pow_mod(g,(p-1)/q,p)==1)
return false;
}
return true;
}
//计算
int getPrimitiveRoot(int p)
{
//质因数分解
int tmp=p-1;
for(int i=2;i*i<=tmp;i++)
{
if(tmp%i==0)
{
arr.add(i);
while(tmp%i==0)
{
tmp/=i;
}
}
}
if(tmp!=1)
arr.add(tmp);
int g=2;
while(true)
{
if(g_test(g, p))
return g;
if(g>p)
return -1;
g++;
}
}
}
离散对数
static int getDiscreteLog(int x,int n,int m)
{
int[] arr = new int[(int)Math.sqrt(m) + 1];
int s = (int) Math.ceil(Math.sqrt(m));
int cur=1;
for(int i=0;i<s;i++)
{
arr[i] = cur;
cur = (int) ((long)cur * x % m);
}
// x 的逆元 (模 m) 的s次幂
int tmp=quickPowMod.quick_pow_mod(x, m - 2, m);
int curInv = quickPowMod.quick_pow_mod(tmp, s, m);
int y = n;
for (int i = 0; i < s; i++)
{
for (int j = 0; j < s; j++)
{
if (arr[j] == y)
{
return i * s + j;
}
}
y = (int) ((long)y * curInv % m);
}
return -1;
}
n次剩余
static ArrayList<Integer> residue(int p,int N,int a)
{
//求 p 的原根 g
primitiveRoot pRoot=new primitiveRoot();
int g=pRoot.getPrimitiveRoot(p);
//求离散对数m
int m= discreteLog.getDiscreteLog(g,a,p);
//存储结果
ArrayList<Integer> result=new ArrayList<>();
if(a==0)
{
result.add(0);
return result;
}
if(m==-1)
return result;
// 构造线性同余方程 Ax = C (mod B),其中 A = N, B = p-1, C = m
int A=N,B=p-1,C=m;
Int x=new Int();
Int y=new Int();
//计算 gcd结果
int d=Euclid.extend_gcd(A,B,x,y);
if(C%d!=0)
return result;
// 将 x 的值更新为线性同余方程 Ax = C (mod B) 的一个解
x.value=x.value*(C/d)%B;
int delta=B/d;
for(int i=0;i<d;i++)
{
// 将解转换为对应的元素(使用原根 g 和模幂运算)
x.value=((x.value+delta)%B+B)%B;
result.add(quickPowMod.quick_pow_mod(g, x.value, p));
}
Collections.sort(result);
return result;
}
基本概念
分数式
a 0 + 1 a 1 + 1 a 2 + 1 a 3 + 1 … a n \Large{a_{0}+\cfrac{1}{a_{1}+\cfrac{1}{{a_{2}} +\cfrac{1}{a_{3} +\cfrac{1}{\dots a_{n} }}}}} a0+a1+a2+a3+…an1111
分数式
a 0 + 1 a 1 + 1 a 2 + 1 a 3 … \Large{a_{0}+\cfrac{1}{a_{1}+\cfrac{1}{{a_{2}} +\cfrac{1}{a_{3} \dots}}}} a0+a1+a2+a3…111
一个有理分数 p / q p/q p/q 被称之为实数 x x x 的最佳有理逼近,当且仅当在所有分母不超过 q q q 的有理分数中, ∣ p q − x ∣ |\frac{p}{q}-x| ∣qp−x∣最小
性质
有限连分数的值是一个有理分数。
任意有理分数有且仅有两种有限连分数表示式。
任意有理分数 p / q p/q p/q 其中 ( p , q ) = 1 (p,q)=1 (p,q)=1,它的有限连分数表示式可以通过辗转相除法得到。
比较有限连分数的大小。
设 a = [ a 0 ; a 1 , a 2 , … , a n ] a=[a_0;a_1,a_2,…,a_n] a=[a0;a1,a2,…,an] 和 b = [ b 0 ; b 1 , b 2 , … , b s ] b=[b_0;b_1,b_2,…,b_s] b=[b0;b1,b2,…,bs] 为两个有限连分数。
大小比较
用 a ( k ) a^{(k)} a(k) 表示连分数 a a a 的第 $ k$ 个渐进分数
a ( 1 ) > a ( 3 ) > a ( 5 ) > ⋯ > a ( 2 s − 1 ) > ⋯ a ( 0 ) < a ( 2 ) < a ( 4 ) < ⋯ < a ( 2 t ) < ⋯ a ( 2 s − 1 ) > a ( 2 t ) s ≥ 1 , t ≥ 0 \Large{a^{(1)}>a^{(3)}>a^{(5)}>\cdots>a^{(2s-1)}>\cdots}\\ \Large{a^{(0)}a^{(2t)} \qquad s\ge1,t\ge 0} a(1)>a(3)>a(5)>⋯>a(2s−1)>⋯a(0)<a(2)<a(4)<⋯<a(2t)<⋯a(2s−1)>a(2t)s≥1,t≥0
无限连分数一定收敛,且值为无理数。
比较无限连分数的大小
实数 x x x 的连分数表示的所有渐进分数都是 x x x 的最佳有理逼近。