从2开始,将每个素数的各个倍数(一倍除外),标记成合数。一个素数的各个倍数,是一个差为此素数本身的等差数列。此为这个筛法和试除法不同的关键之处,后者是以素数来测试每个待测数能否被整除。埃拉托斯特尼筛法(英语:sieve of Eratosthenes ),简称埃氏筛,也称素数筛。这是一种简单且历史悠久的筛法,用来找出一定范围内所有的素数。埃拉托斯特尼筛法是列出所有小素数最有效的方法之一。
每个整数 n >= 2 可唯一分解成素数乘积 n = p1p2…pr.
一种解决办法是约定每次只在 pmin 处,对该合数 n 做筛除。
#include <stdio.h> #include <string.h> #define MAX (int)1e2 #define ll long long ll prime[MAX + 1], cnt; bool isPrime[MAX + 1]; void init_prime() { cnt = 1; memset(isPrime, 1, sizeof(isPrime)); isPrime[0] = isPrime[1] = 0; for (ll i = 2; i <= MAX; i++) { if (isPrime[i]) prime[cnt++] = i; //位置1 for (ll j = 1; j < cnt && i * prime[j] <= MAX; j++) { isPrime[i * prime[j]] = 0; //位置2 if (!(i % prime[j])) break; //位置3 } } } int main() { init_prime(); for (ll i = 1; i < cnt; i++) printf("%lld%s", prime[i], i == cnt - 1 ? "\n" : " "); return 0; } 这个算法可以保证每个数字都只被标记一遍,实现方法如下:对于所有大于1的数,分为素数和合数,而合数又分为:
一个素数乘以一个素数一个素数乘以一个合数在位置 1,得到了所有素数,在位置 2 标记了所以合数。 在位置 2 的时候,又分为两种可能:
i 是素数,prime[j] 是素数i 是合数,prime[j] 是素数如上解释了为何可以标记合数
那么是如何不重复标记合数的呢? 对于 n = pmin * c, 更具最小约定应该有 c >= pmin,反过来即是每次拎一个数 c’,只需要到小于等于它的素数范围做乘法筛除即可。可是我们发现,依然会出现 34 = 26 这样的重复筛除,该如何避免这样的重复呢?依靠位置 3。
对于 i 是素数的情况,这个和下一个素数 prime[j + 1] 继续标记肯定不会重复;
对于 i 是合数的情况,如果这个合数分解质因数后:
最小的质因数等于 prime[j], 则若继续与下一个素数 prime[j + 1]相乘标记合数,就违反了不重复的约定-----因为对于由 i * prime[j + 1] 标记的合数,显然有更小的素数 prime[j] 去做乘积来标记。所以这种情况就应该停止迭代。
最小质因数大于 prime[j],则就可以继续与下一个素数相乘继续标记,下一轮迭代不会出现重复标记的情况。