使用函数求素数和_算法第4课:验证素数的费马小定理及其惊人的运算效率

0.引言

素数问题始终是数学家魂牵梦绕的问题,我们在算法第三课中讲到了素数的基础问题求GCD。
本节课我们将深入探讨求解素数的方法。

算法第3课:人类文明的第一个算法,欧几里得法求最大公因数

当下,我们已经有了各种方法测试某个具体数字是否为质数。其中最简单的的解法,就是先找到最小公因数(大于1的数字),然后再检验目标数字能够被最小公因数整除。

205345cacb94eb32bb5005a2e60932d5.png

1.寻找最小公因子

第一步先找到最小因数。

def smallest_divisor(n):    for divisor in range(2, n):         if divisor ** 2 > n: return n        if n % divisor == 0: return divisor # 就只有这两种情况

`if divisor ** 2 > n: return n`这一行是最有意思的。因为素数必然能分解为两个数字,而其中的一个必然小于n的平方根。

如何验证素数呢?素数的最小因素必然是其本身。

def prime-predicate(n):    return smallest_divisor == n 

这种解法的算法复杂度为O(n^1/2)

2.费马小定理

下面,我们一起来欣赏天才的解法。费马在在1636年提出一个验证素数的定理。如果p是一个质数,而整数a不是p的倍数,则有a^p≡a(mod p)。换言之,a的p次幂与a就模数p同余。

43434984bd2e299b4c8bda4bc7e98cec.png

我们先写一个幂指函数:

def expmod(base, exp, m):    # base case    if exp == 0: return 1    if evenp(exp):        return expmod(base, exp/2, m) ** 2 % m    else:        return base * expmod(base, exp-1, m) % mdef evenp(n):    return n % 2 == 0print(expmod(2, 8, 7))# 4

再写一个测试

def fermat_test(n):    def try_it(a):        return expmod(a, n, n) == a    try_it(1 + random.randint(n-1)) 

最后验证素数:

 def fast_prime(n, times): #recursive的方法可以协助思考, 思考起点和终点.    if times == 0: return True    if fermat_test(n): return fast_prime(n, times -1)    else: False

这种解法的算法复杂度低到惊人的O(log(n))。

3.埃拉托斯特尼素数筛子

作为补充贴上素数筛子的算法:

6e29c015715a3b917d6915ad0defbcf5.png
def primes(n):    if n < 2:        return None    primes_sieve = [True] * (n + 1)    primes_sieve[1] = False    primes_sieve[0] = False    # sieve    for i in range(2, int(n ** 0.5) + 1):  # 要包含n这个数字.        if primes_sieve[i]:            for j in range(i * i, n + 1, i):  # mark the multiplies, 本数不能划掉.                primes_sieve[j] = False    return [i for i, p in enumerate(primes_sieve) if p]def count_primes(n):    if n < 2:        return None    primes_sieve = [True] * (n + 1)    primes_sieve[1] = False    primes_sieve[0] = False    # sieve    for i in range(2, int(n ** 0.5) + 1):  # 要包含n这个数字.        if primes_sieve[i]:            for j in range(i * i, n + 1, i):  # mark the multiplies, 本数不能划掉                primes_sieve[j] = False    return primes_sieve.count(True)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值