学习生物信息需要熟练掌握一门甚至多门编程语言,比如Python、R、C++,这取决于你的需求。
但语言只是工具,最关键的是如何合理使用工具去解决生物学问题,这需要你去多写、多查、多看来锻炼。不然给你把屠龙刀,你不去修炼,它也只是块废铁。工具有多好不仅取决于自身,更在于使用的人。
为了领略生物信息学的意义、美好、乐趣,之后我会分享更多文章,敬请期待~
本文内容主要包括:质数和孪生质数的定义
质数分布的规律
两种实现方法,逐步优化~
为了更好的解决问题,我们先把“如何使用Python找质数”范围缩小一下,变成“如何使用Python找出100以内的素数”。
接下来,先明确概念,以便精确下手。
什么是质数?
质数( Prime number ):又称素数,指在大于1的自然数中,除了1和自身外,无法被其他自然数整除的数(也可定义为只有1和本身两个因素的数)。
与之相对的是合数,指自然数中除了能被1和本身整除外,还能被其他数(0除外)整除的数。
试除法
根据质数的定义,可以有一个直观的思路:将
除以每个大于
且小于等于
的整数,如果不能被整除,结果有余数,那么
就为质数。
这种方法也叫试除法。
#!/usr/bin/env python
# coding=utf-8
# file: is_prime.py
"""试除法,判断质数"""
import math
def is_prime(n):
"""Define whether the number is a prime number"""
if n <= 1:
return False # 质数要是大于1的自然数
for i in list(range(2, int(math.sqrt(n) + 1))): # 注意要开方后加1
if n % i == 0:
return False
break
return True
if __name__ == "__main__":
primes = [i for i in list(range(2,100)) if is_prime(i)] # range从2开始
print(primes)
最终得到 [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]。和搜索结果一致~1到100的质数
在Python中,一个.py文件就是一个模块。一般情况下,模块名就是文件名。
__name__ 存放当前模块名,当模块被直接运行时模块名为 __main__ 。
所以,if __name__ = ' __main__' 表示:当模块被直接运行时,该行以下的代码块将被运行;当模块是被导入(import)时,代码块不被运行。
试除法虽然简单,但是在测试大整数时很快会变得不切实际。因为可能的约数数量会随着n的增加而迅速增加。根据素数定理,小于
的素数之数量大约为
。也就是说,采用试除法判断
,要除4.5亿次,对许多实际应用而言都太过庞大。
那么,有没有改进的方法呢?接着看下去吧~
质数分布规律
首先看一个关于质数分布的规律:大于等于5的质数一定和6的倍数相邻,例如5和7,11和13,17和19。
用符号表示更容易理解:当n≥5时,如果n-1和n+1都为质数,那么n一定是6的倍数。
如何证明这个规律呢?
证明:设
且
为整数。
则大于等于
的自然数可表示如下:
即:
注意可以因式分解的式子。其中,
可以被
整除,而
,
,
分别可以写成
,
、
的形式,所以它们都一定不是质数。
剩下的就是
、
、
,代入几个数字试一下:
x = 1 时,为5,7,11
x = 2 时,为11,13,17
可以看到11被计算了两次。能不能减少计算,更好的表示大于等于5的质数呢?
实际上,可以用
表示
。为什么呢?因为
。
所以大于等于5的质数可以表示为:
,
。这两个数都在
身边不离不弃。
以上即证明了质数分布的规律:大于等于
的质数一定和
的倍数相邻。
我们来试试用Python实现这种思路,建议新手先自行尝试再看下面代码,更容易理解:
#!/usr/bin/env python
#coding:utf-8
# file: is_prime.py
"""质数分布规律,判断质数"""
def is_prime(n):
"""Define whether the number is a prime number"""
if n <= 1:
return False # 质数要是大于1的自然数
elif n == 2 or n == 3: # 小于5且大于1的数中,只有2和3是质数
return True
elif n % 6 != 1 and n % 6 !=5: # n = 6x+1 或 n = 6x+5 (6x-1), 质数一定在6倍数的两侧
return False
else:
return True
if __name__ == "__main__":
primes = [i for i in list(range(2,100)) if is_prime(i)] # range从2开始
print(primes)
结果是[2, 3, 5, 7, 11, 13, 17, 19, 23, 25, 29, 31, 35, 37, 41, 43, 47, 49, 53, 55, 59, 61, 65, 67, 71, 73, 77, 79, 83, 85, 89, 91, 95, 97]。
相比之前试除法得出的[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97],多了几个合数,如25,55,49。
抓抓脑壳,究竟出了什么差错?
事实上,虽然质数是出现在
或是
的数列中,但是满足这个条件的数却并不一定是质数。举个例子,6的4倍是24,它的两侧分别是23和25 ,但25不是质数。
问题来啦~为什么满足
或是
(
)的数不一定是质数?
如何从满足
或是
(
)的数中进一步筛出质数?
先着手第一个问题,我们“改造”一下这两个式子:
可以看到,如果
或
是5或7的倍数,那么
和
(
)也是5或是7的倍数,它俩就不是质数了。
所以满足
或是
(
)的数不一定是质数。
所以如果想解决第二个问题,就得在代码中增加判断 n 是否为5或7的倍数。注意,这里n要大于7,不然会把5和7漏掉,它俩也是质数。
#!/usr/bin/env python
#coding:utf-8
# file: is_prime.py
"""质数分布规律,判断质数"""
def is_prime(n):
"""Define whether the number is a prime numbern <= 1, false1 <= n <= 7, output 2,3,5,77 < n, first determine n % 6 == 1 or n % 6 == 5, than filter n % 5 == 0 and n % 7 == 0"""
if n <= 1:
return False
elif n == 2 or n == 3 or n == 5 or n == 7:
return True
elif n % 6 != 1 and n % 6 !=5:
return False
elif n % 5 !=0 and n % 7 !=0: # 改进的地方
return True
if __name__ == "__main__":
primes = [i for i in list(range(2,100)) if is_prime(i)] # range从2开始
print(primes)
返回的结果是[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97],和试除法的结果一致。
同样的思路可以换一种方式写代码,另外也可以进一步使用time模块比较两种的时间:
#!/usr/bin/env python
#coding:utf-8
"""质数分布规律,判断质数"""
from math import sqrt
import time # 比较代码运行时间
start = time.time()
def is_prime(n):
if n % 5 == 0 or n % 7 == 0: # 判断n能否被5或7整除
return False # 是,则为假
else:
return True
if __name__ == "__main__":
n = 1 #输入n值
if n > 7:
num = list(range(5, n+1, 6)) + list(range(7, n+1, 6))
primes = [x for x in num if is_prime(x)]+[2,3,5,7]
primes.sort()
elif n == 7:
primes = [2,3,5,7]
elif 5 <= n <= 6:
primes = [2,3,5]
elif 3 <= n <= 4:
primes = [2,3]
elif n == 2:
primes = [2]
else:
primes = ""
print("Prime number don't exist")
print(primes)
end = time.time()
print(str(end-start))
到这里就大功告成啦~
想理解算法时间复杂度的表示法,先要搞明白何谓“算法时间复杂度”。
如果你学过开车的话(没有的话,回忆一下初中物理),就会知道,挂一档时,可能发动机转十几圈,车轮才转一圈;但挂5档时,可能发动机转一圈,车轮就转了一圈。
换句话说,在不同挡位,发动机转一圈,车轮转多少是不一样的。
这个玩意儿背后,其实就是初中学过的轮轴原理。
作者:invalid s
最后补充下“孪生质数猜想”:孪生素数的问题已经有约200年的历史。
“孪生质数”是指两个相差为2的质数,所以上面我们说的
和
也互为孪生质数。
“孪生素数猜想”是说,存在无穷对孪生质数。在1900年的国际数学家大会上,希尔伯特将孪生质数猜想列入了他那著名的23个数学问题。
关于质数还有很多有趣的东西,比如梅森素数、数论。你可以自行了解,这里就不展开了。
现在,你学会了如何用Python找质数了吗?
参考: