康托展开是一个全排列到一个自然数的双射,常用于构建hash表时的空间压缩。设有n个数(1,2,3,4,…,n),可以有组成不同(n!种)的排列组合,康托展开表示的就是当前排列组合在n个不同元素的全排列中的 名次-1。
若映射f既是单射,又是满射,则称映射f为A到B的“双射”(或“一一映射”)。 函数为双射当且仅当每个可能的像有且仅有一个变量与之对应。
康托展开是一个全排列到一个自然数的双射,常用于构建hash表时的空间压缩。设有n个数(1,2,3,4,…,n),可以有组成不同(n!种)的排列组合,康托展开表示的就是当前排列组合在n个不同元素的全排列中的 名次-1。
若映射f既是单射,又是满射,则称映射f为A到B的“双射”(或“一一映射”)。 函数为双射当且仅当每个可能的像有且仅有一个变量与之对应。
1 | void make_prime() |
初始时,假设全部都是素数,当找到一个素数时,显然这个素数乘上另外一个数之后都是合数(注意上面的 ii , 比 i2 要快点 ),把这些合数都筛掉
但仔细分析能发现,这种方法会造成重复筛除合数,影响效率。比如10,在i=2的时候,k=215筛了一次;在i=5,k=56 的时候又筛了一次。所以,也就有了快速线性筛法。
快速线性筛法没有冗余,不会重复筛除一个数,所以“几乎”是线性的。
1 | #include <iostream> |
任何合数都能表示成一系列素数的积
不管 i 是否是素数,都会执行到“关键处1”,
①如果 i 都是是素数的话,那简单,一个大的素数 i 乘以不大于 i 的素数,这样筛除的数跟之前的是不会重复的。筛出的数都是 N=p1*p2的形式, p1,p2之间不相等
②如果 i 是合数,此时 i 可以表示成递增素数相乘 i=p1p2…*pn, pi都是素数(2<=i<=n), pi<=pj ( i<=j )
p1是最小的系数。
根据“关键处2”的定义,当p1==prime[j] 的时候,筛除就终止了,也就是说,只能筛出不大于p1的质数*i。
我们可以直观地举个例子。i=235
此时能筛除 2i ,不能筛除 3i
如果能筛除3i 的话,当 i’ 等于 i’=335 时,筛除2i’ 就和前面重复了。
欧几里得算法,又名辗转相除法1
2举例:
辗转相除法基于如下原理:两个整数的最大公约数等于其中较小的数和两数的差的最大公约数。例如,252和105的最大公约数是21(252 = 21 × 12;105 = 21 × 5);因为252 − 105 = 21 × (12 − 5) = 147,所以147和105的最大公约数也是21。在这个过程中,较大的数缩小了,所以继续进行同样的计算可以不断缩小这两个数直至其中一个变成零。这时,所剩下的还没有变成零的数就是两数的最大公约数。
递归公式表示位:
gcd(a,b) = gcd(b,a mod b)
两个数的最大公约数通常写成gcd(a, b),如果有gcd(a, b)==1,则有a,b互质。
在数学中,尤其是高等数学的环论中,最大公约数有一个更加巧妙的定义:a和b的最大公约数g是a和b的线性和中(绝对值)最小的一个,即所有形如ua + vb(其中u和v是整数,可正可负)的数中(绝对值)最小的数。所有ua + vb都是g的整数倍(ua + vb = mg,其中m是整数)。
1 | int gcd(int a, int b) |
1 | int gcd(int a, int b) |
扩展欧几里德算法是用来在已知a, b求解一组p,q使得pa+qb=Gcd(a,b)。
扩展欧几里德常用在求解模线性方程及方程组中。
1 | //求解x*a+y*b=gcd(a,b) 中的x, y, gcd(a,b)即exgcd的return |
把这个实现和Gcd的递归实现相比,发现多了下面的x,y赋值过程,这就是扩展欧几里德算法的精髓。 可以这样思考: 对于a’ =b , b’ =a%b 而言,我们求得x, y使得a’ x+b’ y=Gcd(a’, b’) 由于b’ = a % b = a - a / b * b 那么可以得到:
1 | a'x + b'y = gcd(a', b') |
Stein算法由J. Stein 1961年提出,这个方法也是计算两个数的最大公约数。和欧几里德算法 算法不同的是,Stein算法只有整数的移位和加减法,因此对于大素数Stein将更有优势。
1 | 1. 如果A=0,B是最大公约数,算法结束 |
理由:设在某一次过程中A、B的最大公因子为D,可得A=kD,B=hD;(A>B -> k>h); 所以A-B=|k-h|*D。A,B都为奇数,由奇偶数乘积奇偶性可得D,k,h都为奇数。所以|k-h|一定是偶数,与h互质,所以A-B与B的最大公因数依旧为D。
(偶数乘奇偶数都是偶数,只有奇数相乘才奇数)A为奇数->k和D为奇数,B为奇数->h和D为奇数,两个奇数相减得偶数,所以A-B为偶数,k-h为偶数。
1 | int gcd(int a, int b) |
严格意义上来说,负数和正数之间是没有公因子这一说的【因子要大于0】,但是如果非要求他们的正因子【负因子】,可以先把两个数取绝对值,stein之后再加个符号即可。
对于不定整数方程pa+qb=c,若 c mod Gcd(p, q)=0,则该方程存在整数解,否则不存在整数解。 上面已经列出找一个整数解的方法,在找到p a+q b = Gcd(p, q)的一组解p0,q0后,p a+q b = Gcd(p, q)的其他整数解满足:
至于pa+qb=c的整数解,只需将p a+q b = Gcd(p, q)的每个解乘上 c/Gcd(p, q) 即可。 在找到p a+q b = Gcd(a, b)的一组解p0,q0后,应该是得到p a+q b = c的一组解p1 = p0(c/Gcd(a,b)),q1 = q0(c/Gcd(a,b)),p a+q b = c的其他整数解满足:
此处的p 、q就是p a+q b = c的所有整数解。
1 | bool linear_equation(int a,int b,int c,int &x,int &y) |
同余方程 ax≡b (mod n)对于未知数 x 有解,当且仅当 gcd(a,n) | b。(原因见下一条
且方程有解时,方程有 gcd(a,n) 个解。 求解方程 ax≡b (mod n) 相当于求解方程 ax+ ny= b, (x, y为整数)。
设 d= gcd(a,n),假如整数 x 和 y,满足 d= ax+ ny(用扩展欧几里德得出)。
如果 d| b,则方程a x0+ n y0= d, 方程两边乘以 b/ d,(因为 d|b,所以能够整除),得到 a x0 b/ d+ n y0 b/ d= b。
所以 x= x0 b/ d,y= y0 b/ d 为 ax+ ny= b 的一个解,所以 x= x0* b/ d 为 ax≡ b (mod n ) 的解。
ax≡b (mod n)的一个解为 x0= x (b/ d ) mod n,且方程的 d 个解分别为 xi= (x0+ i (n/ d ))mod n {i= 0… d-1}。
设ans=x*(b/d),s=n/d; 方程ax≡b (mod n)的最小整数解为:(ans%s+s)%s;
1 | 相关证明: |
1 | 首先看一个简单的例子: |
1 | bool modular_linear_equation(int a,int b,int n) |
同余方程ax≡b (mod n),如果 gcd(a,n)== 1,则方程只有唯一解。
在这种情况下,如果 b== 1,同余方程就是 ax=1 (mod n ),gcd(a,n)= 1。
这时称求出的 x 为 a 的对模 n 乘法的逆元。
对于同余方程 ax= 1(mod n ), gcd(a,n)= 1 的求解就是求解方程
ax+ ny= 1,x, y 为整数。这个可用扩展欧几里德算法求出,原同余方程的唯一解就是用扩展欧几里德算法得出的 x 。