算法——数论(自然数、整除、公约数、同余、素数)
自然数和整数
自然数从0开始,每个自然数都有一个后继。如果一组关于自然数的命题对0成立,且对任意自然数都可以推导出对其后继成立,则该命题对全部自然数成立。(数学归纳法)
负整数∪自然数=整数
整除
如果一个整数a能被另一个整数d整除,记作d|a,这意味着存在某个整数k,使得a=kd。
- 如果d|a,则对于任意自然数k都有d|ka;
- 如果d|a且d|b,则d|(a±b)
- 如果a|b且b|a,则a=b
特殊的整除:
- 如果2能整除a的最末尾,则2|a;如果4能整除a的最后两位,则4|a,因为4|100;如果8能整除a的最后三位,则8|a,因为8|1000;……
- 如果5能整除a的最末尾,则5|a;如果25能整除a的最后两位,则25|a;如果125能整除a的最后3位,则125|a;……
- 如果3能整除a的个位数字之和,则3|a;如果9能整除a的各位数字之和,则9|a;
- 如果11能整除a的偶位数字之和与奇位数字之和的差,则11|a。
公约数和公倍数
- 如果d是a的约数且是b的约数,则d是a与b的最大公约数。
- 如果m是a的倍数并且也是b的倍数,则m是a与b的公倍数。
最大公约数
所有公约数中最大的一个,记作gcd(a,b)。
-
- gcd(a, ka) = |a|;
- 对任意整数a与b,如果d|a并且d|b,则d|gcd(a,b)
- 对所有整数a和b以及任意非负整数n,gcd(an,bn)=n*gcd(a,b)
- 对所有正整数d,a和b,如果d|ab并且gcd(a,d)=1,则d|b
- 如果a=bq+r,则gcd(a,b)=gcd(b,r)
最小公倍数
所有公倍数中最小的那个,记作lcm(a,b)
-
- lcm(a,b)=a*b/gcd(a,b)
辗转相除法求最大公约数和最小公倍数
int gcd(int a, int b) { if (b == 0) { return a; } return gcd(b, a % b); } int lcm(int a, int b) { if (a * b == 0) { return 0; } return a * b / gcd(a, b); }
同余
设m是正整数,a,b是整数,如果m|(a-b),则称a和b关于模m同余,记作a≡b(mod m)
同余的性质
- a≡a(mod m)【自反性】
- 如果a≡b(mod m),则b≡a(mod m)【交换性】
- 如果a≡b(mod m)且b≡c(mod m),则a≡c(mod m),因为m|(a-b+b-c)【传递性】
- 如果a≡b(mod m)且c≡d(mod m),则a±c≡b±d(mod m),ac≡bd(mod m)【结合性】
-
如果a≡b(mod m),则an≡bn(mod m),n∈N
- 如果ac≡bc(mod m),则a≡b(mod (m/gcd(c,m))
-
如果a≡b(mod m)且d|m,则a≡b(mod d)【更小的约数】
-
如果a≡b(mod m),则ad≡bd(mod m)【数乘】
-
如果a≡b(mod mi),i=1,2,…,n,l=lcm(m1,m2,…,mn),则a≡b(mod l)
-
如果p为素数,则ap ≡ a(mod p);如果gcd(a,p)=1,则ap-1 ≡ 1(mod p)
素数
自然数中,除了1之外,只能被1和该数自身整除的数
-
- 其他
- 2是最小的素数
- 2是唯一一个偶素数
筛法求素数
const int MAX = 10000; bool prime[MAX + 1]; void searchprime() { memset(prime, true, sizeof(prime)); prime[1] = false; for (int i = 2; i <= (int)floor(sqrt(MAX)); ++i) { if (prime[i]) { int j = i * 2;
// 标记所有的i的倍数为非素数 while (j <= MAX) { prime[j] = false; j += i; } } } }
素数的判定
-
- 原始的判定方法,根据素数的定义
- 改进的判定方法1,x可以分解为两个整数a,b的积,即 x=a*b,a≤b,那么a ≤sqrt(x)
- 改进的判定方法2,其实2到x的平方根中那些合数也是没有必要用来判断的。如果事先生成一个素数表,循环的次数还可以降低。利用素数表来求解。
bool method_1(int num) { for (int i = 2; i < num; i++) { if (num % i == 0) { return false; } } return true; } bool method_2(int num) { int max = (int)floor(sqrt(num)); for (int i = 2; i <= max; i++) { if (num % i == 0) { return false; } } return true; } bool method_3(int num) { int max = (int)floor(sqrt(num)); int i = 0; while (list[i] <= num) { if (num % i == 0) { return false; } i++; } return true; }