质素

质数又称素数。一个大于1的自然数,除了1和它自身外,不能被其他自然数整除的数叫做质数;否则称为合数。

性质

  • 如果 为合数,因为任何一个合数都可以分解为几个素数的积

合数

合数指自然数中除了能被1和本身整除外,还能被其他数(0除外)整除的数。

性质

  • 所有大于2的偶数都是合数。
  • 所有大于5的奇数中,个位为5的都是合数。

根据定义判断一个数是不是质素

x = int(input('>>>:'))
for i in range(2, x):
    if x % i == 0:
        print(f'{x}是质素')
        break
else:
    print(f'{x}不是质素')

求10以内所有的质数

x = 10
l = []
for i in range(2, x):
    for j in range(2, i):
        if i % j == 0:
            break
    else:
        l.append(i)
print(l)
[2, 3, 5, 7]

计算10万内所有的质数

import datetime
start = datetime.datetime.now()

x = 100000
count = 0
for i in range(2, x):
    for j in range(2, i):
        if i % j == 0:
            break
    else:
        count += 1
delay = (datetime.datetime.now() - start ).total_seconds()
print(count, delay)
9592 38.573707

耗时太长,效率太低,需要优化,根据定义以及特性优化

import datetime
start = datetime.datetime.now()

x = 100000
count = 1 # 2是最小的质数;除2外所有的正偶数均为合数;2可以理解成特例,初始计数为 1
for i in range(3, x, 2):  # 取所有奇数,所有大于2的偶数都是合数。
    if i > 5 and i % 5 == 0:
        continue       # 所有大于5的奇数中,个位为5的都是合数。
    for j in range(3, i, 2):  # 取所有奇数,排除所有偶数
        if i % j == 0:
            break
    else:
        count += 1
delay = (datetime.datetime.now() - start ).total_seconds()
print(count, delay)
9592 22.093344

对比上次的时间,有所改善;

假如一个数N是合数,它有一个约数a,a×b=N
则a、b两个数中必有一个大于或等于根号N,一个小于或等于根号N。
因此,只要小于或等于根号N的数(1除外)不能整除N,则N一定是素数。

进一步对算法进行优化,修改j in range(3, i, 2)的范围,不需要从3 一直遍历到i,只要到 i ** 0.5 即可

import datetime
start = datetime.datetime.now()

x = 100000
count = 1 # 2是最小的质数;除2外所有的正偶数均为合数;2可以理解成特例,初始计数为 1
for i in range(3, x, 2):  # 取所有奇数,所有大于2的偶数都是合数。
    if i > 5 and i % 5 == 0:
        continue       # 所有大于5的奇数中,个位为5的都是合数。
    for j in range(3, int(i ** 0.5) + 1, 2):  # 取所有奇数,排除所有偶数
        if i % j == 0:
            break
    else:
        count += 1
delay = (datetime.datetime.now() - start ).total_seconds()
print(count, delay)
9592 0.124274

可以看到质的飞跃,直接缩短到毫秒级.


以上是根据质素的定义来计算然后根据特性优化方法.为了对比定义为:方法一

  • 因为任何一个合数都可以分解为几个素数的积
    利用这个特性,可以先定义一个列表存放已知的素数,然后用一个数字取递归取模计数

方法二:

import datetime
start = datetime.datetime.now()
number_list = [] # 列表中为空,2 是偶数为特殊值
x = 100000
count = 1 # 2是最小的质数;除2外所有的正偶数均为合数;2可以理解成特例,初始计数为 1
for i in range(3, x, 2):  # 取所有奇数,所有大于2的偶数都是合数。
    for j in number_list:  # 从质素列表中取值
        if i % j == 0:
            break
    else:
        count += 1
        number_list.append(i)
delay = (datetime.datetime.now() - start ).total_seconds()
print(count, delay)
9592 3.667497

发现效率便不高,计算时间反而大了,通过观察发现还是可以优化的.

假如一个数N是合数,它有一个约数a,a×b=N
则a、b两个数中必有一个大于或等于根号N,一个小于或等于根号N。
因此,只要小于或等于根号N的数(1除外)不能整除N,则N一定是素数。

还是根据这个特点,来优化j 的取值范围, j 大于 i ** 0.5 就不要再进行遍历计算了,只需要计算小于的一半即可

import datetime
start = datetime.datetime.now()
number_list = [] # 列表中为空,2 是偶数为特殊值
x = 100000
count = 1 # 2是最小的质数;除2外所有的正偶数均为合数;2可以理解成特例,初始计数为 1
for i in range(3, x, 2):  # 取所有奇数,所有大于2的偶数都是合数。
    if i > 5 and i % 5 == 0:
        continue       # 所有大于5的奇数中,个位为5的都是合数。
    for j in number_list:  # 从质素列表中取值
        if j <= int(i ** 0.5) and i % j == 0:  # 1.只需要计算小于开方数的一半,通过测试发现效率更低
            break
    else:
        count += 1
        number_list.append(i)
delay = (datetime.datetime.now() - start ).total_seconds()
print(count, delay)
9592 10.451074

效率反而降低了?这是为什么?

  • 观察代码可以发现: j <= int(i ** 0.5) 每次for 循坏都需要if 计算判断一次,
  • 通过调优,把 j <= int(i ** 0.5)拿到for j 外面
import datetime
start = datetime.datetime.now()
number_list = [] # 列表中为空,2 是偶数为特殊值
x = 100000
count = 1 # 2是最小的质数;除2外所有的正偶数均为合数;2可以理解成特例,初始计数为 1
for i in range(3, x, 2):  # 取所有奇数,所有大于2的偶数都是合数。
    if i > 5 and i % 5 == 0:
        continue       # 所有大于5的奇数中,个位为5的都是合数。
    k = int(i ** 0.5)  # 2.重复计算放到外面

    for j in number_list:  # 从质素列表中取值
        if j <= k and i % j == 0:  # 1.只需要计算小于开方数的一半,通过测试发现效率更低
            break   # 合数退出
    else:
        count += 1
        number_list.append(i)
delay = (datetime.datetime.now() - start ).total_seconds()
print(count, delay)
9592 2.979975

效率还是不理想

import datetime
start = datetime.datetime.now()
number_list = [] # 列表中为空,2 是偶数为特殊值
x = 100000
count = 1 # 2是最小的质数;除2外所有的正偶数均为合数;2可以理解成特例,初始计数为 1
for i in range(3, x, 2):  # 取所有奇数,所有大于2的偶数都是合数。
    if i > 5 and i % 5 == 0:
        continue       # 所有大于5的奇数中,个位为5的都是合数。
    k = int(i ** 0.5)  # 2.重复计算放到外面
    flag = False
    for j in number_list:  # 从质素列表中取值
        if j > k:      # 3. if j <= k and i % j == 0:  隐含条件是大于k肯定是 质数
            flag = True
            break  # 质数退出

        if i % j == 0:  # 1.只需要计算小于开方数的一半,通过测试发现效率更低
            break   # 合数退出
    if flag:
        count += 1
        number_list.append(i)
delay = (datetime.datetime.now() - start ).total_seconds()
print(count, delay)
1 0.024039

通过 1 2 3 的优化,发现计算结果出了问题,分析发现初始列表为空导致的,考虑初始化列表值为[3]因为2 的倍数为偶数,浪费计算成本,

number_list = [ 3 ]
count = 2 # 隐含包括 2,3
for i in range(5, x, 2): # 所以从下一个质数开始
import datetime
start = datetime.datetime.now()
number_list = [3] # 列表中为空,2 是偶数为特殊值
x = 100000
count = 2 # 2 ,3
for i in range(5, x, 2):  # 取所有奇数,所有大于2的偶数都是合数。
    if i > 5 and i % 5 == 0:
        continue       # 所有大于5的奇数中,个位为5的都是合数。
    k = int(i ** 0.5)  # 2.重复计算放到外面
    flag = False
    for j in number_list:  # 从质素列表中取值
        if j > k:      # 3. if j <= k and i % j == 0:  隐含条件是大于k肯定是 质数
            flag = True
            break  # 质数退出

        if i % j == 0:  # 1.只需要计算小于开方数的一半,通过测试发现效率更低
            break   # 合数退出
    if flag:
        count += 1
        number_list.append(i)
delay = (datetime.datetime.now() - start ).total_seconds()
print(count, delay)
9592 0.139435   # 方法一
9592 0.086791   # 方法二 更优

方法三:孪生质数

大于3的质素只有 6N-1 或 6N+1 两种形式,如果6N-1 等于 6N+1 的素数称为孪生质数

3

5

7

11

13

17

19

3

6-1

6+1

6*2-1

6*2+1

6*3-1

6*3+1

3

5

5+2

7+4

11+2

13+4

17+2

import datetime
start = datetime.datetime.now()
number_list = [3, 5] # 列表中为空,2 是偶数为特殊值
x = 100000
count = 3 # 包含 2 3 5
i = 7
step = 4
while i < x:
    if i % 5 != 0:

        k = int(i ** 0.5)  # 2.重复计算放到外面
        flag = False
        for j in number_list:  # 从质素列表中取值
            if j > k:      # 3. if j <= k and i % j == 0:  隐含条件是大于k肯定是 质数
                flag = True
                break  # 质数退出

            if i % j == 0:  # 1.只需要计算小于开方数的一半,通过测试发现效率更低
                break   # 合数退出
        if flag:
            count += 1
            number_list.append(i)
    i += step
    step = 4 if step == 2 else 2

delay = (datetime.datetime.now() - start ).total_seconds()
print(count, delay)
9592 0.12006  # 方法一
9592 0.08318  # 方法二
9592 0.079766	# 方法三