求 0-N 内有多少个素数
Posted half-worm
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了求 0-N 内有多少个素数相关的知识,希望对你有一定的参考价值。
问题:求 0-N 内素数的个数。
预备知识
- 什么是素数:素数(又叫质数),与之相反的是合数。素数的因数只有 1 和 他本身,例如:7 = 1 * 7。而 6 = 1 * 6 = 2 * 3,因此6不是素数。 规定:0 和 1 既不是 素数 也不是 合数。
- 判断素数:如果一个数 x 是素数,那么在整数范围 [2,√x ] 之间,找不到任何能整除x 的整数。为什么只需要尝试到 √x ,而不是 n-1 呢?(肯定不是n呀,因为n是他本身,判断 n 就和判断 1 一样,没有意义的)。
- √x 的由来:一个正数 n ,可以表示成:n = √n * √n 。n 的两个因数有以下两种可能:? 两个因数都为 √n ?一个因数大于 √n,另一个因数一定小于 √n。如果我们在 2 到 √n 之间找不到一个因数的话,那么对应的在 √n 到 n-1 的范围内 绝对也找不到另外一个因数。而素数所期待的不正是 2 到 n-1 的范围内找不到两个因数,只希望因数为 1 和 n 吗?
阶段一
1 public void countPrime_1(int n) {
2
3 // 之所以数组范围设置为 n+1,是因为数组从下标为 1 开始计数,n表示最后一个数
4 // 数组初始值为false,因为我们假设false代表是素数,true代表不是素数
5 boolean[] b = new boolean[n + 1];
6
7 // 2 - n 每个数逐一判断
8 for (int i = 2; i <= n; i++) {
9 // 【2,√i】
10 for (int j = 2; j * j <= i; j++) {
11 if (i % j == 0) {
12 // 在判断 i 是否为素数的过程中,遍历 2 - √i
13 // 一旦发现在【2,√i】中,有数整除了,将数组b中下标为i的设置为true,
14 // 代表不是素数,并且中断内层for循环,因为已经判断出i不是素数了,没有必要
15 // 继续循环判断下去。 直接判断第i+1个数
16 b[i] = true;
17 break;
18 }
19 }
20 }
21
22 /**
23 * 此部分内容在接下来将不写
24 */
25 int count = 0;
26 // 下标从2开始,是因为 1 不是素数也不是合数,没有必要判断
27 for (int i = 2; i <= n; i++) {
28 // 当为false时,代表素数
29 if (b[i] == false) {
30 count++;
31 }
32 }
33 }
注意:在第10行中的for循环内,使用的是 j * j <= i ,而不是 j <= sqrt(i)。原因:第一:sqrt是用来处理浮点数的,而浮点数的计算速度远远慢于integer。第二,函数调用也会造成时间的浪费。第三:浮点数的存储误差可能引出致命错误,如 sqrt(9) 可能等于 2.9999999 ,那么 int(sqrt(9)) 就等于2 而不是3。
阶段二
筛选法:在一张纸上写上 1-n 全部整数,然后逐个判断是否为素数,找出一个非素数,就把它挖掉,最后剩下的就是素数。
具体做法如下:
- 先将 1 挖掉(因为1不是素数)
- 用 2 去除它后面的各个数,把能被 2 整除的数挖掉,即把 2 的倍数挖掉
- 用 3 去除它后面各数,把 3 的倍数挖掉
- 分别用 4、5 等数作为除数去除这些数以后的各数。这个过程一直进行到在除数后面的数已全被挖掉为止
- 剩下的数就是素数
1 public void countPrime_2(int n) {
2
3 boolean[] b = new boolean[n + 1];
4
5 for (int p = 2; p * p <= n; p++) {
6 // 假设当 p 为2时,也就是判断在数组 b 中下标为 2 的值是不是false,即 数字2 是不是素数
7 // 如果是素数,就将 2 的倍数 4 6 8 10 等等全部标记为 非素数,即b[2] b[4] b[6] 的值为true
8 if (b[p] == false) {
9 /**
10 * 当 p 为 3 时,j = 3 * 3 = 9 。哎??? 怎么直接将 9 标记为 非素数了 ???
11 * 不应该是将 6 先标记为 非素数吗? 直接标记 9 了,那 6 怎么办 ???
12 * 因为: 6 也是 素数2的倍数,在计算素数 2 的时候就已经将6除去了
13 * 所以直接从 p * p 开始计算,相当于又优化了
14 * j = j + p 代表跳到 p 的下一个倍数
15 */
16 for (int j = p * p; j <= n; j = j + p) {
17 if (b[j] == false) {
18 b[j] = true;
19 }
20 }
21 }
22 }
23 }
对于一个数p,会依次去除 p*p , p*(p+1) , p*(p+2) .... p*(p+k) 【p*(p+k)<=n】
前面不是说要去除 p 的所有倍数的吗?那 p*2 ,p*3, p*4 ... p*(p-1)怎么不去除呢?
他们已经被去除了。因为当前我们要消去 p 的倍数,那么,之前一定去除了 2 3 4 ... p-3 , p-2 ,p-1 的这些数 的倍数,举个例子:之前一定去除了 2*p 3*p 4*p (p-1)*p。所以 , 当想去除p的倍数时,如果我们还是去除 p*2 p*3 p*4 p*(p-1) 那么岂不是与 我们去除2的倍数时 会去除 2*p 、去除3的倍数时 会去除 3*p、去除4的倍数时 会去除 4*p 重复了 ???
以上是关于求 0-N 内有多少个素数的主要内容,如果未能解决你的问题,请参考以下文章