Python全栈[第十篇]:素数(质数)高效算法

质素

质数又称素数。一个大于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 的素数称为孪生质数

35711131719
36-16+16*2-16*2+16*3-16*3+1
355+27+411+213+417+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	# 方法三 
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值