数论[计算机数学专题(5)]
哒哒哒!掌握一种心理学的学习概念,人的认知是不断成长的,不必要因为一时的失意,而否定您。
数学不好,也没关系,一起成长。早在出生起,我们每天笨拙的咿呀咿呀学汉语与走路,我们最终都学会了。为什么,因为那时候的我们,不会在意别人的眼光,不会怕做的不好就不去练习。
来吧,短暂人生路,让我们一起拥有手眼通天的胆量!更重要的是,如果您可以认真看完这里的,数论可以算入门了,不过我会实时更新,常回来看看哈,RSA加密数论部分更新ing。
《目录》
- 数论基础前置知识:
- NO. 1 约数篇[因数]
- 什么是素数
- 素数验证方法:
- 威尔逊定理趣谈
- GCD 最大公约数 与 LCM 最小公倍数
- 威尔逊定理证明的一种方法:
- 模 %
- 费马大定理
数论基础前置知识:
整除:
- 如果 a 被 p 整除,a 是 p 的倍数,记为:p | a ,如 3 | 9 , 1 | 9, 9 | 9; 其中 1、9 是9的频繁因子 , 3是9的非频繁因子。
传递性:
- 如果 a 是 p 的倍数,那么 a 的倍数也是 p 的倍数,如 2 | 4 , 4 | 12 . 故有 2 | 12,记为 a | b, b | c, 则 a | c。
整数的质因数分解 :
一个正整数拆分为质数的幂的乘积,如 24 = 2 * 2 * 2 * 3 = * , 17 =
质因数分解采用短除法【小学知识】
唯一分解定理 :【数论基石,也叫算术基本定理】
- 正整数的质因数分解是唯一的 , n = * * ··· , 是质数,如上 24 和 17。
质因数分解看除法 :
如 ,12 = * , 180 = * * , 那么;
易发现,正整数的除法,即把俩个数的质因数分解,接着每个质数的质数相减。
质因数分解看整除 :
m | n , 显然 是当且仅当 n ➗ m 是整数。[ 4 | 12 => 12 ➗ 4 ]
解释一下,如果 ,那么算出来的就是一个分数,不是整数。如,。
学了这些,我们就可用这个代替整除,把 n 和 m 质因数分解,然后对比指数。
如果 n 每一个质数的指数都大于等于m 的对应指数,那么 n 就是 m 的倍数 !!
编程,只学理论,不敲代码;都是耍流氓
题目:20 * 30 * 50 / 60 * 70 / 80 * 90 和 7 是否 相等。
3 min ...
那么,暴力高精度 O() , n 是数字长度,可是太慢。用 double 计算,如果有上万次运算,精度无法保证。
需要指出,我们可以采用 刚刚学习的质因数分解方法解,您看最大数 90,90以内的素数只有 24 个。[利用容斥原理也可以得到素数个数 , , , 小于9的素数只有4个, 2、3、5、7,解出来]
我们把每个整数分解成质数的幂,然后存储这些指数。
乘法 : 指数相加 [如果是加法,那只能高精度了]
除法 : 指数相减 [如果是减法,那只能高精度了]
判断是否相等 : 判断每一个指数是否相等 [唯一分解定理]
#include <iostream> using namespace std; const int n = 24; int prime[n] = {2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89};struct Hi {int flag, r[n]; // r[] 存储每个质数的指数friend Hi operator * (Hi a, Hi b) // 指数加法{Hi ans;for( size_t i = 0; i < n; i ++ )ans.r[i] = a.r[i] + b.r[i];return ans;}friend bool operator != (Hi a, Hi b) // 判断指数是否相等{for( size_t i = 0; i < n; i ++ )if( a.r[i] != b.r[i] ) return true;return false;} };int main(void) {// 同九义汝何秀return 0; }
NO. 1 约数篇[因数]
- 整数 a(4) 能被整数 b(2) 整除,a(4) 叫做 b(2) 的倍数,b(2) 就叫做 a(4) 的约数。
- 约数个数是有限的,记为 d(n) (自然数是这样 - 从0开始的整数)
- 所有数的约数都包含 自身 和 (或) 1
借 图.jpg 献计:
请看,n = 1 , d(n) = 1 => 1 的约数有且只有一个即 1;
n = 10 , d(n) = 4 => 10 的约数共 4 个即 1、2、5、10;
其余数同。
那么想一想,我们怎么把这个表格打出来?? 莫急莫急,要成长,就需要建立新的神经元连接,数学与思考正是良计!!
#include <stdio.h> #define N 1000 int d[N]; // 默认为 0 ,存储在 .data 段// 计算 [1,n] 的约数个数 O(n log n) void fews(int n){for( int i = 1; i <= n; i ++ ){for( int j = i; j <= n; j += i ) // 枚举 i 的所有倍数,换一个角度想,本来是约数d[j] ++;} }// 【人之千虑,比有一得 ~ ,您的方法是怎么样的,推敲一遍】 int main(void) {int cnt = 0;int n;scanf("%d",&n);fews(n);for( int i = 1; i <= N; i ++ )if(d[i]) cnt++,printf("%-3d%c",d[i],cnt%10 ? ' ' : '\n');printf("\n[1,n] 每个数约数表");return 0; }
下面选修 ~
数论的基石,唯一分解定理解析约数个数。
一个数 ··· ,且 [a , b , c] > 0, 如
这里指数一一对应, a 可取 {0,1,2} , b 可取 {0,1,2} , c 可取 {0,1} , (a,b,c) 一共3 * 3 * 2 = 18种取法,每种取法对应一个约数。
e.g. a , b , c 都取 1 时, ,而 30 就是 180 的一个约数。
所以,如果一个数 n 可以分解 为 , 则 ta 的约数有
e.g. 180 , , d(n) = (2+1) * (2+1) * (1+1) = 18 种。
OK,欢迎在评论区或者邮件分享您的代码哦·简单的理解了约数和倍数,接下来学习的素数,是数论里的?,非常重要。
许许多多的理论与问题都围绕素数建立,如
- 被用于电影,安全码,谜题,甚至是大学教授的孤独主题
- 哥德巴赫猜想
- 是否有无穷多个梅森素数
- 第六代加密算法 - RSA非对称加密法 [手机支付加密、网银加密]
- 斐波那契数列是否存在无穷多个素数
- 费马大定理是否成立
- 黎曼猜想
- 孪生素数是否有无穷多组 [间隔为2,如 (3,5) , ... ,(71,73) , ...]
- 中国剩余定理 [韩信点兵、物不知数]
- 自然界多数生物的生命周期(年)是素数,最大限度减少碰见天敌的机会[蝉昆虫每隔13或17年从地面发芽]
- 机械中齿轮的齿数是素数,以增加俩⚙️俩个相同齿相遇啮合次数的最小公倍数 [提高了耐用度, 减少故障]
- ... ...
可见素数不是一般的重要,上面列举出来的,程序员是要学习与理解的,只为优质的算法效率足以。
P.S. 我会将上面的问题,都解出来,不过,大概需要一些时间。star ~
什么是素数
数学定义: 素数, 又叫质数, 是指在一个大于 1 的自然数中, 除了1 和 自身外, 无法被其他自然数整除的数。
p.s. 1 、0, 即非素数又非合数,素数也是 "不可分割数" 。
- Primes是所有数字的基石。就像在化学中一样,了解材料的化学结构有助于理解和预测其性质。
- Primes具有特殊属性,如难以确定(是的,即使是困难也可能是一个积极的特征)。这些属性可用于加密,循环以及查看其他数字如何相乘。
小学生都明白的概念,可是,从古至今,多少数学家都想能明白素数的排列的规则,却找不到其具体的分布的规律,只知道是螺旋排列,如下图
每一个黑点都是一个素数 ~
介绍一个程序里算法常模质数 : 19260817。在计数问题中,由于产生的数据规模非常大[高精度问题],这时候要规定一个范围,通常情况是模一个素数。大部分情况 选 19260817 就好,大小合适,也是质数。
目前我知道最大的素数是 2017年12月全球合作项目 "互联网梅森素数搜索" 中由 现年51岁的田纳西州的电气工程师Jonthan Pace 在自己电脑上,发现了M77232917,即 2的77232917次方 - 1。啧啧,不愧是电气工程师,电脑配置肯定杠杠的 ~
素数验证方法:
方法一: 枚举 2 ~ n-1, 如果 n % i == 0, 则 n 为合数
代码略 ... ... O(n²);
方法二: 优化方法一, 若一个数是合数, 则必有一个小于等于ta的平方根的因数 O(n√n)
比如, n = 15, n 的素数有 1、3、5, 除数5因为再 n 被3整除时, 其商是5, 也就表示 n 能被 5 整除。
#include <stdio.h>
#include <math.h>bool is_prime(size_t n)
{size_t qn = sqrt(n);for( int i = 2 ; i <= qn; i ++ )if( qn % i == 0 )return false;return true;
}int main(void)
{printf("%d\n",is_prime(1));return 0;
}
加油加油 ~
方法三: 埃拉托色尼筛 O( n log logn )
一个合数可以被一个不超过 ta 的平方根的素数整除,如果,为找出不超过 100 的素数的个数,首先注意不超过 100 的合数一定有一个不超过 10 的素因子。由于小于 10 的素数只有 2 、3、5、 7,因此不超过 100 的素数就是这 4 个素数以及那些大于 1 和不超过 100 且不被 2、3、5、7 整除的正整数。
把 n 个自然数按次数排序,可用计数数组。
step-0: 筛去 0、1 , 先算出 根号n 的长度,就不用每次循环 i*i 或者 sqrt(n) [优化步骤, 可省略]
step-1: 筛去 合数, 减少一半时间
step-2: 筛去 2的倍数
step-3: 筛去上一步没筛的第一个数(3)的倍数
step-4: 筛去上一步没筛的第一个数(5)的倍数 [如果没去合数,那么就是4, 下面同理,所以说减一半时间]
step-5: 筛去上一步没筛的第一个数(7)的倍数
step-6: 第一个划掉的数字总是当前这轮所用素数的平方, 避免重复判断[优化步骤,可省略]
step-7: 一直循环, 最后留下来的就是不超过n的素数
请看动图
如,i 为 24 时,i 模了 2, 3 (没优化合数和模4) 。
#include <stdio.h> #include <math.h> #define Max 1000 int flag[Max]; // 默认为0, 存储在 .datavoid Init(int *a); void Eratosthenes( size_t n ); // O( n log(logn) )int main(void) {size_t cnt = 0;size_t n = Max;Init(flag);Eratosthenes(n);printf("\t\t[2,%d] 素数表:>\n\n",Max);for ( int i = 2; i < Max; i++ )if( !(flag[i]) )cnt++, printf("%-5d%c", i, cnt % 8 ? ' ': 10);printf("\n\n%d %s %u", Max, "以内的素数共", cnt);return 0; }void Eratosthenes( size_t N ) {for (int i = 2; i * i <= N; i++) // 试除 [2,√n]{if ( !(flag[i]) ) // 筛选素数的标志{for (int j = i * i; j <= N; j += i) // 筛去 i * n,即 i 倍 // 每一轮筛选时, 第一个划掉的数字总是当前这轮所用素数的平方{flag[j]++;}}} }void Init(int *a) // 去合数,优化步骤 step-0 {for( int i = 4; i * i <= Max; i += 2 ) // 合数形式 2n if( i & 0x1 == 0 ) // 同 % 2a[i] ++; }
欧拉筛法(线性筛) :过程同埃筛,不过只模最小质数。 加油,下面有举例。不过,我先讲清楚原理,您认真看上面的动图,可以发现,会有重复筛除的合数。比如 30,在 i = 2 时,会筛30,i = 5 时,会筛30,所以就有了线性筛。
#include <stdio.h> #include <math.h> #define Max 1000int flag[ Max ], cnt; // 默认为0, 存储在 .data void Init(int *a); int juge[ Max ]; void Euler( size_t N ); // O( n )int main(void) {size_t cnt = 0;size_t n = Max;Init(flag);Euler(n);printf("\t\t[2,%d] 素数表:>\n\n",Max);for ( int i = 2; i < Max; i++ )if( !(flag[i]) )cnt++, printf("%-5d%c", i, i % 8 ? 32 : 10);printf("\n\n%d %s %u", Max, "以内的素数共", cnt);return 0; }// 改动部分 void Euler( size_t N ) {for( int i = 2; i <= N; i ++ ) // 枚举每个数{if( !flag[i] ) juge[cnt++] = i; // juge[] 存储素数for( int j = 0; i * juge[j] <= N; j ++ ) // 枚举已经筛好的素数{flag[i * juge[j]] ++; // 筛去合数, 任何合数都可以写为多个素数积if( !( i % juge[j] ) ) break; // 保证 p[j] 是 i * p[j] 最小的素因子, 每个数只被最小质因数筛}} }void Init(int *a) // 去合数 {for( int i = 4; i * i <= Max; i += 2 )if( i & 0x1 == 0 ) // 于 % 2a[i] ++; }
对于上面的程序只改动了这一个接口,这一接口相对上面的埃筛,不同的是最后取模 if(..) break 因为ta 保证取最小素数模。
如,i 为 24 时,i 只模了 2; 还有 3, 4, 并没有模。
方法五,比 欧拉筛 更快。 思路,空间换时间。
俩种实现,第一种,把目前所有知道的素数按顺序存储在一个数组里 [大数存储要设计算法]
第二种,把一定范围的素数按照数组下标缓存,是素数,那么对应下标设为0 [个人爱好]。
说到空间,这里有一种节约空间的方法。位图筛法求素数,代码拿出来好好研究(在理解埃筛后补习一下位操作)。
#include <stdio.h>
#include <stdlib.h>
#define SHIFT 5 // 2的5次方, 一个int占32位
#define MASK 0x1F // 0x0001 1111const int N = 100;
int state[ (N>> SHIFT) + 1 ]; // 申请 100 位 (N>>SHIFT):100-(32*3) = 4 bits
int totalPrime[N]; // 记录有多少个素数bool isPrime( int x )
{ return !( state[x>>SHIFT] & (1<<(x & MASK) ) ); } // 获取结果,0为素数 接着取反 !0 = 1, 所以打印时素数变成 1 void Prime( void )
{memset(totalPrime, 0, sizeof(totalPrime) ); // 记录素数的数组清零memset( state, 0, sizeof(state) ); // 状态数组清零state[0] |= (1<<0); // 设置 0 为合数类(规定 1 不是素数)state[0] |= (1<<1); // 设置 1 为合数类(规定 1 不是素数)for( size_t i = 2; i < N; i ++ ){totalPrime[i] = totalPrime[i-1]; // 因为记录是一个数组,所以需要加上之前统计的if( (state[i>>SHIFT] & (1<<(i&MASK) ) ) ) // 获得状态,是 真 即合数continue;for( size_t j = i*i; j < N; j += i )state[j>>SHIFT] |= (1<<( j&MASK) ); // 位设置if( isPrime(i) ) // 如果是素数计数++totalPrime[i] ++;}
}int main(void)
{Prime( );size_t cnt = 0;for( size_t i=0; i<N; i ++ )cnt ++,printf("%4d : %-4d%c", i, isPrime(i), cnt&3?'\t':'\n' ); // cnt&3 == cnt%4printf("\n 共 %d 个素数",totalPrime[cnt-1]);return 0;
}
2019,新年快乐,祝一切顺利。
当然,如果对素数有兴趣。可以参加全球合作项目 "互联网梅森素数搜索"GIMPS。1996年迄今为止,ta 共找到了16个梅森素数。
Jonthan Pace 获得了 3000 美金,虽然不多但很有意义。发现更多的素数,可以帮助数学家更深入的理解,素数是在上面情况下出现。
【赏金】 GIMPS:
- 谁第一个找到 1 亿位的素数会有 15 万美元的奖励
- 谁第一个找到 10 亿位的素数会有 25 万美元的奖励
目前,找素数都是分布式网络,许多的计算机联合暴力计算完成。因为,谁也没有发现素数具体的分布规律,也没有特定的公式,所以找素数的任务交给了 GIMPS。
下面正式进入素数的世界【反证法,证明素数是无穷多个】
预先给定几个素数,则有比他们更多的素数。
设 a, b, c 为给定的素数, 构造一个数 A 等于 给定素数相乘变为合数再加一
形如, A = a * b * c + 1
那么 A 可以质因数分解嘛 ?[表示成上述的质数他们的乘积]
很显然,我们用其中任何一个素数均不能整除 A 。
- 要么 A 本身是一个素数,那么 A 就不等于 a , b , c 任意一个数。
- 要么 A 能被不同于 a , b , c 的某个数整除。
无论,上述哪种情况。必然存在一个素数 s 不同于已有的素数a, b, c ;如
- 2 * 3 * 5 + 1 = 31
- 3 * 5 * 7 + 1 = 106 = 2 * 53
也就是说,有了 n 个素数,就可以构造出第 n + 1 个素数,因此素数有无穷多个。
素数是有限的,这不是谬论吗?
所以,质数数是无穷的!!质数存储在螺旋数字里的分布也只是模糊的螺旋状。相信,当我们知道的素数达到一定界限时,我们可以找到一条定理来描述素数分布的规律,ta实在太重要了,对数学家,编程爱好者都是如此。
-------------------------------------------------------------------------------------------------------------------------------
威尔逊定理趣谈
有人说,如果一个人不知道威尔逊定理,那么ta的 算术 算是白学了!
夸张吗 ~ 我觉得是有戏剧性的。那您可知道,ta为什么这么说呢??事实上,威尔逊定理是莱布尼茨首先发现的,后经拉格朗日证明的;威尔逊(这里是人名,英国法官J. Wilson)的一位擅长拍马屁的朋友 沃润(E. Waring) 于 1770年出版一本书里说,这条定理是威尔逊发明, 而且对外说这条定理永远不会被证明(因为缺少好的符号来处理素数),这话被高斯听见了,当时高斯也不清楚拉格朗日证明了这条定理。站在 blackboard 前 5 分钟,就证明了这条定理。所以,高斯说,威尔逊差的不是符号,而是概念。
故事说完了,定理我们展开慢慢说。
-------------------------------------------------------------------------------------------------------------------------------
威尔逊定理 :
- 若 p 为素数, 则 (其中 !表示 阶乘),
则威尔逊逆定理也成立,即:
- 若对某一正整数 p,有 (p-1)! + 1 一定是 p 的倍数,所以再利用 sin 函数的特点,可以构造出一个素数分布的函数曲线 这个函数值为 0 的点都是素数所在的点。
稍稍解释一下,如果看了不明白,那么看一下同余。
威尔逊定理应用很广,例如对较大的质数 p,我们虽然无力算出 (p-1)! 的值,但却知道 (p-1)! 被 p 除但余数是 -1 或 p - 1。事实上,由于 (p-1)! + 1 可被 p 整除,则存在自然数 n,使得 (p-1)! + 1 = np,(p-1)! = np - 1 = (n-1)p+(p-1),所以 (p-1)! 被 p除的余数是 -1 或者 p - 1。
我假定,数论您是零基础的,没关系,我们一起解读。
先学习一个同余的概念,选修 ~
,ta 意味着 32 - 2 = 5 * n, n =》6
若 a, b 为俩个整数,且它们的差 a - b 能被某个自然数 m 所整除,则称 a 就模 m 来说同余于b,或者说 a 和 b 关于模 m 同余,记为 ,等同 a - b = m * k (k是整数) 。
推出: (p-1)! + 1 = p * n,OK 同余补习好了,请看上面的红色推导部分吧。
如果我们要判断 p 是否是素数,那么就看 p 满不满足 ,满足就是质数。
#include <iostream> using namespace std;const int Q = 20;void factorial(long long (&fac)[Q] ) // 传引用, 求阶乘 {for( size_t i=1; i<=Q; i ++ ){fac[i] = fac[i-1] * i;} } // 可以换 预处理阶乘 或 快速乘bool check( long long *fac, size_t p ) // p 为一个正整数 {if( fac[p-1]%p == p-1 ) // fac 阶乘数组 [公式: (p-1)! + 1 = p * n]return true; return false; } // 威尔逊逆定理 , O(n) 容易爆 long longint main(void) {long long fac[Q] = {1,1};factorial(fac);for( size_t i=2; i<=Q; i ++ ){if( check(fac,i) )cout<<i<<" ";}return 0; }
其实,这定理。有点费呀,n! 阶乘,对于计算机的内存都是 很伤的,按照 一位算法工程师角度说,这应该是一个无效的坏算法!
下面,是证明威尔逊定理的一种方法。[学会证明,是数学学习的一种很好的偷懒方法因为高效]
设 p 是素数,p = 2 时,定理成立不足道。考虑到所占空间较多,我在博客最后证明。
-------------------------------------------------------------------------------------------------------------------------------
GCD 最大公约数 与 LCM 最小公倍数
有俩个数 n and m,ta们的最大公约数 gcd(n,m) ,满足 d | n 与 d | m 的最大值 d。
那么 d 是多少 ? 如果从质因数分解的角度说,d的指数要尽可能大,但得小于等于 n 和 m 的最小指数[如果选 n 和 m最大指数,就是 最小公约数]。
e.g.
GCD(n,m) =》 n 和 m 质因数分解,于是每个质数取其在 n , m 中最小指数。
LCM(n,m) =》 n 和 m 质因数分解,于是每个质数取其在 n , m 中最大指数。
下面讲解,gcd 证明,选修 ~
xxx
编程,只学理论,不敲代码;都是耍流氓
俩个数的 GCD 我们用 欧几里得算法,LCM 这里有一个很漂亮很漂亮的性质。
GCD(n,m) * LCM(n,m) = n * m
下面证明,为什么是这样的,选修 ~ [前置技能 : 质因数分解]
···
···
p.s. 实际不止 2 , 3 , 5 ,还有更多素数,不过,这个公式衔接我还不会,所以,··· 加不上。
质因数分解那里,有说俩数相乘就是指数相加。
, are you ok , 是不是有点感觉 ?
故,
就等于 n * m , 证毕 。
, 程序写法 : lcm = n * m / gcd(n,m);
程序里面,您可不能这样写,因为 n * m 较大时, 可会爆 long long 。想一想,要改成什么样呢 ???
答 : , 程序写法 : lcm = n / gcd(n,m) * m;
求GCD我知道的实现方法:
- 暴力枚举、
- 更相减损、
- 辗转相除(也叫欧几里得)
- 二进制算法(更相减损+辗转相除优化)、
- 贝祖等式优化欧几里得(辗转相除)、
- 硬件+奇偶优化
代码更新ing...
威尔逊定理证明的一种方法:
对于奇素数,令 , 则 中不会对于除数 p 同余的俩个数; 事实上,若 ,,,则 可被 p 除尽,而 ,但 B 中数不可能被 p 除尽。 于是 B 中数被 p 除得到的余数形成的集合 C = {1, 2, ··· , p-1}。
设 B 中被 p 除余 1 的数 是 :
- 若 ,则 被 p 除于 a ,又 a 2,与 矛盾,故 。
- 若 ,则 ,ta 被 p 除余 a,所以 。
- 若 ,则 ,由于 ,故应有 , 这只能是 a = 1 或 a = p-1,此与 矛盾,故 。
由 1、2、3 得,,且 。
a 不同时, 亦相异; 若 , 且 ,因 , 而 B 中数关于 mod p 不同余,可见 , 则 。
依次取 a 为 2, 3, ···, ; 使 的数 分别为 即:>
从而
2 * 3 * ··· (p-2) 1 (mod p)
又 (p-1)! -1 (mod p),则
(p-1)! = 1 * 2 * 3 * ··· * (p-2) * (p-1) -1 (mod p)
从而 (p-1)! + 1 可被 p 除尽。
若 p 是合数, p 有因数 q, 从而 (p-1)! 可被 q 整除;(p-1)! + 1 不能被 q 整除,亦不能被 p 整除。
计算机RSA算法部分数论知识
Miller_Rabin素数判定 :有一定概率会失败,我们称这样判断的素数为 "工业素数",我在加密博客RSA算法判断素数时便采用的 Miller_Rabin素数判定。
数论学家利用费马小定理研究出许多种素数测试方法,Miller_Rabin素数测试算法是其中一种,其过程如下:
- 计算奇数 M,使得
- 选择一个随机数 A < N
- 对于任意 i < r,若 ,则 N 通过随机数 A 的测试
- 或者,若 ,则 N 通过随机数 A 的测试
- 让 A 取不同的值对 N 进行 5 次测试,若全部通过则判定 N 为素数
若 N 通过一次测试,则 N 不是素数的概率为 25% ,若 N 通过 t 次测试,则 N 不是素数的概率为 。事实上,t = 5 时,N 不是素数的概率为 ,N 为素数的概率已经大于 99.99%。实际应用中,可首先用 300 ~ 500 个小素数对 N 进行测试,以提高Miller_Rabin素数测试通过的概率,从而提高测试速度。而在生成随机素数时,选取的随机数最好让 r = 0,则可省去 步骤3 的测试,进一步提高测试速度。
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
const int cnt = 10;int modular_exp( int a, int m, int n ) // 模幂运算采用平方 -- 乘降幂法算法(下面的模运算细说),RSA 加密重点, 可以改成迭代
{if( 0 == m ) return 1;if( 1 == m ) return ( a % n );long long w = modular_exp( a, m>>1, n );w = w * w % n;if( m & 1 ) w = w * a % n;return w;
}
/* 平方 -- 乘降幂法算法: 避免出现 大数的中间值,比如俩个 200 位素数相乘变成 40000 位的中间值 */bool Miller_Rabin( int n )
{if( 2 == n ) return true;for( int i = 0; i < cnt; i ++ ){int a = rand( ) % ( n - 2 ) + 2;if( modular_exp( a, n, n ) != a ) return false;}return true;
}int main(void)
{srand( time(NULL) );int n;scanf("%d",&n);if( Miller_Rabin(n) ) printf("%s","Probably a prime\n"); // 质数elseprintf("%s","A composite\n"); // 合数return 0;
}
假如随机数选取 4 个数是 2、3、5、7,则在 以内唯一一个判断失误的数为 3 215 031 751。
费马定理 : 若 p 为素数,a 为正整数,且 a 和 p 互质,则 :
证明 :
首先,p-1 个整数 a, 2a, 3a, ··· (p-1)a 中没有一个是 p 的倍数。
其次,a, 2a, 3a, ···(p-1)a 中没有任何俩个同余于模 p 的。
于是 : a, 2a, 3a, ···(p-1)a 对模 p 的同余既不是0,也没有俩个同余相同,因此,这 p-1 个数对模 p 的同余一定是 1, 2, 3, ···p-1 的某一种排列,即:
化简为 :
又由于 p 是素数,根据威尔逊定理得出 (p-1)! 和 p 互质。所以约去 (p-1)!,得到 :
其实这是一种特殊形式,一般情况下,若 p 为素数,则 : ,这就是著名的费马小定理。
欧拉定理 : 若 a 与 m 互质,则
费马定理是用来阐述在素数模下,指数的同余性质。当模是合数的时候,就要应用范围更广的欧拉定理了。
欧拉函数 : 对正整数 n,欧拉函数是小于等于 n 的数中与 n 互质的数的数目。
欧拉函数又称为 函数,如 ,因为 1、3、5、7 均和 8 互质。
引理( 1 ):
1. 如果 n 为某一个素数 p,则 ;
2. 如果 n 为某一个素数 p 的幂次 ,则 : ;
3. 如果 n 为任意俩个互质的数 a、b 的积,则 : ;
证明:
1. 显然;
2. 因为比 小的正整数有 个。其中,所有能被 p 整除的那些数可以表示成 ,即共有 个这样的数能被 p 整除,从而不与 互质。所以,;
3. 在比 a * b 小的 a * b - 1 个整数中,只有那些既与 a 互质(有 个),又与 b 互质(有 个)的数,才会与 a * b 互质。显然满足这种条件的数有 个。所以
比如: 要求 ,因为40 = 5 * 8,且 5 和 8 互质,所以: 。而 。
引理( 2 ):
设 为正整数 n 的素数幂乘积表达式,则:
证明: 由于诸素数幂互相之间是互质的,根据引理(1)得出:
比如:
素数的一般判别方法:
- 枚举 优化为
- 威尔逊逆定理 基本没用,需要大数计算来扩展
- Miller_Rabin素数判定 ,RSA加密术用 C/C++ 实现需要实现大数,让俩个素数达到 1024 位以上
- Eratosthenes埃筛 非常接近线性
- 欧拉筛(线性筛)
- 位图筛法 时间复杂度同筛法,空间复杂度极小~
- 安利资料:判定素数
模 %
模 广泛应用,
- 时钟、手表是 模 12 系统;
- 计算机的 n 位运算器是 模 n 系统,
- 算盘 也是一个模系统;
- 哈希算法本质也是模 运算,让余数保持在一个固定的范围。
- 我们约定的星期几,其实也是一个模 7(6) 系统,
- Web 编程的分页也是 模运算
- ... ...
加法模,例如时钟,10 - 4 = 10 - (12 - 4) = 10 + 8 = 6 (mod 12),
模 7 系统里, 3 + 3 = 6 (mod 7), 6 + 6 = 5(mod 7),
乘法模,在模 13 系统里, 11 * 9 = 8 (mod 13),
11 * 9 = 99 % 13 = 8,
模的另外一种表现形式 : n % p 是否整除可以写为 ,还可以扩展代替除法
随时取模(加法和乘法):
- (a+b)%p = (a%p + b%p) % p ,运用这个模性质,不会让 程序里的 a + b 溢出;
- (a*b)%p = [ (a%p) * (b%p) ] %p,基本同上;
利用上面俩个性质,可将模幂运算转化成模乘运算如,以计算 % n 为例,可以分解为( % n × a % n)%n,将 % n又可以继续分解为( % n × % n)%n, % n最终分解为( % n × % n)%n , % n 即 (a % n * a % n)%n。
费马大定理
当 n 3 时, 方程不存在自然数解。
数论推荐资料:基础数论模版大全\勾股数组
数论[计算机数学专题(5)]相关推荐
- 数论与数学专题练习(一)(201802~201805)
数论与数学专题练习(一)(201802~201805) 手动博客搬家: 本文发表于20180224 00:33:28, 原地址https://blog.csdn.net/suncongbo/artic ...
- 集合论[ 计算机数学专题(1) ]
中学老师说,集合论是现代数学的基础,很多定义都是基于集合论的.如,数列.函数都以集合研究的. 其实我觉得老师,讲的,毫无影响,所以,我就带您重新感受集合的魅力. 我整理了几个关于集合的问题给您. 集合 ...
- 机器学习数学方面的介绍[计算机数学专题(9)]
在学习机器学习之前,应该具有坚实的计算机基础. 深入学习了数据结构与数据结构的算法.计算机组织和系统架构,也具有编写一个完整项目的能力. 我的 AI 专题,主要是以 高中数学 为基础的,所以您应该看看 ...
- 计算几何[计算机数学专题(4)]
<目录> 介绍 解 ...
- 计算机的数学思想源头(回复“计算机数学”可下载PDF典藏版)
计算机的数学思想源头(回复"计算机数学"可下载PDF典藏版) 2018-04-16 数据简化DataSimp 数据简化DataSimp导读:前篇<计算机诞生的详细历史> ...
- 计算机数学专业是应用数学专业吗,大学数学系的数学与应用数学专业学什么课程内容?...
数学,现在大多分为纯数学和应用数学,但最终都是应用数学. 数学与应用数学是计算机专业的基础和上升的平台,是与计算机科学与技术联系最为紧密的专业之一,在计算机领域做出很好的成果的,大多是数学专业背景.在 ...
- 训练指南——数学专题一的总结
差不多一个星期过去了,在这一个多星期里,我做了一个数学专题和两场训练赛,要说对自己的感觉,只能说很差劲,开始的时候以为环境会比现在宽松很多,后来才发现想法是错误的,实验室室里室一种紧张的气氛,感觉就像 ...
- sql相同顺序法和一次封锁法_数学专题 | Ep01 隔板法的妙用
数学专题(一) 隔板法的妙用 浓度常见哪些问题? 排列组合分堆?涂色?到底掌握透彻了吗? 解析几何与韦达定理? 公式总是记不住?应用题还不会解? 除了写作(写作听我的).逻辑(逻辑说)专题外,本周起 ...
- 计算机辅助设计技术案例,【智能科技学院】学院前沿技术运用课程组开展“计算机辅助设计”专题讲座...
3月19日下午,智能科技学院前沿技术运用课程组在学术报告厅开展了"计算机辅助设计"专题讲座,讲座对计算机辅助设计技术在专业中的运用做了具体讲解.主讲人由智能科技学院教师黄毅担任. ...
最新文章
- R语言问题剖析20篇(一)-R语言泛函式编程purrr实现优雅循环迭代
- python生成10000个样本数据集_python产生随机样本数据
- CoolQ/DingTalk 实现CI/CD消息推送到群
- 线性搜索或顺序搜索算法在Java中如何工作? 示例教程
- 兰州交通大学计算机科学与技术学院,兰州交通大学计算机科学与技术
- 每个人都是生活的导演
- java远程关机_通过jsch实现对linux服务器的shell客户端远程控制关机完整示例代码分享...
- c语言二进制加法_“整形数”还真没那么简单(C语言版)
- 英语词汇辨异 —— 形近字、近义词
- java.lang.NoSuchMethodError: org.springframework.web.context.ConfigurableWebApplicationContext.setId
- hdu 1426(DFS+坑爹的输入输出)
- Golang - Structs 包的使用
- BLE 技术(四)--- 链路层五种通信模式和空口协议设计 (Core_v5.2)
- 迷你商城后端管理系统 ———— stage2 项目的核心代码实现
- 压缩软件替代方案BandZip
- 北京 | 微信小程序及小游戏开发者线下交流会
- 2017-2018 ACM-ICPC, Asia Daejeon Regional Contest (大部分题解)
- Java 生成验证码。随机产生一个四位数的验证码,每位数可能是数字、大写字母或小写字母。
- 5、kubernetes Scale Up/Down在线增加或减少 Pod 的副本数、Failover故障转移、用 label(标签)控制 Pod 的位置
- android imageview 拉伸图片大小,【教程】安卓保证图片长宽比的同时拉伸图片
热门文章
- 难处理的js单引号与双引号问题解决
- 高德地图Demo,生成apk发布到手机签名不一致
- 弹性云服务器有什么用
- 视觉定位系统在机器人全场定位的应用
- 关于华为实习的一两点感触
- 巴比特 | 元宇宙每日必读:连续七个季度出现亏损,Meta元宇宙部门Q2亏损28 亿美元,扎克伯格称这种情况可能会持续数年...
- %I64d 和%lld 的区别
- java计算 月数_Java 计算开始年月到结束年月期间的年月数
- 紧跟时代步伐,朗强HDMI分布式矩阵可以通过手机来控制
- Spark Mllib里的分布式矩阵(行矩阵、带有行索引的行矩阵、坐标矩阵和块矩阵概念、构成)(图文详解)...