查看原文
其他

质数判定的几种方法及性能优化

码中人 码农真经 2023-12-25

文章内容

素性测试 (Primality test)

什么是质数?

遍历取模

遍历取模函数式版本

正则表达式法

开方优化

性能比较


最近在刷笔试题,有几个关于质数的题目,总结一下判断是否为质数的几个方法。


素性测试 (Primality test)




素性测试或素数判定,是检验一个给定的整数是否为素数的测试。 维基百科

什么是质数?




质数(Prime number,又称素数),指在大于1的自然数中,除了1和该数自身外,无法被其他自然数整除的数(也可定义为只有1与该数本身两个正因数的数)。

大于1的自然数若不是素数,则称之为合数(也称为合成数)。

遍历取模




判断当前整数是否为质数最常规的方法,就是让它与所有小于它的数(且大于1)取模。

取模运算是求两个数相除的余数。余数为0,说明能被整除。
只要能被其中一个数整除即为合数,不能被所有整除即为质数。
const isPrime = num => { for (let i = 2; i < num; i++) if (num % i === 0) return false; return num > 1;}
const log = n =>{ console.time(n); console.log(n, isPrime(n)); console.timeEnd(n);}
[2,3,4,5,6,7,8,9,10,11,197,977,7919,51977,99991,999983,999987,1000000007,12345678911].map(log);
以上代码时间复杂度为:O(n),被检测的数越大,在经历的循环次数越多,耗时越大。
...
...
10 false
10: 0.025ms
11 true
11: 0.025ms
197 true
197: 0.034ms
977 true
977: 0.063ms
7919 true
7919: 0.362ms
51977 true
51977: 2.624ms
99991 true
99991: 1.359ms
999983 true
999983: 5.989ms
999987 false
999987: 0.024ms
1000000007 true
1000000007: 6007.705ms
12345678911 false
12345678911: 0.046ms
如上,需要6秒钟才能判断1000000007是个质数。

遍历取模函数式版本




可以用数组方法替代for循环。

坏处就是需要生成一个1至n的数组,增加了空间复杂度。n越大,数组越大。
虽然JavaScript中数组长度的最大值为 2**32-1,但实际上肯定到不到2**32-1,如10000000000就不行了。
所以以上代码,因无法生成1000000007长度的数组而无法检测出1000000007等一系列大的质数。

正则表达式法




这个方法很酷,详细的解释,请戳:A wild way to check if a number is prime using a regular expression | by Mark Sauer-Utley | ITNEXT
https://itnext.io/a-wild-way-to-check-if-a-number-is-prime-using-a-regular-expression-4edfb725f895
简单解释:
^1?$|^(11+?)\1+$
这个正则表达式可以匹配所有不是质数的数(合数,以及0,1),模式如下图所示:
这个正则表达式主要是通过将数值转化为字符1的个数来实现监测质数的功能的。此正则表达式分成 由”|”分割两部分: /^1?$//^(11+?)\1+$/
前一部分匹配0或1,这两个数不是质数。
后一部分(11+?)表示至少有两个1,(11+?)\1中的\1表示对前面的匹配的引用。\1+表示前面的(11+?)出现至少一次,相当于(11+?){2,}。整个式子的含义为至少出现两次(11+?)。也就是说,所匹配的数可以写成两个大于2的整数的乘积。而?通过backtrace穷举所有的可能行,从而完成匹配所有的合数。
因此,两部分使用|连接,不匹配的就只能是质数了。
存在问题:
需要生成  “1”.repeat(n)  的字符串,n越大,字符串越大,空间复杂度越大。
同样,理论上js字符串的长度为2**53-1,实际上1000000000就不行了。
100000000可以,但其内存占用100mb以上。
所以正则表达式方法也检测不了大的质数。

开方优化




以上方案中,遍历取模是适用而比较广的,它能检测到质数相对较大。

只要想办法减少遍历次数,即可提升性能。
因为一个数的最小质因数一定会小于或等于这个数的开方(平方根)。
质因数素因数质因子)在数论里是指能整除给定正整数质数。除了1以外,两个没有其他共同质因子的正整数称为互质
  • 1没有质因子。
  • 5只有1个质因子,5本身。(5是质数)
  • 6的质因子是2和3。(6 = 2 × 3)
  • 2、4、8、16等只有1个质因子:2。(2是质数,4 =2²,8 = 2³,如此类推)
  • 10有2个质因子:2和5。(10 = 2 × 5)
如100的开方是10,其质因数 2和5都小于10。121的质因数11,等于其开方11。
因为一个数的最小质因数小于或等于其开方,所以只需要遍历到其开方数就可以了。
其性能大大提升。

性能比较




开方优化>>遍历取模>函数式遍历取模>正则表达式。

你还知道哪些质数判定的方法,请留言告诉我!

参考资料

  • A wild way to check if a number is prime using a regular expression | by Mark Sauer-Utley | ITNEXT
  • 正则表达式检测质数 | HE Tao
  • Demystifying The Regular Expression That Checks If A Number Is Prime – The Codeumentary

往期推荐

最简单的剪映字幕导出工具

逻辑学导论(第13版)

so easy...自制代码截图工具

计算机黑皮书简介

Markdown完全教程

继续滑动看下一个

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存