Test Lucasa-Lehmera

Z testwiki
Wersja z dnia 09:33, 24 sty 2025 autorstwa imported>Pajmas (growthexperiments-addlink-summary-summary:1|0|1)
(różn.) ← poprzednia wersja | przejdź do aktualnej wersji (różn.) | następna wersja → (różn.)
Przejdź do nawigacji Przejdź do wyszukiwania

Test Lucasa-Lehmeratest pierwszości dla liczb Mersenne’a. Test został ułożony przez Edwarda Lucasa w 1856, a następnie ulepszony przez niego w 1878. W 1930 test został zmodyfikowany przez Derricka Henry’ego Lehmera.

Niech Mp oznacza liczbę Mersenne’a Mp=2p1 dla pewnej nieparzystej liczby pierwszej p (tzn. liczby pierwszej większej od 2). Pierwszość liczby p można sprawdzić za pomocą prostego algorytmu podziału, gdzie p jest wykładniczo mniejsza od Mp. Definiuje się następujący ciąg liczb naturalnych (Sk):

Sk={4gdy k=0,Sk122gdy k0.

Oto kilka początkowych wyrazów tego ciągu: 4, 14, 194, 37634, ... (ciąg A003010 w OEIS[1]).

Test Lucasa-Lehmera orzeka, że liczba Mp jest pierwsza wtedy i tylko wtedy, gdy jest dzielnikiem wyrazu o numerze (p2) w tym ciągu, co krótko zapisuje się kongruencją:

Sp20(modMp).

Resztę z dzielenia liczby Sp2 przez Mp nazywa się residuum Lucasa-Lehmera liczby p. Istotę testu można zatem streścić sformułowaniem:

liczba Mersenna Mp jest pierwsza wtedy i tylko wtedy, gdy residuum Lucasa-Lehmera liczby p równe jest zeru.

Za pomocą pseudokodu test można przedstawić w następujący sposób:

// Ustalamy, czy Mp = 2p − 1 jest pierwsza
Lucas-Lehmer(p)
    niech s ← 4
    niech M ← 2p − 1
    powtórz p − 2 razy:
        s ← ((s × s) − 2) mod M
    jeśli s = 0 zwróć PIERWSZA w przeciwnym razie zwróć ZŁOŻONA

Działanie mod M w każdej iteracji zapewnia, że wszystkie pośrednie wyniki są co najwyżej p-bitowe (w innym przypadku liczba bitów byłaby dublowana w każdej iteracji).

Złożoność czasowa

W powyższym algorytmie podczas każdej iteracji wykonywane są dwie kosztowne operacje: mnożenie s × s i operacja modulo mod M. Operacja mod M może być wykonywana szczególnie efektywnie na standardowych dwójkowych systemach poprzez dokonanie następującej obserwacji

k(kmod2n)+k/2n(mod2n1).

Mówi ona, że najmniej znaczące n bity spośród k plus pozostałe bity k, są równoważne k modulo 2n1. Równoważność może być stosowana powtarzalnie do momentu co najwyżej n bitów reszty. W tym postępowaniu reszta po wykonaniu dzielenia k przez liczbę Mersenne’a 2n1 jest obliczona bez używania dzielenia. Na przykład:

916mod251=11100101002mod251=111002+101002mod251=1100002mod251=12+100002mod251=100012mod251=100012=17.

Ponadto, ponieważ s × s nigdy nie przekroczy M2<22p, ta prosta technika zbiega w co najwyżej 1 p-bitowy dodatku (ewentualnie przeprowadza z p-tego bitu w pierwszy bit), co może być wykonane w liniowym czasie. Algorytm ten posiada wyjątkowy przypadek. Daje 2n1 dla mnożenia modulo zamiast poprawnej wartości 0. Jednak ten przypadek jest łatwy do wykrycia i naprawienia.

Przykłady

Liczba Mersenne’a M3 = 7 jest pierwsza. Test Lucasa-Lehmera sprawdza to w następujący sposób. Początkowo s jest ustalona i równa 4, później jest modyfikowana 3−2 = 1 raz:

  • s ← ((4 × 4) − 2) mod 7 = 0.

Ponieważ końcowa wartość s wynosi 0, wnioskujemy, że M3 jest pierwsza.

Z drugiej strony, M11 = 2047 = 23 × 89 nie jest pierwsza. Powtórnie, s jest ustalona i równa 4, ale teraz jest modyfikowana 11−2 = 9 razy:

  • s ← ((4 × 4) − 2) mod 2047 = 14
  • s ← ((14 × 14) − 2) mod 2047 = 194
  • s ← ((194 × 194) − 2) mod 2047 = 788
  • s ← ((788 × 788) − 2) mod 2047 = 701
  • s ← ((701 × 701) − 2) mod 2047 = 119
  • s ← ((119 × 119) − 2) mod 2047 = 1877
  • s ← ((1877 × 1877) − 2) mod 2047 = 240
  • s ← ((240 × 240) − 2) mod 2047 = 282
  • s ← ((282 × 282) − 2) mod 2047 = 1736

Ponieważ końcowa wartość s nie jest równa  0, wnioskujemy, że M11 = 2047 nie jest pierwsza. Mimo że M11 = 2047 posiada nietrywialne czynniki, test Lucasa-Lehmera nie daje żadnych wskazówek, jakie mogą one być.

Dowód

Dowód poprawności testu przedstawiany tutaj jest prostszy niż oryginalny dowód podany przez Lehmera. Przywołajmy definicję

si={4gdy i=0,si122w pozostałych przypadkach.

Naszym celem jest pokazanie, że Mp jest pierwsza wtedy i tylko wtedy, gdy sp20(modMp).

Ciąg si jest dany rekurencyjnie z rozwiązaniem w jawnej postaci. Niech ω=2+3 oraz ω=23. Poprzez indukcję dostajemy si=ω2i+ω2i dla każdego i:

s0=ω20+ω20=(2+3)+(23)=4

i

sn=sn122=(ω2n1+ω2n1)22=ω2n+ω2n+2(ωω)2n12=ω2n+ω2n.

Ostatni krok wykorzystuje ωω=(2+3)(23)=1. Jawna forma jest używana zarówno w dowodzie dostateczności, jak i konieczności.

Dostateczność

Celem jest pokazanie, że sp20(modMp) implikuje, że Mp jest pierwsza. Bezpośredni dowód wykorzystujący elementarną teorię grup daną przez J. W. Bruce[2], jak nawiązuje Jason Wojciechowski[3].

Przypuśćmy, że sp20(modMp). Wówczas

ω2p2+ω2p2=kMp

dla pewnego całkowitego k, więc

ω2p2=kMpω2p2.

Mnożąc przez ω2p2, otrzymujemy

(ω2p2)2=kMpω2p2(ωω)2p2.

Zatem

ω2p1=kMpω2p21.(1)

Dla uzyskania sprzeczności, przypuśćmy, że Mp jest złożona, i niech q będzie najmniejszym pierwszym czynnikiem liczby Mp. Liczby Mersenne’a są nieparzyste, więc q>2. Niech q będzie zbiorem liczb całkowitych modulo q i niech X={a+b3a,bq}. Mnożenie w X jest zdefiniowane, jak następuje

(a+3b)(c+3d)=[(ac+3bd)modq]+3[(ad+bc)modq].

Oczywiście to mnożenie jest działaniem wewnętrznym, to znaczy produkt liczb z X przechodzi w siebie. Rozmiar X oznaczamy przez |X|.

Gdy q>2, ω i ω należą do X[uwaga 1]. Podzbiór elementów X z ich odwrotnościami tworzy grupę, którą oznaczamy X*, a jej rząd |X*|. Jeden z elementów X nie posiada odwrotności, jest to 0, więc |X*||X|1=q21.

Teraz Mp0(modq) i ωX, więc

kMpω2p2=0

w X.

Następnie, wykorzystując równanie (1),

ω2p1=1

w X i podnosząc obie strony do kwadratu, otrzymujemy

ω2p=1.

Zatem ω należy do X* i posiada odwrotność ω2p1. Co więcej, rząd ω dzieli 2p, jednak ω2p11, więc nie dzieli on 2p1. Wobec tego rząd ω wynosi dokładnie 2p.

Rząd elementu jest nie większy niż rząd (rozmiar) grupy, więc

2p|X*|q21<q2.

Ale q jest najmniejszym pierwszym czynnikiem rozkładu liczby złożonej Mp, więc

q2Mp=2p1.

To prowadzi do sprzeczności 2p<2p1. Dlatego Mp jest pierwsza.

Konieczność

Z drugiej strony, celem jest pokazanie, że pierwszość Mp implikuje sp20(modMp). Poniższy uproszczony dowód przedstawił Öystein J.R.

Gdy 2p17(mod12) dla nieparzystych p>1, z własności symbolu Legendre’a wynika, że (3|Mp)=1. To oznacza, że 3 jest podwójnym nieresiduum modulo Mp. Dzięki kryterium Eulera, jest to równoważne

3Mp121(modMp).

Natomiast 2 jest podwójnym residuum modulo Mp, ponadto 2p1(modMp) i stąd 22p+1=(2p+12)2(modMp). Tym razem kryterium Eulera pozwala napisać

2Mp121(modMp).

Połączenie tych dwóch równoważnych relacji daje

24Mp12(2Mp12)3(3Mp12)(1)3(1)1(modMp).

Niech σ=23 i zdefiniujmy X tak jak wcześniej jako pierścień X={a+b3a,bMp}. Wówczas w pierścieniu X otrzymujemy

(6+σ)Mp=6Mp+(2Mp)(3Mp)=6+2(3Mp12)3=6+2(1)3=6σ,

gdzie pierwsza równość wykorzystuje dwumian Newtona w skończonej dziedzinie, która przedstawia się

(x+y)MpxMp+yMp(modMp),

a druga równość wykorzystuje małe twierdzenie Fermata, które brzmi

aMpa(modMp)

dla każdej liczby całkowitej a. Wartość σ została wybrana tak, aby ω=(6+σ)224. Co za tym idzie, może być wykorzystana do wyliczenia ωMp+12 w pierścieniu X jako

ωMp+12=(6+σ)Mp+124Mp+12=(6+σ)(6+σ)Mp2424Mp12=(6+σ)(6σ)24=1.

To co pozostaje, to mnożenie obu stron równania przez ωMp+14 i wykorzystanie ωω=1, co daje

ωMp+12ωMp+14=ωMp+14ωMp+14+ωMp+14=0ω2p1+14+ω2p1+14=0ω2p2+ω2p2=0sp2=0.

Skoro sp2 jest 0 w X, jest również 0 modulo Mp.

Zastosowanie

Test Lucasa-Lehmera jest testem pierwszości używanym przez Great Internet Mersenne Prime Search do znajdowania dużych liczb pierwszych. Te poszukiwania są bardzo owocne i przyczyniły się do znalezienia wielu największych liczb pierwszych znanych dzisiaj[4]. Test jest uważany za wartościowy, ponieważ potrafi zbadać pierwszość olbrzymich liczb zawartych w zbiorach o dużej liczbie elementów w przeciągu przystępnego wymiaru czasu. W odróżnieniu od równie szybkiego testu Pépina dla każdej liczby Fermata, który może być używany jedynie na wiele mniejszych zbiorach tak olbrzymich liczb, zanim zakres obliczeniowy się wyczerpie.

Uwagi: test jest bardzo szybki i bardzo prosty. Przy jego użyciu znaleziono największe dotąd znane liczby pierwsze. Test wymaga jak najszybszego algorytmu mnożenia np. szybkiej transformaty Fouriera. Ponieważ mnożenie liczb zapisanych w systemie liczbowym o danej podstawie jest w pewnym sensie splotem funkcji (cyfra jako wartość w funkcji potęgi podstawy), a transformata Fouriera splotu dwóch funkcji jest iloczynem ich transformat, zatem wielkie liczby naturalne można mnożyć szybko przez siebie, robiąc szybką transformatę Fouriera z ich cyfr, a następnie szybką transformatę odwrotną iloczynu tych transformat.

Implementacje

Krótki kod w języku Mathematica (tutaj sprawdzający liczbę pierwszą Mersenne’a Davida Slowinskiego & Paula Gage’a z 4 stycznia, 1994 (znaną 33.) z wykładnikiem p=859433) w czasie nieco ponad 2 godzin na komputerze osobistym z procesorem Skylake o częstotliwości 2,9 GHz): Funkcja Mod[s, M] zwraca na końcu 0 jedynie jeśli M jest liczbą pierwszą.

M = 2^859433 - 1;
s = 4;
AbsoluteTiming[
 Monitor[Do[
   s = Mod[s*s - 2, M], {n, 1, 859433 - 2}], {ProgressIndicator[
    n, {1, 859433 - 2}], n}]];
Print[Mod[s, M]]

Kod w Python

Przykładowy kod w języku Python 3 wykorzystujący bibliotekę gmpy2, kod zawiera optymalizację dzielenia modulo (patrz wyżej – Złożoność czasowa) oraz monitorowanie pozostałego czasu wykonania (estymowanego).

'''
Dla liczby p > 2, funkcja zwraca True jeżeli 2^p-1 jest pierwsza, w innym razie zwraca False
'''
from gmpy2 import *
from datetime import *

def Lucas_Lehmer(p):
    s = xmpz(4)
    M = xmpz(2 ** p) - 1
    startt = datetime.now()
    for i in range(0, p - 2):
        time_elapsed = datetime.now() - startt
        speed = i / (time_elapsed.total_seconds() + 1)
        remaining = (p - 2 - i) / (speed + 1)
        if i % int(speed + 1) == 0:
            print('Czas pozostały (hh:mm:ss.ms) {} czas wykonywania {}'.format(timedelta(seconds=remaining), time_elapsed))
        if (s.bit_length() <= p):
            # Podstawowa wersja algorytmu dla s o bitach <= p
            s = s * s - 2
            s = f_mod(s, M)
        else:
            # Optymalizacja dla s bitów > p
            s = s * s - 2
            md = s.bit_length() % 2
            right = s.bit_length() // 2 + md
            left = s.bit_length() // 2
            s = s[0:right] + f_mod(s[left:], M)

    if s == 0:
        return True
    else:
        return False

Zobacz też

Uwagi

Szablon:Uwagi

Przypisy

Szablon:Przypisy

Linki zewnętrzne

Szablon:Teoria liczb


Błąd rozszerzenia cite: Istnieje znacznik <ref> dla grupy o nazwie „uwaga”, ale nie odnaleziono odpowiedniego znacznika <references group="uwaga"/>