质数
质数:也被称为素数,在大于1的自然数,除了1和它本身以外不在有其他因子的自然数。即:质数x因子只有1和x, 2到x - 1中均不是因子。
def in_prime(x):
if x <= 1:
return False
for i in range(2, x):
if x % i == 0:
return False
return True
print(in_prime(3))
对上面的代码进行优化:
若x = pq, 记p <= q, 则p <= x ** 0.5 <= q
先前:从[2, x - 1]中判断是否存在因子, 如果存在则不是质数。
现在:从[2, x ** 0.5]中判断是否存在因子, 如果存在则不是质数,如果不存在则是质数。
因为如果有因子, 则一定会出现在前半部分, 如果前半部分没有则后半部分也没有。
def in_prime(x):
if x <= 1:
return False
m = int(x ** 0.5)
for i in range(2, m + 1):
if x % i == 0:
return False
return True
print(in_prime(97))
如何快速的筛选出2 - n 中的所有质数。
埃氏筛
算法描述:
埃氏筛,即埃拉托斯特尼筛法,是一种由希腊数学家埃拉托斯特尼所提出的简单检定素数的算法。它的基本思想是从2开始,将每个素数的倍数都标记为合数,直到遍历完所有小于给定数的自然数。这样,剩下的未被标记的数就是素数。
具体步骤如下:
- 列出2以后的所有序列数。
- 标出序列中的第一个素数,即2,然后将序列中2的倍数全部划掉。
- 如果这个序列中最大数小于最后一个标出的素数的平方,那么剩下的序列中所有的数都是素数,否则继续下一步。
- 回到第二步,将序列中下一个素数的倍数全部划掉。
- 不断重复上述步骤,直到遍历完所有小于给定数的自然数。
这种方法效率较高,适用于求解大范围内的素数,其应用范围广泛,可以用于解决各种与素数相关的问题。
基本方法:每次找到一个质数,将其倍数全部删除。
代码实现
def get_prime(n):
# 求出n以内的所以质数
# vis:标记数组,标记的表示被删除的
vis = [0] * (n + 1)
vis[0] = vis[1] = 1
# li:用来存放质数
li = []
for i in range(2, n + 1):
if vis[i] == 0:
li.append(i)
for j in range(i * i, n + 1, i):
vis[j] = 1
return vis, li
埃氏筛法的时间复杂度是0(n*log(logn))。
欧拉筛,线性筛
线性筛:相比如埃氏筛时间上会更加的快,线性筛的思想主要来源于对埃氏筛法的改进。在埃氏筛法中,一些含有多个质因子的数可能会被重复筛掉,例如30(=235)会被2、3、5都筛一遍,这显然是不必要的。
线性筛法正是从这个地方着手改进,它的核心思想是在筛的过程中,每个合数都只被它的最小质因子筛去。这样就能保证每个合数都只被筛一次,复杂度为O(n)。具体来说,线性筛法使用一个长度为n的bool数组,刚开始所有元素都未被标记。然后从2开始,对于每个数i,如果i是质数或者i的最小质因子大于当前遍历的质数,则将当前质数标记为i的最小质因子;否则停止遍历,因为i的倍数已经被更小的质因子筛去了。
线性筛法通常用于快速处理大范围内质数筛选的场景,例如求小于或等于某个数n的所有质数,并对每个数的质因数分解有快速的处理能力。此外,通过对线性筛法进行扩展,还可以计算出几乎所有的积性函数。
算法模版
def get_primes(n):
# 筛选出1 - n只内的素数
# vis:标记数组
vis = [0] * (n + 1)
vis[0] = vis[1] = 1
# 用来存放素数
li = []
for i in range(2, n + 1):
if vis[i] == 0:
li.append(i)
for x in li:
if x * i > n:
break
vis[x * i] = 1
if i % x == 0:
break
return li
n = int(input())
print(get_primes(n))
线性筛法的时间复杂度是0(n)。