文章目录
- 前言
- 质数相关
- 质数判断
- 求约数
- 求取区间质数
- 埃氏筛法
- 线性筛法
- 分解质因数
- 欧拉
- 欧拉函数
- 求取单个数
- 线性筛法求取
- 欧拉定理
- 求逆元
- 快速幂/幂取模
- 欧几里得算法
- 求最小公约数
- 拓展欧几里得算法
- 求解同余方程
前言
本文主要是提供Python版本的常见的一些与数论相关的模板,例如求解质数,质因数分解,简单博弈论,以及组合型问题(经典的括号匹配组合问题)等等。至于原理与证明的话,由于存在大量的数学公式推理,因此本文不展示,仅展示代码与变量说明和使用场景。
注意: 相关原理将使用红色字体进行标注,证明可自行查阅资料。本文不再赘述!
质数相关
质数判断
朴素方式:
def isPrim(n):
if(n==1):
return False
for i in range(2,n):
if(n%i==0);
return False
return True
之后的话,由于除法的一些性质,当 n/d = d 的时候,d^2 = n 在这个循环过程当中 d^2 <= n 这个时候的话,是没有必要全部除去的,只需要一般就好了。
优化版本:
def isPrime(n):
if(n==1):
return
i = 2
while(i<=(n//i)):
if(n%i==0):
return False
return True
求约数
这个的话就是前面说的那句话的比较好的运用
def yueShu(n):
i = 1
while(i<=n//i):
if(n%i==0):
print(i)
if(i!=n//i):
print(n//i)
i+=1
此外:
求取区间质数
现在我们知道了如何判断一个质数,那么现在我们需要求取一个区间内的质数,例如,我们需要求取,从1~n之间所有的质数有哪些?
埃氏筛法
直接看到代码:
def getPrims(n):
prime = [0] * (n+1):
st = [False] * (n+1)
cnt = 0
for i in range(2,n+1):
if(not st[i]):
prime[cnt] = i
cnt+=1
j = i+i
while(j<=n):
st[j] = True
j+=i
这个的话,就是把这个质数的倍数给筛掉。
线性筛法
埃氏筛法的效率还是可以的,但是还可以优化一下。
def getPrim(n):
prime = [0] * (n+1):
st = [False] * (n+1)
cnt = 0
for i in range(2,n+1):
if(not st[i]):
prime[cnt] = i
cnt+=1
j = 0
while(prime[j] <=n//i):
st[prime[j]*i] = True
if(i%(prime[j])==0):
break
j+=1
这个的话就是把那个筛选的给优化了一下。
分解质因数
ok,接下来是分解质因数,这个分解质因数是很有用的东西。
N = p1^a1 * p2*a2 …pk^ak
对于一个数N都可以拆解为这样的样子,其中这个P是质数
def division(n):
i = 2
while(i<=n/i):
if(n%i==0):
a = 0
while(n%i==0):
n//=i
a+=1
print("当前质因数为:{},对于的幂是{}".format(i,a))
if(n>1):
print("当前质因数为:{},对于的幂是{}".format(n,1))
欧拉
这个东西的话和我们的这个质数还是有点关系的。
欧拉函数
欧拉函数φ(5) 表示从1~5 当中和5 互质的元素个数有几个。
求取单个数
同样的和我们刚刚的这个质数是类似的,可以直接求取这个欧拉函数,也有求取一个区间的所有欧拉函数值。
def OuLa(n):
res = n
i = 2
while(i<=(n//i)):
if(n%i==0):
while(n%i==0):
n//=i
#res = res* (1-(1/i)) 优化一下避免处于小数
res = res // i *(i - 1)
return res
线性筛法求取
这个的话就是求取这个1~n这个范围的所有的欧拉数
def ouLaLiner(n):
prim = [0] * (n+1)
st = [False] * (n+1)
cnt = 0
phi = [1] * (n+1) # φ(1) = 1
for i in range(2,n+1):
if(not st[i]):
prim[cnt] = i
i+=1
phi[i] = phi[i-1]
j = 0
while(prim[j]<=n//i):
st[prim[j]*i] = True
if(i%prim[j]==0):
phi[prim[j]*i] = phi[i] *(prim[j])
break
phi[prim[j]*i] = phi[i] *(prim[j]-1)
j+=1
欧拉定理
那么接下来就是这个欧拉定理,这个欧拉定理的话,可以用在我们的求逆元的时候用上。
这个定理非常简单。
求逆元
那么这里的话,求解的时候的话,还需要一个求快速幂的算法
快速幂/幂取模
def KuaiSu(a,k):
res = 1
while(k):
if(k%2==0):
res*=a
a*=a
k//=2
return res
def KuaiSu(a,k,p):
res = 1
while(k):
if(k%2==0):
res*=a % p
a*=a % p
k//=2
return res
原理是这个:
(a + b) % p = (a % p + b % p) % p(1)
(a - b) % p = (a % p - b % p ) % p (2)
(a * b) % p = (a % p * b % p) % p .(3)
欧几里得算法
求最小公约数
这里的话主要是这个欧几里得算法,这个欧几里得算法的话作用还是非常大的,一个是求取最小公约数,然后的话就是用拓展欧几里得算法来求取这个同余方程(当然这块是先用裴蜀定理可以证明一下)
def gcd(a,b):
if(b==0):
return a
return (b,a%b)
拓展欧几里得算法
我们使用这个拓展欧几里得算法的话可以求出这个来。
我们先来直接看到代码:
globe x=0,y=0
def exgcd(a,b,x,y):
if(b == 0):
x = 1,y = 0
return a
d = exgcd(b, a % b, y, x);
y -= (a//b) * x;
return d
这里的话这个y 是这样的:
求解同余方程
那么这个拓展欧几里得算法的话就可以去求解同余方程以及我们刚刚的求逆元。
因为我们那个求逆元其实也就是解一个同余方程,只是那个方程比较特殊而已。
为什么可以怎么来的如下:
这里的话就不给代码了,上面有。然后求逆的话是这样的: