關於質數(素數)

什麼是質數?

質數(Prime number)又稱素數, 質數是大於1並且只能被1和自身整除的天然數。大於1的天然數若是不是素數,就稱爲合數(Composite number)。python

算術基本定理

算術基本定理最先由歐幾里得證實, 是表示任何合數均可以不斷分解成素數的組合,如108能夠分解爲2,3兩個素數,歐幾里得發現把這些素數因子的次方相乘能夠獲得原來的數字108=2^2×3^3,並且這種分解爲素數乘積的方式是惟一的。素數因子分解就像是一個鎖,並且只有一把開鎖的鑰匙,這也是現代密碼學的基礎。面試

素數定理

素數定理描述素數在天然數中分佈的漸進狀況,就是小於n中素數的個數隨着n的增大素數的密度就愈來愈小。當n愈來愈大時它的圖像就愈來愈接近\frac{x}{ln(x)}。因此一個數字內素數的數量x約等於\frac{x}{ln(x)},當x越大時偏差越小。因此好比要生成s大小的素數序列,使用這個方法的話就要提升反推出來上界x的大小。app

試除法

那麼怎麼判斷一個數是否是素數?這也是不少面試題裏面問到的。一種簡單的方法是試除法。dom

好比判斷n是否是素數,可讓n除以1n之間的整數,若是能夠除盡則表示是合數不然是質數。測試

def isPrime(n):
    if n < 2: return False
    for i in range(2, n):
        if n % i == 0:
            return False
    return True
複製代碼

可是能夠發現大於2偶數都不是質數,由於它們能夠被2整除,因此這能夠減小迭代次數。ui

def isPrime(n):
    if n == 2: return True
    if n < 2 or n % 2 == 0: return False
    for i in range(3,n,2):
        if n % i == 0:
            return False
    return True
複製代碼

還有沒有更快的方法?有,就是迭代到\sqrt{n},由於一個數字分解爲兩個因子,其中必然有一個小於或等於\sqrt{n},否則兩個都大於\sqrt{n}它們相乘就大於n了。spa

def isPrime(n):
    if n == 2: return True
    if n < 2 or n % 2 == 0: return False
    for i in range(3, int(n ** 0.5) + 1, 2):
        if n % i == 0:
            return False
    return True
複製代碼

試除法還能不能更快?根據算術基本定理任何合數最終均可以分解爲素數的組合,因此只用除小於n的素數就好了。.net

篩選法

篩選法(sieve of Eratosthenes)能夠給出小於n的素數序列,好比要生成100內的素數序列,首先能夠生成2到100間數字表,而後將列表第一個沒被標記的數字標記爲素數而後將數字表中它的倍數標記爲合數,而後不斷重複這個步驟。3d

對於給定n只須要遍歷到\sqrt n,剩下的就都是素數了。code

def sieve(n):
    composite = {}
    primes = [2]

    for i in range(3, int(n ** 0.5) + 1, 2):
        if i not in composite:
            for j in range(i ** 2, n + 1, i): composite[j] = 1
    for k in range(3, n + 1, 2):
        if k not in composite: primes.append(k)

    return primes
複製代碼

對於不指定n的大小能夠這麼寫。

def genPrimes():
    primes = [2]
    i = 1
    yield 2
    while True:
        i += 2
        for p in primes:
            if i % p == 0:
                break
        else:
            primes.append(i)
            yield i
複製代碼

費馬小定理

雖然通常判斷是否是素數用試除法就好了,可是當要判斷一個大數是否是素數時,也仍是太慢了。

費馬小定理是歐拉定理的一個特殊狀況,它是說一個正整數a質數p次方減a能夠被p次方整除。用公式表示能夠爲a^{p-1} \equiv 1\ (mod\ p)

可是也不能徹底正確,好比227^{561-1} \equiv 1\ (mod\ 561) 可是561=3×11×17,因此能夠隨機生成多個a來測試,這樣就能夠下降將出錯機率。

import random
def gcd(a, b):
    while b != 0:
        a, b = b, a % b
    return a
def randA(p):
    return random.randint(1, p - 1)
def fastMod(f, p, m):
    ans = 1
    whlie p > 0:
        if p % 2 == 1:
            ans = (ans * f) % m
            p -= 1
        p //= 2
        f = (f ** 2) % m
    return ans
def isPrime(p):
    trials = 30
    for i in range(trials):
        a = randA(p)
        if gcd(a, p) != 1: return False
        if fastMod(a, p - 1, p) != 1: return False
    return True
複製代碼
相關文章
相關標籤/搜索