PrimeSieve 素数筛法

问题

素数(质数)是除了11和它自身没有其他数能够整除的正整数,最小的素数是22。而不符合该特性的正整数是合数,常见的素数有

2,3,5,7,9,11,13,17,19,232, 3, 5, 7, 9, 11, 13, 17, 19, 23 \dots

素数是数论学科中的基础概念,关于素数的最为著名的问题就是哥德巴赫猜想。判断[2n1][2 \dots n-1]中哪些是素数,哪些是合数。

简单筛法

按照素数的定义,判断一个正整数nn是否为素数,需要满足[1,n][1, n]范围内的所有正整数除了1,n1, n没有其他能够被nn整除的整数。

即遍历[2,n1][2, n-1]所有数字ii,判断其是否能被nn整除(n/n /% i = 0)即可。

用该算法判断一个正整数是否为素数的时间复杂度为O(n)O(n),判断范围[2,n1][2, n-1]内的整数是否为素数的时间复杂度为O(n2)O(n^2)

埃拉托斯特尼筛法/埃氏筛法(Sieve of Eratosthenes)

埃式筛法设置数组s=[2n1]s = [2 \dots n-1]s[i]s[i]是一个布尔值,表示数字ii是否为素数。

(1)(1)22为筛子,除了22本身所有22的倍数都不是素数,即s[2]=true,s[2×2]=false,s[2×3]=false,s[2] = true, s[2 \times 2] = false, s[2 \times 3] = false, \dots

(2)(2)33为筛子,除了33本身所有33的倍数都不是素数,即s[3]=true,s[3×2]=false,s[3×3]=false,s[3] = true, s[3 \times 2] = false, s[3 \times 3] = false, \dots

(3)(3)55为筛子,除了55本身所有55的倍数都不是素数,即s[5]=true,s[5×2]=false,s[5×3]=false,s[5] = true, s[5 \times 2] = false, s[5 \times 3] = false, \dots

\cdots

称这样作为遍历起点的数字为筛子。

对筛法进行两点优化:

(1)(1)xx不是素数,那么存在a×b=xa \times b = x,显然a,ba, b不能同时大于x\sqrt{x}。因此筛子只需要选择[2,n1][2, \lfloor \sqrt{n-1} \rfloor ],即可判断[2,n1][2, n-1]范围内的所有数字是否为素数。

(2)(2) 筛子xx遍历倍数时[2x,3x,,xx,,n1][2 \cdot x, 3 \cdot x, \dots, x \cdot x, \dots, n-1],其中之前的筛子22已经筛选了2x2 \cdot x,筛子33已经筛选了3x3 \cdot x\dots,筛子x1x - 1已经筛选了(x1)x(x-1) \cdot x。可以跳过这部分来节省时间。因此那么对于筛子xx只需要遍历[x2,x2+x,,n1][x^2, x^2 + x, \dots, n-1]这部分即可。

由于存在某些素数被重复筛选,埃氏筛法的时间复杂度为O(nlog2(log2n))O(n \cdot log_2 (log_2 n))(证明略)。

素数筛法

源码

PrimeSieve.h

PrimeSieve.cpp

测试

PrimeSieveTest.cpp

Last updated