质素
质数又称素数。一个大于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 # 方法三