Uživatelské nástroje

Nástroje pro tento web


prog:mersenne

Mersennova prvočísla

Přirozená čisla o jednu menší než celočíselná mocnina 2 nazveme Mersennova čísla, Mersennovo číslo Mn má tedy tvar

Mn = 2n - 1.

pro n = 1, 2, … . Máme tedy M1 = 1, M2 = 3, M3 = 7, atd. V některých případech se jedná o prvočísla (např. M3, pak hovoříme o Mersennových prvočíslech), v jiných případech o čísla složená (např. M11 = 2047 = 23.89).

Čislo 274 207 281 − 1, které má více než 22 miliónů desetinných cifer, bylo v době psaní této stránky největším známým prvočíslem vůbec. Dne 3. ledna 2018 bylo ohlášeno nalezení již 50. Mersennova prvočísla 277 232 917 − 1 s více než 23 milióny číslic a 21. prosince téhož roku bylo oznámeno již 51. prvočíslo 282 589 993 − 1 (24 862 048 desetinných číslic). Hledání těchto prvočísel koordinuje projekt GIMPS. Není dokázáno, zda existuje nekonečně mnoho Mersennových prvočísel.

Přímé hledání dělitelů

Vyzbrojeni algoritmem na ověřování prvočíselnosti můžeme zkusit zkoumat, která Mersennova čísla jsou prvočísly. V cyklu od 1 do nějaké rozumné meze tedy vytváříme testovaná čísla a aplikujeme na ně náš algoritmus. Pro jednotlivá čísla vypisujeme, zda se jedná o prvočíslo nebo uvedeme jeho rozklad na důkaz toho, že se jedná o číslo složené.

mersenne1.py
# hledání Mersennových prvočísel M_n = 2^n - 1
# pro n od 2 do 60
 
n = 2 # počítadlo
mocnina2 = 4 # průběžně si pamatujeme mocninu 2
# testujeme kandidáty v určeném rozsahu
while n <= 60:
    cislo = mocnina2 - 1 # číslo je rozhodně liché
 
    # není potřeba testovat dělitelnost 2!
    # testování lichých dělitelů
    delitel = 3 
    # testování jednotlivých dělitelů v cyklu
    while delitel * delitel <= cislo: # POZOR: musí být <=
        if cislo % delitel == 0: # test dělitelnosti
            break # dělitel nalezen, není třeba dále testovat
        delitel = delitel + 2 # další lichý dělitel
    # konec testování lichých dělitelů
 
    if delitel * delitel > cislo: # výpis nalezeného prvočísla
        print('M', n, '=', cislo, 'je prvočíslo')
    else:
        print('M', n, '=', cislo, '=', delitel, '*', cislo // delitel)
 
    n = n + 1 # jdeme na další číslo
    mocnina2 = mocnina2 * 2 # aktualizujeme mocninu 2

V testování dělitelů jsme si mohli dovolit přeskočit 2, protože Mersennovo číslo nikdy nemůže být sudé. Mocniny 2 vytváříme průběžně násobením 2, předchozí mocninu si pamatujeme ve zvláštní proměnné. Po ukončení vnitřního cyklu vždy vyhodnotíme podmínku a vypíšeme výsledek.

Ukazuje se, že tímto programem snadno a rychle otestujeme čísla do indexu 60. Při pokusu o testování M61 však výsledek dlouho nedostáváme, proto pravděpodobně sáhneme k násilnému ukončení programu.

Závěrečný blok cyklu

blok else ve while cykluNa tomto místě učiníme malou odbočku a ukážeme si jednu další možnost, kterou nám v Pythonu dává cyklus while. Můžeme totiž i pro cyklus definovat závěrečný blok else, který se vykoná po ukončení vlastního cyklu. Ptáte se možná, k čemu to je dobré? Jde o to, že pokud je provádění cyklu přerušeno příkazem break, tento závěřečný blok se přeskočí. To znamená, že else blok se provede pouze v případě ukončení cyklu na základě nesplnění úvodní podmínky. Snad je to dobře patrné i z vývojového diagramu.

V některých případech nám může použití bloku else ušetřit dodatečnou podmínku, jako v příkladu níže, nebo zvýšit přehlednost kódu. Na druhou stranu se lze poměrně snadno bez takové možnosti obejít, ostatně řada jiných programovacích jazyků jako C/C++, Pascal, Java, C# aj. ekvivalentní konstrukci neposkytují.

mersenne2.py
# hledání Mersennových prvočísel M_n = 2^n - 1
# pro n od 2 do 60
 
n = 2 # počítadlo
mocnina2 = 4 # průběžně si pamatujeme mocninu 2
# testujeme kandidáty v určeném rozsahu
while n <= 60:
    cislo = mocnina2 - 1 # číslo je rozhodně liché
 
    # není potřeba testovat dělitelnost 2!
    # testování lichých dělitelů
    delitel = 3 
    # testování jednotlivých dělitelů v cyklu
    while delitel * delitel <= cislo: # POZOR: musí být <=
        if cislo % delitel == 0: # test dělitelnosti
            print('M', n, '=', cislo, '=', delitel, '*', cislo // delitel)
            break # dělitel nalezen, není třeba dále testovat
        delitel = delitel + 2 # další lichý dělitel
    # konec testování lichých dělitelů
    else: # výpis nalezeného prvočísla
        print('M', n, '=', cislo, 'je prvočíslo')
 
    # break skočí až sem
    n = n + 1 # jdeme na další číslo
    mocnina2 = mocnina2 * 2 # aktualizujeme mocninu 2

Lucas-Lehmerův test

Uvedli jsme, že výše uvedený program nám v rozumně krátké době nezodpověděl, zda M61 je prvočíslo. Není to až tak překvapivé, protože 261 ≈ 2,306.1018 a pro prokázání prvočíselnosti je potřeba otestovat více než tři čtvrtiny miliardy dělitelů. Již dříve jsme učinili některé zkušenosti, že tam, kde nestačí hrubá síla, se můžeme zkusit obrátit s prosbou o pomoc na matematiku. Ta nás naštěstí ani v tomto případě nezklame.

Není až tak obtížné dokázat, že Mersennovo číslo Mn může být prvočíslem jen pro prvočíselné n, v opačném případě je vždy složeným číslem. Již to nám vydělí celou řadu kandidátů. Velký krok vpřed je pak test odvozený Lucasem a následně vylepšený Lehmerem, který dokáže jednoznačně rozhodnout o prvočíselnosti Mn, pokud je n libovolné prvočíslo (s výjimkou 2). Test je poměrně jednoduchý1), stačí vytvořit posloupnost podle těchto pravidel:

  • s0 = 4,
  • si = (si-1.si-1 - 2) mod Mn pro i = 1, 2, …, n - 2. Operace modulo k znamená zbytek po celočíselném dělení k.

Nakonec otestujeme poslední člen posloupnosti (tj. sn-2). Je-li nulový, pak Mn je prvočíslo, je-li libovolný nenulový, jde o číslo složené.

S pomocí nově nabytých znalostí pak snadno a rychle určíme, že M61 je opravdu prvočíslo2).

lucas_lehmer.py
# Lucas-Lehmerův test, zda Mersennovo číslo M_61 = 2^61 - 1
# je prvočíslo
# podmínky testu jsou splněny - exponent 61 je liché prvočíslo
 
exponent = 61
cislo = 2 ** exponent - 1 # zkoumané Mersennovo číslo
 
s = 4 # počátek LL posloupnosti
krok = 0 # počítadlo kroků
while krok < exponent - 2:
    s = (s * s - 2) % cislo # další člen LL posloupnosti
    krok = krok + 1 # aktualizace počítadla
 
# závěrečný test a tisk výsledku
if s == 0:
    print('M', exponent, '=', cislo, 'je prvočíslo')
else:
    print('M', exponent, '=', cislo, 'je složené číslo')

Nakonec poskládáme všechny díly skládačky do programu na hledání Mersennových prvočísel s využitím Lucas-Lehmerova testu. Kromě n = 2 budeme procházet jen liché exponenty n. Klasickým algoritmem zkontrolujeme prvočíselnost n a v kladném případě postoupíme k Lucas-Lehmerovu testu (viz vývojový diagram). Možný výsledek je v programu mersenne3.py. S jeho pomocí lze i na běžném již dosti vousatém počítači nalézt všechna Mersennova prvočísla až do indexu 3500 během necelé půl minuty! To znamená všechna Mn, která mají méně než 1000 číslic v dekadickém zápisu! Vzpomeneme-li si na naše původní obtíže s indexem 61, jedná se o úžasný výsledek.

Naše nové zkušenosti tak znovu vysvětlují velmi úzký vztah mezi teoretickou informatikou a matematikou. Je snad na místě také podotknout, že studium prvočísel, jejich testování, hledání faktorizace čísel a příbuzné úlohy jsou stále v centru pozornosti vědců i programátorů. Mají totiž úzký vztah ke konstrukci šifrovacích schémat a bezpečnosti informací.

Poznámka k typu int

Na závěr této kapitoly dovolte malou poznámku k implementaci celočíselného typu int v jazyku Python. Bezstarostně a s maximálním pohodlím jsme výše prováděli aritmetické výpočty s poměrně velkými nezápornými čísly bez jakékoliv ztráty přesnosti (důsledně jsme používali datový typ int). Takový komfort většina programovacích jazyků ve své základní výbavě neposkytuje. Naopak je typické, že existují celočíselné typy pevné velikosti (často ve znaménkové a neznaménkové formě), např. 16bitový znaménkový integer obvykle pojme hodnoty od -32 768 do +32 767, 32bitový neznaménkový od 0 do 4 294 967 295 apod. Ty většinou odpovídají datovým typům, se kterými dokáže pracovat mikroprocesor na nejnižší úrovni. Při výpočtech pak musíme dávat pozor na to, zda jsme konkrétní datový typ správně zvolili a zda se výsledky operací vejdou do určeného rozsahu. Částečně podobné problémy jsme řešili u typu float. Pro celočíselné výpočty v „neomezeném“ rozsahu se pak v ostatních jazycích používají speciální nadstavbové knihovny.

1)
byť jeho odvození a ověření správnosti zdaleka tak triviální není
2)
Můžete si vyzkoušet, že kupříkladu číslo M59 test vyloučí. Z výsledků předchozího programu již víme, že 259 - 1 = 179 951 . 3 203 431 780 337 je skutečně číslo složené
prog/mersenne.txt · Poslední úprava: 04.01.2019 06:04 autor: Ivana Stefanová

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki