这可能是Python中最快的质数计算方案:
穷举2亿内质数只要1秒!
穷举10亿内质数只要4.4秒!
这个速度可能比不了C,
但是对于Python可以说是非常快了!
筛选法简直无敌
从筛选法单线程实现,单线程结果保存,
到多进程实现,结果分段保存,自动合并
再到边计算边合并, 最后整理代模块化
一下搞了将近20个小时…
上结果!
穷举2亿内质数 ↓
穷举10亿内质数↓
以下是代码:
# -*- coding = utf-8 -*-
# @Time : 2020/9/8 20:43
# @Author : Suage
# @File : 找质数_火力全开.py
import time
import concurrent.futures
import numpy
class FindPrimeNumber:
def __init__(self, block_size, core_num):
self.block_size = block_size
self.core_num = core_num
self.upper = None
def find_prime(self, upper):
prime_list = []
mid = int(numpy.sqrt(upper))
nums = numpy.arange(upper)
nums[1] = 0
while True:
primes = nums[nums > 0]
if primes.any():
p = primes[0]
prime_list.append(p)
nums[p::p] = 0
if p > mid:
break
else:
break
prime_list.extend(nums[nums > 0].tolist())
return prime_list
def fast_find_prime(self, upper, base=None):
self.upper = upper
if base is None:
base = int(numpy.sqrt(self.upper)) + 1
if self.upper <= base:
return self.find_prime(self.upper)
self.prime_list = self.find_prime(base)
self.base_prime_array = numpy.array(self.prime_list)
print('Create task list', end='...')
task_base_prime_array_list = []
task_range_list = []
start = base
while start < self.upper:
end = start + self.block_size
if end > self.upper:
end = self.upper
mid = numpy.sqrt(end)
base = self.base_prime_array[self.base_prime_array <= mid]
task_base_prime_array_list.append(base)
task_range_list.append((start, end))
start += self.block_size
print('OK!')
with concurrent.futures.ProcessPoolExecutor(max_workers=self.core_num) as executor:
returns = executor.map(self.process_func,
zip(task_base_prime_array_list, task_range_list))
print('合并计算结果...')
for r in returns:
self.prime_list.extend(r)
def process_func(self, params):
primes, task_range = params[0], params[1]
nums = numpy.arange(task_range[0], task_range[1])
for p in primes:
k = (p - task_range[0] % p) % p
nums[k::p] = 0
print(
f'{task_range[1]:0=15d} be checked! Progress:{task_range[1] / self.upper:5.2%}')
return nums[nums > 0].tolist()
if __name__ == '__main__':
# 以下两项根据自己的配置修改
block_size = 500000 # i9-9900k(500000)
core_num = 8 # 处理器物理核心数(非线程数) i9-9900k(8)
upper = int(input('计算终点:')) + 1
# upper = 100000000 + 1
t0 = time.time()
fpn = FindPrimeNumber(block_size=block_size,
core_num=core_num)
fpn.fast_find_prime(upper)
print('*' * 50)
print('OK!')
print(f'{upper - 1}以内的质数计算耗时{time.time() - t0:0.2f}秒')
print(f'计{len(fpn.prime_list)}个')
结果保存至文件的代码:
# -*- coding = utf-8 -*-
# @Time : 2020/9/8 20:43
# @Author : Suage
# @File : 找质数_火力全开.py
from shutil import rmtree
from os import rename, listdir, remove, path, makedirs
import re
import threading
import time
import concurrent.futures
import numpy
class MergeFile(threading.Thread):
def set_show_merge_progress_to_true(self):
self.show_merge_progress = True
def run(self):
self.show_merge_progress = False
result_prime_nums_file = open(f'{save_path}/PrimeNum.txt', 'w')
re_rules = '_(\d{15})~(\d{15})_'
next_file_start_index = 2
can_not_find_next_file = False
while True:
if can_not_find_next_file:
time.sleep(0.1)
can_not_find_next_file = True
for f in listdir(save_path):
if f'PrimeNum_{next_file_start_index:0=15d}~' in f:
find_file_name = f
file_name_research = re.search(re_rules, find_file_name)
if self.show_merge_progress:
print(
f'Merge progress:'
f'{int(file_name_research.group(1)) / upper:5.2%}')
next_file_start_index = int(file_name_research.group(2)) + 1
temp_file = open(f'{save_path}/{find_file_name}', 'r')
result_prime_nums_file.write(temp_file.read())
temp_file.close()
remove(f'{save_path}/{find_file_name}')
can_not_find_next_file = False
if next_file_start_index == 1:
result_prime_nums_file.close()
return
break
class FindPrimeNumber:
def __init__(self, save_path, block_size, core_num):
self.save_path = save_path
self.block_size = block_size
self.core_num = core_num
self.upper = None
def find_prime(self, upper):
prime_list = []
mid = int(numpy.sqrt(upper))
nums = numpy.arange(upper)
nums[1] = 0
while True:
primes = nums[nums > 0]
if primes.any():
p = primes[0]
prime_list.append(p)
nums[p::p] = 0
if p > mid:
break
else:
break
prime_list.extend(nums[nums > 0].tolist())
return prime_list
def fast_find_prime(self, upper, base=None):
self.upper = upper
if base is None:
base = int(numpy.sqrt(self.upper)) + 1
if self.upper <= base:
return self.find_prime(self.upper)
self.prime_list = self.find_prime(base)
with open(f'{self.save_path}/PrimeNum_{2:0=15d}~{base - 1:0=15d}_preparation.pn',
'w',
encoding='utf-8') as file:
file.write(
str(self.prime_list)[1:-1].replace(' ', '')
)
file.write(',')
file.close()
rename(f'{self.save_path}/PrimeNum_{2:0=15d}~{base - 1:0=15d}_preparation.pn',
f'{self.save_path}/PrimeNum_{2:0=15d}~{base - 1:0=15d}_complete.pn')
self.base_prime_array = numpy.array(self.prime_list)
print('Create task list', end='...')
task_base_prime_array_list = []
task_range_list = []
start = base
while start < self.upper:
end = start + self.block_size
if end > self.upper:
end = self.upper
mid = numpy.sqrt(end)
base = self.base_prime_array[self.base_prime_array <= mid]
task_base_prime_array_list.append(base)
task_range_list.append((start, end))
start += self.block_size
print('OK!')
with concurrent.futures.ProcessPoolExecutor(max_workers=self.core_num) as executor:
executor.map(self.process_func,
zip(task_base_prime_array_list, task_range_list))
with open(f'{self.save_path}/PrimeNum_{self.upper:0=15d}~'
f'{0:0=15d}_complete.pn', 'w') as end_mark:
end_mark.close()
def process_func(self, params):
primes, task_range = params[0], params[1]
nums = numpy.arange(task_range[0], task_range[1])
for p in primes:
k = (p - task_range[0] % p) % p
nums[k::p] = 0
with open(f'{self.save_path}/PrimeNum_{task_range[0]:0=15d}~'
f'{task_range[1] - 1:0=15d}_preparation.pn',
'w',
encoding='utf-8') as file:
file.write(
str(nums[nums > 0].tolist())[1:-1].replace(' ', '')
)
file.write(',')
file.close()
rename(f'{self.save_path}/PrimeNum_{task_range[0]:0=15d}~'
f'{task_range[1] - 1:0=15d}_preparation.pn',
f'{self.save_path}/PrimeNum_{task_range[0]:0=15d}~{task_range[1] - 1:0=15d}'
f'_complete.pn')
print(
f'{task_range[1]:0=15d} be checked! Progress:{task_range[1] / self.upper:5.2%}')
if __name__ == '__main__':
# 用于保存计算结果(最后不要斜杠)
save_path = './prime_out' # 此目录会被清空,请注意!
# 以下两项根据自己的配置修改
block_size = 500000 # i9-9900k(500000)
core_num = 8 # 处理器物理核心数(非线程数) i9-9900k(8)
if path.exists(save_path):
rmtree(save_path)
makedirs(save_path)
mf = MergeFile()
upper = int(input('计算终点:')) + 1
# upper = 100000000 + 1
t0 = time.time()
mf.start()
fpn = FindPrimeNumber(save_path=save_path,
block_size=block_size,
core_num=core_num)
fpn.fast_find_prime(upper)
print('*' * 50)
mf.set_show_merge_progress_to_true()
mf.join()
print('*' * 50)
print('OK!')
print(f'{upper - 1}以内的质数计算耗时{time.time() - t0:0.2f}秒')
感谢一位大佬, 学来了筛选法, 想贴出来的, 但是代码写完了之前看的页面找不到了…