质数又称素数。一个大于1的自然数,除了1和它自身外,不能被其他自然数整除的数叫做质数;否则称为合数(规定1既不是质数也不是合数)。
来源: https://baike.baidu.com/item/质数
最近几种语言交叉的学习,觉得有些混乱,所以有想法用质数算法来将他们比较一下,也顺便粗略比较一下几种语言在我的电脑上的运算速度。
问题
问题: 在从小到大排列的质数序列中,计算前若干个质数的和。
下面就用Python,C,Go,BASIC四种语言分别对这个问题求解。
Python
Python这种胶水语言,写起来太方便了。
完全按照定义的算法
首先使用基本上不用动脑子的算法来求解。(完全按照质数的定义来)
import time
def is_prime(num):
n = 2 # 约数从2开始算起
while n < num:
if num % n == 0: # 如果可以整除
return False
else:
n += 1
return True # 如果找不到约数
def sum_prime(count):
sum = 0
number = 2
cnt = 0
while cnt < count: # 当算到质数的数量为count时停止循环
if is_prime(number):
sum += number
cnt += 1
number += 1
return sum
start_time = time.time()
print(sum_prime(10000))
print("{:.3f} seconds".format(time.time() - start_time))
上面的代码计算了前10000
个质数的和,结果是496165411
,用了54.327
秒。
稍微优化一下
思考了一下:
- 大于2的质数,都不会是偶数(因为偶数可以被2整除),一定是奇数
- 既然大于2的质数,都是奇数,那么只验证大于2的奇数是否为质数即可
- 那么,验证时,也只需要验证是否能被奇数整除即可,因为奇数一定不能被偶数整除
- 验证某个数 n 是否为质数时,查找他的因数时,只需要查找
3
到sqrt(n)
(即n的开平方√n)的所有奇数即可
- 考虑开始查找因数前,先判断
3
是否为因数,不知道是否能节省时间
按上面想法优化
import time
import math
def is_prime(num):
num_2 = int(math.sqrt(num))
n = 5
while n <= num_2:
if num % n == 0:
return False
n += 2
return True
def sum_prime(count):
sum = 5 # 2 + 3
number = 5 # 从第三个质数5开始
cnt = 2 # 已经计算了前两个质数2和3的和
while cnt < count:
if number % 3 != 0 and is_prime(number):
sum += number
cnt += 1
number += 2
return sum
start_time = time.time()
print(sum_prime(100000))
print("{:.3f} seconds".format(time.time() - start_time))
上面的代码计算了前100000
个(注意是10万,而第一个实验是1万)质数的和,结果是62260698721
,用了4.927
秒。
既然是Python,如果不想重复“造轮子”的话……
import time
from sympy.ntheory import isprime
def sum_prime(count):
sum = 5 # 2 + 3
number = 5 # 从第三个质数5开始
cnt = 2 # 已经计算了前两个质数2和3的和
while cnt < count:
if number % 3 != 0 and isprime(number):
sum += number
cnt += 1
number += 2
return sum
start_time = time.time()
print(sum_prime(100000))
print("{:.3f} seconds".format(time.time() - start_time))
结果是:
62260698721
3.466 seconds
比自己造的“轮子”快一些。
注意,运行上述代码需要先安装sympy模块
pip3 install sympy
C
Python毕竟是解释器运行的,来看看C
怎么样吧。
#include <stdio.h>
#include <stdint.h>
#include <time.h>
#include <math.h>
uint8_t is_prime(uint64_t num) {
uint64_t num_2 = (uint64_t) sqrt(num);
uint64_t n = 5;
while (n <= num_2) {
if (num % n == 0) {
return 0;
}
n = n + 2;
}
return 1;
}
uint64_t sum_prime(count) {
uint64_t sum = 5; // 2 + 3
uint64_t number = 5; // 从第三个质数5开始
uint64_t cnt = 2; // 已经计算了前两个质数2和3的和
while (cnt < count) {
if ((number % 3 != 0) && is_prime(number)) {
sum = sum + number;
cnt ++;
}
number = number + 2;
}
return sum;
}
int main() {
uint64_t n = 100000L;
clock_t start, finish;
double duration;
start = clock();
printf("%llu\n", sum_prime(n));
finish = clock();
duration = (double)(finish - start) / CLOCKS_PER_SEC;
printf("%.3f seconds\n", duration);
}
运行结果是:
62260698721
0.396 seconds
同样的算法,比Python速度快了一个数量级,当然编译和解释没法比。
试一下计算前50万个质数的和,把uint64_t n =
那一行改为:
uint64_t n = 500000L;
运算结果为:
1774825271439
4.602 seconds
GOLANG
下面试试刚学的GOLANG,直接计算前50万质数的和。
package main
import (
"fmt"
"time"
"math"
)
func is_prime(num uint) bool {
var n uint
n = 3
var num_sqrt uint = uint(math.Sqrt(float64(num)))
for n <= num_sqrt {
if num % n == 0 {
return false
} else {
n += 2
}
}
return true
}
func sum_prime(count uint) uint {
var sum uint = 2 + 3
var sn uint = 5
var cnt uint = 2
for cnt < count {
if is_prime(sn) {
sum += sn
cnt += 1
}
sn += 2
}
return sum
}
func main() {
start := time.Now()
fmt.Println(sum_prime(500000))
elapsed := time.Since(start)
fmt.Println("执行完成耗时:", elapsed)
}
计算结果是:
1774825271439
执行完成耗时: 4.481641184s
BASIC
试试古老的BASIC,完全是为了怀旧。
Dim a As _Unsigned _Integer64
Dim start_time As _Unsigned Long
a = 500000
start_time = sec_time~&
Print sum_prime(a)
Print sec_time~& - start_time; "seconds."
End
Function is_prime% (num~&&)
Dim flag As Integer
Dim n As _Unsigned _Integer64
Dim num_2 As _Unsigned _Integer64
Let num_2 = Sqr(num~&&)
Let n = 5
Let flag = 1
While n <= num_2
If num~&& Mod n = 0 Then
flag = 0
Exit While
Else
n = n + 2
End If
Wend
is_prime = flag
End Function
Function sum_prime~&& (count~&&)
Dim sum As _Unsigned _Integer64
Dim number As _Unsigned _Integer64
Dim cnt As _Unsigned _Integer64
Let sum = 5
Let number = 5
Let cnt = 2
While cnt < count~&&
If number Mod 3 <> 0 And is_prime(number) = 1 Then
sum = sum + number
cnt = cnt + 1
End If
number = number + 2
Wend
sum_prime = sum
End Function
Function sec_time~& ()
t$ = Time$
hour$ = Left$(t$, 2): H% = Val(hour$)
min$ = Mid$(t$, 4, 2): M% = Val(min$)
sec$ = Right$(t$, 2): S% = Val(sec$)
sec_time~& = H% * 60 + M% * 60 + S%
End Function
运行结果是:
用了9秒,比想象的快,运行这段程序用的是QB64,QB64应该是编译后运行的。
总结
数据汇总
最后我把实验的数据汇总一下:
(单位:秒; X表示未测试)
前…个质数的和 | Python | Python+sympy | C | Go | BASIC |
---|---|---|---|---|---|
10,000 (按质数定义的算法) | 53.054 | X | X | X | X |
100,000 | 4.927 | 3.466 | X | X | X |
500,000 | 58.121 | 20.273 | 4.477 | 4.346 | 9 |
1,500,000 | X | X | 23.721 | 22.996 | 49 |
测试环境
硬件和操作系统
iMac(Late 2015)
3.1GHz 四核 Intel Core i5
内存 8G
macOS Big Sur 11.4
软件(编译)环境
Go
go1.16.5
C
gcc
Apple clang version 11.0.0 (clang-1100.0.33.16)
Python
Python 3.7.9 on Thonny 3.3.10
BASIC
QB64 Version 1.5
最后
本人数学渣渣,编程渣渣,算法渣渣……如果有高手看到本文中谬误,恳请指正,本人感激不尽!