质数算法的实验 - Python,C,Go,BASIC

质数又称素数。一个大于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 是否为质数时,查找他的因数时,只需要查找 3sqrt(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表示未测试)

前…个质数的和PythonPython+sympyCGoBASIC
10,000
(按质数定义的算法)
53.054XXXX
100,0004.9273.466XXX
500,00058.12120.2734.4774.3469
1,500,000XX23.72122.99649

测试环境

硬件和操作系统

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

最后

本人数学渣渣,编程渣渣,算法渣渣……如果有高手看到本文中谬误,恳请指正,本人感激不尽!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值