int cnt = 0, pr[N]; for (int i = 2; i <= n; i ++) { k = true; // 标记为素数 for (int j = 2; j <= n - 1; j ++) { if (i % j == 0) { k = false; break; } } if (k == true) pr[++ cnt] = i; }
其实我们不用全部遍历,因为如果 是 的除它之外最大的因子,那么 ,如果前面没有,后面就不用扫了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
int cnt = 0, pr[N], m; for (int i = 2; i <= n; i ++) { k = true; // 标记为素数 for (int j = 2; j * j <= n; j ++) { if (i % j == 0) { k = false; break; } } if (k == true) pr[++ cnt] = i; }
现在的时间复杂度是 ,非常糟糕,那么肯定会有人不能接受,然后大呼:“我不能接受!”。
所以我们有了接下来的素数筛法,正是因为这些发现它们的数学家不能接受这样糟糕的复杂度。
埃拉托色尼筛
这是一种比小朋友大一点的中朋友都可以想到的筛法,相信你也可以。
我们把 的数按顺序写出来:
从前往后看,找到第一个未被划掉的数 ,这说明它是质数。然后把 的倍数(不包括 )划掉:
下一个未被划掉的数是 ,它是质数把 的倍数划掉:
接下来是 ,但是 已经超过 了,所以遍历结束,剩下未被划掉的都是素数。
这个过程写成代码就是:
1 2 3 4 5 6 7 8
bool isnp[N]; voidSieve(int n) { for (int i = 2; i * i <= n; i ++) if (!isnp[i]) // 未被划去 for (int j = i * i; j <= n; j += i) // 因为之前的肯定都被较小数划掉了,所以直接从 i * i 开始 isnp[j] = 1; // 标记为不是素数 }
bool isnp[N]; vector<int> primes; voidEular_Sieve(int n) { for (int i = 2; i <= n; i ++) { if (!isnp[i]) primes.push_back (i); for (int p : primes) { if (p * i > n) break; isnp[p * i] = 1; if (i % p == 0) break; } } }
当然也可以改一种写法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
voidEular_Sieve(int n) { for (int i = 2; i <= n; i ++) { if (!isnp[i]) primes[++ cnt] = i, sum ++; for (int j = 1; j <= sum && j * primes[j] <= n; j ++) { isnp[primes[j] * i] = true; if (i % primes[j] == 0) break; } } }