问题
素数(质数)是除了1和它自身没有其他数能够整除的正整数,最小的素数是2。而不符合该特性的正整数是合数,常见的素数有
2,3,5,7,9,11,13,17,19,23… 素数是数论学科中的基础概念,关于素数的最为著名的问题就是哥德巴赫猜想。判断[2…n−1]中哪些是素数,哪些是合数。
简单筛法
按照素数的定义,判断一个正整数n是否为素数,需要满足[1,n]范围内的所有正整数除了1,n没有其他能够被n整除的整数。
即遍历[2,n−1]所有数字i,判断其是否能被n整除(n/)即可。
用该算法判断一个正整数是否为素数的时间复杂度为O(n),判断范围[2,n−1]内的整数是否为素数的时间复杂度为O(n2)。
埃拉托斯特尼筛法/埃氏筛法(Sieve of Eratosthenes)
埃式筛法设置数组s=[2…n−1],s[i]是一个布尔值,表示数字i是否为素数。
(1) 以2为筛子,除了2本身所有2的倍数都不是素数,即s[2]=true,s[2×2]=false,s[2×3]=false,…;
(2) 以3为筛子,除了3本身所有3的倍数都不是素数,即s[3]=true,s[3×2]=false,s[3×3]=false,…;
(3) 以5为筛子,除了5本身所有5的倍数都不是素数,即s[5]=true,s[5×2]=false,s[5×3]=false,…;
称这样作为遍历起点的数字为筛子。
对筛法进行两点优化:
(1) 若x不是素数,那么存在a×b=x,显然a,b不能同时大于x。因此筛子只需要选择[2,⌊n−1⌋],即可判断[2,n−1]范围内的所有数字是否为素数。
(2) 筛子x遍历倍数时[2⋅x,3⋅x,…,x⋅x,…,n−1],其中之前的筛子2已经筛选了2⋅x,筛子3已经筛选了3⋅x,…,筛子x−1已经筛选了(x−1)⋅x。可以跳过这部分来节省时间。因此那么对于筛子x只需要遍历[x2,x2+x,…,n−1]这部分即可。
由于存在某些素数被重复筛选,埃氏筛法的时间复杂度为O(n⋅log2(log2n))(证明略)。
素数筛法
源码
PrimeSieve.h
PrimeSieve.cpp
测试
PrimeSieveTest.cpp