4.6 素数函数

这里介绍一个素数函数的练习。

首先需要解决如何判断素数。素数也称质数,指在大于1的自然数中,除了1和它本身以外不再有其他因数的自然数。

那么具体的判断思路是什么?这是编程写代码的基础。比如对于7这个数,我们可以使用2到7之前的所有整数(即2到6),分别去除这个7,比如先使用2:

如果不能整除,就继续下一个3:

 还不能,就继续,直到6为止:

如果在全部的除法运算中,没有发生一次整除,就表明这个7就是素数。反之,有些数,可能就会发生被整数的情况,如8在除以2时发生整除,就表明8不是素数:

 按照这个思路,可以写出伪代码:

取出每一个数 i
    取出 2 到小于当前整数的每一个整数 j
        i 除以 j
        如果可以整除,说明 i 不是素数
    如果都没有整除,说明当前 i 是素数,并输出

这是第一份实现的代码:

for i in range(2, 101):
    isPrime = True
    for j in range(2, i):
        if i % j == 0:
            isPrime = False
    if isPrime:
        print(i)

输出为2到100之间所有素数。

这里定义了一个变量isPrime,它的主要目的是用于标记当前数是否为素数。为什么要定义?因为判断素数是在循环中来实现的,但是输出结果是在循环结束后才进行。因此,通过定义变量,就可以标记在循环中判断是否为素数,然后在循环后就可以根据这个变量得知刚才循环判断的结果。

对于每个需要判断素数的整数,isPrime初始值都是真,表示假设每个当前整数都是素数。一旦在从2到小于当前整数的除法运算中发生了整数,就表示当前整数不是素数,因此将isPrime再次设置为假。最后,到循环结束后,就根据这个isPrime是否为真,来决定是否输出。

这里最后的判断语句条件也可以写成下面的写法,意思一样:

for i in range(2, 101):
    isPrime = True
    for j in range(2, i):
        if i % j == 0:
            isPrime = False
    if isPrime == True:
        print(i)

也可以使用变量表示不是素数,效果一样:

for i in range(2, 101):
    isNotPrime = False
    for j in range(2, i):
        if i % j == 0:
            isNotPrime = True
    if not isNotPrime:
        print(i)

接下来,就可以进一步考虑执行效率的问题。还是看下一开始那个例子:

此时,对于8而言,一旦有一个数可以整除,说明8不是素数,其实也就没有必要再以3判断是否整除。

for i in range(2, 101):
    isPrime = True
    for j in range(2, i):
        if i % j == 0:
            isPrime = False
            break
    if isPrime:
        print(i)

这个代码的关键在于循环中的判断语句增加了break语句,一旦整除发生,即结束当前数和其他后续整数的整除判断。


接来下,我们增加下整除比较次数统计,看看这种写法究竟提高了多少效率?

这是增加了次数统计的原始版本:

count = 0
for i in range(2, 101):
    isPrime = True
    for j in range(2, i):
        count += 1
        if i % j == 0:
            isPrime = False
    if isPrime:
        print(i)
print(count)

输出的最后一行为:4851

这是增加了次数统计的改进版本:

count = 0
for i in range(2, 101):
    isPrime = True
    for j in range(2, i):
        count += 1
        if i % j == 0:
            isPrime = False
            break
    if isPrime:
        print(i)
print(count)

输出的最后一行为:1133。看得出,减少了3/4的不必要比较。

能不能再提高效率?比如是不是所有的数都要去判断是否为素数?偶数显然就没有必要去判断是否为素数。这里为了避免复杂,只考虑3及以后的整数。

for i in range(3, 101, 2):
    isPrime = True
    for j in range(2, i):
        if i % j == 0:
            isPrime = False
            break
    if isPrime:
        print(i)

代码的改进在于改变了循环取整数的方式,从3开始,每次步幅为2,依次取到100,这些正好是第一个循环中range函数参数的设定内容。

此时统计比较次数:

count = 0
for i in range(3, 101, 2):
    isPrime = True
    for j in range(2, i):
        count += 1
        if i % j == 0:
            isPrime = False
            break
    if isPrime:
        print(i)
print(count)

输出的最后一行为:1084

能不能再提高代码执行效率?是不是所有的数都要去除? 比如7:

如果2已经除过,其实就没有必要再去除以3、4、5和6,原因很简单。7的开方是2.65,和比开方小的整数进行除法操作得到的商一定是大于比开方大的数,因此,如果和比开方小的整数进行除法操作都没有整除,也必然意味着比开方大的整数进行除法操作也不会整除。

因此,可以在进行整除循环时,无需比较从2到小于当前数的所有整数,而只要比较从2到小于当前数开方的最大整数即可:

import math

for i in range(3, 101, 2):
    isPrime = True
    for j in range(2, int(math.sqrt(i)) + 1):
        if i % j == 0:
            isPrime = False
            break
    if isPrime:
        print(i)

此时统计比较次数:

import math

count = 0
for i in range(3, 101, 2):
    isPrime = True
    for j in range(2, int(math.sqrt(i)) + 1):
        count += 1
        if i % j == 0:
            isPrime = False
            break
    if isPrime:
        print(i)
print(count)

输出的最后一行为:187。这一次效率提升最为明显。


我们再次回到代码编写逻辑上来。标记变量isPrime是这个代码中非常重要的变量,但是可以不可以使用标记变量?也就是说,利用整除比较过程中的一些特征,即可观察出是否为素数,而不是非要通过设置一个标记变量,这可能吗?

我们以这个代码开始:

import math

for i in range(3, 101, 2):
    for j in range(2, int(math.sqrt(i)) + 1):
        if i % j == 0:
            break
    if isPrime:
        print(i)

可以看出,如果是素数,整除在整除循环还没结束时就已经发生,并且会提前终止当前的整除循环,这会导致 j 变量的取值无法达到所在循环的下限!反之,如果是素数,j 变量值一定可以达到循环的下限。因此,不妨就利用这个特征:

for i in range(3, 101, 2):
    for j in range(2, i):
        if i % j == 0:
            break
    if j == i - 1:
        print(i)

输出效果一致。但是这里取消了标记变量,就是利用 j 的取值是否达到所在整除循环的下限,如果达到了,则没有发生过整除,则就是素数!

同时,巧妙的利用Python语言的特点也可以实现一种:

for i in range(3, 101, 2):
    for j in range(2, i):
        if i % j == 0:
            break
    else:
        print(i)

输出效果一致。循环的else语句只要在循环全部遍历完后才会被执行,一旦有break提前结束,则不会执行。这个效果正好等价于如果是素数(循环全部遍历完)才会被执行(即输出),一旦不是素数(有整除,导致break提前结束)就不执行。


最后,到了封装函数的时候了。我们希望这个函数能够这样使用:

print(isPrime(7))

输出为:True。

整体思路如:

 最终得到函数:

def isPrime(num):
    for j in range(2, num):
        if num % j == 0:
            break
    else:
        return True
    return False

可以看出,函数的定义,关键在于代码逻辑的实现,实现好后,只需将被处理的数据换成形参,加上函数头,输出换成返回值,即可完成函数的定义。

配套学习资源、慕课视频:

Python大数据分析-李树青 (njcie.com)icon-default.png?t=M1H3http://www.njcie.com/python/

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

leeshuqing

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值