Serwis Edukacyjny w I-LO w Tarnowie Materiały dla uczniów liceum |
Wyjście Spis treści Wstecz Dalej Autor artykułu: mgr Jerzy Wałaszek |
©2025 mgr Jerzy Wałaszek |
ProblemNależy sprawdzić, czy liczba naturalna p jest liczbą pierwszą. |
W roku 1640 Pierre de Fermat odkrył ponownie chińskie twierdzenie o pierwszości:
Jeśli p jest liczbą pierwszą, to 2p mod p = 2.
Równanie jest spełniane przez wszystkie bez wyjątku liczby pierwsze. Jeśli
liczba p nie spełnia równania
Przykład:
p = 341 = 11 × 31
2341 mod 341 = 2
Pomyślisz w tym miejscu, iż w takim razie twierdzenie jest niepoprawne. Nic z tych rzeczy. W matematyce relacja wynikania nie jest zwrotna:
Z A wynika B – nie jest tym samym, co z B wynika A – wtedy mielibyśmy relację równoważności.
Twierdzenie mówi jedynie, iż jeśli liczba p jest pierwsza, to 2p jest przystające modulo
p do 2. Czyli jest
spełnione dla każdej liczby pierwszej (z A wynika B).
Jeśli jednak weźmiemy drugą część twierdzenia, czyli równanie
Wszyscy Polacy mówią po polsku (z A wynika B), ale jeśli ktoś mówi po polsku, to wcale nie musi być Polakiem (z B nie wynika A).
Fermat dokładnie przestudiował chińskie twierdzenie o pierwszości oraz zbadał je dla innych podstaw niż 2. W efekcie znacznie poprawił dokładność chińskiego testu. Wyniki pracy Fermata są dzisiaj znane pod nazwą Małego Twierdzenia Fermata (ang. Fermat's Little Theorem – FLT). Można je przedstawić następująco:
Małe Twierdzenie Fermata
Niech p będzie dowolną liczbą pierwszą i a dowolną liczbą naturalną.
Jeśli NWD(p, a) = 1, to ap-1 mod p = 1
Małe Twierdzenie Fermata jest bardziej ogólne od twierdzenia chińskiego. Jeśli w FLT podstawę a zastąpimy przez 2 i obie strony równania pomnożymy przez 2, to otrzymamy chińskie twierdzenie o pierwszości:
a = 2 ap-1 mod p = 1 / ×2 (2p-1)×2 mod p = 1×2 2p-1+1 mod p = 2 2p mod p = 2
Wzór Fermata,
Algorytm Fermata testujący pierwszość liczby
Do wyznaczania
K01: Sprawdź, czy p jest podzielne przez ; wstępna eliminacja liczb złożonych oraz liczbę pierwszą z przedziału <2, 1000>. ; ewentualnych liczb pseudopierwszych Carmichaela Jeśli tak, to idź do kroku K07 ; Dla liczb złożonych zwracamy NIE i kończymy K02: Dla i = 1, 2, …, 10: ; w pętli sprawdzamy warunek Fermata wykonuj kroki K03…K05 K03: a ← Losowa(2, p-1) ; wybieramy losową podstawę a K04: Jeśli NWD(p, a) ≠ 1, ; podstawa musi być względnie pierwsza z p to idź do kroku K07 K05: Jeśli ap-1 mod p ≠1, ; test na liczbę złożoną to idź do kroku K07 K06: Pisz "TAK" i zakończ ; liczba p przeszła pozytywnie wszystkie testy K07: Pisz "NIE" i zakończ ; liczba p nie przeszła testów, jest zatem liczbą złożoną
Uwaga: Zanim uruchomisz program, przeczytaj wstęp do tego artykułu, w którym wyjaśniamy funkcje tych programów oraz sposób korzystania z nich. |
Program odczytuje z pierwszego
wiersza TAK – jeśli liczba
p jest pierwsza lub z bardzo małym
prawdopodobieństwem pseudopierwsza Carmichaela |
Pascal// Test pierwszości Fermata // Data : 5.04.2008 // (C)2020 mgr Jerzy Wałaszek //---------------------------- program prg; // 64 bitowy generator pseudolosowy //--------------------------------- function Losuj(a, b : qword) : qword; var w : qword; i : integer; begin w := 0; for i := 1 to 8 do begin w := w shl 8; w := w or random(256); end; Losuj := a+(w mod (b-a)); end; // Funkcja oblicza NWD //-------------------- function NWD(a, b : qword) : qword; var t : qword; begin while b <> 0 do begin t := b; b := a mod b; a := t; end; NWD := a; end; // Funkcja mnoży a i b mod n //-------------------------- function MnozModulo(a, b, n : qword) : qword; var m, w : qword; begin w := 0; m := 1; while m <> 0 do begin if b and m <> 0 then w := (w+a) mod n; a := (a shl 1) mod n; m := m shl 1; end; MnozModulo := w; end; // Funkcja oblicza a^e mod n //-------------------------- function PotegujModulo(a, e, n : qword) : qword; var m, p, w : qword; begin p := a; w := 1; m := 1; while m <> 0 do begin if e and m <> 0 then w := MnozModulo(w, p, n); p := MnozModulo(p, p, n); m := m shl 1; end; PotegujModulo := w; end; // Tablica początkowych 169 liczb pierwszych //------------------------------------------ const lp : array [0..168] of integer = ( 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997, 1009); var p, a : qword; i : integer; t : boolean; begin randomize; readln(p); t := true; for i := 0 to 168 do if(p <> lp[i]) and (p mod lp[i] = 0) then begin t := false; break; end; if t and (p > 1009) then begin for i := 1 to 10 do begin a := Losuj(2, p-1); if(NWD(p, a) <> 1) or (PotegujModulo(a, p-1, p) <> 1) then begin t := false; break; end; end; end; if t then writeln('TAK') else writeln('NIE'); end. |
C++// Test pierwszości Fermata // Data : 5.04.2008 // (C)2020 mgr Jerzy Wałaszek //--------------------------- #include <iostream> #include <cstdlib> #include <ctime> using namespace std; typedef unsigned long long ulong; // 64 bitowy generator pseudolosowy //--------------------------------- ulong Losuj(ulong a, ulong b) { ulong w = 0; int i; for(i = 1; i <= 8; i++) { w <<= 8; w |= rand() & 0xff; } return a+(w % (b-a)); } // Funkcja oblicza NWD //-------------------- ulong NWD(ulong a, ulong b) { ulong t; while(b) { t = b; b = a % b; a = t; } return a; } // Funkcja mnoży a i b mod n //-------------------------- ulong MnozModulo(ulong a, ulong b, ulong n) { ulong m, w; w = 0; for(m = 1; m; m <<= 1) { if(b & m) w = (w+a) % n; a = (a << 1) % n; } return w; } // Funkcja oblicza a^e mod n //-------------------------- ulong PotegujModulo(ulong a, ulong e, ulong n) { ulong m, p, w; p = a; w = 1; for(m = 1; m; m <<= 1) { if(e & m) w = MnozModulo(w, p, n); p = MnozModulo(p, p, n); } return w; } // Tablica początkowych 169 liczb pierwszych //------------------------------------------ const ulong lp[] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997, 1009}; int main() { ulong p, a; int i; bool t; srand((unsigned)time(NULL)); cin >> p; t = true; for(i = 0; i < 169; i++) if((p != lp[i]) && (p % lp[i] == 0)) { t = false; break; } if(t && (p > 1009)) { for(i = 1; i <= 10; i++) { a = Losuj(2, p-1); if((NWD(p, a) != 1) || (PotegujModulo(a, p-1, p) != 1)) { t = false; break; } } } cout << (t ? "TAK": "NIE") << endl; return 0; } |
Basic' Test pierwszości Fermata ' Data : 5.04.2008 ' (C)2020 mgr Jerzy Wałaszek '--------------------------- ' 64 bitowy generator pseudolosowy '--------------------------------- Function Losuj(Byval a As Ulongint, _ Byval b As Ulongint) _ As Ulongint Dim w As Ulongint, i As Integer w = 0 For i = 1 To 8 w = w Shl 8 w = w Or Culngint(Rnd(1)*256) Next Losuj = a+(w Mod (b-a)) End Function ' Funkcja oblicza NWD '-------------------- Function NWD(Byval a As Ulongint, _ Byval b As Ulongint) _ As Ulongint Dim t As Ulongint While b t = b: b = a Mod b: a = t Wend NWD = a End Function ' Funkcja mnoży a i b mod n '-------------------------- Function MnozModulo(Byval a As Ulongint, _ Byval b As Ulongint, _ Byval n As Ulongint) _ As Ulongint Dim As Ulongint m, w w = 0: m = 1 While m If b And m Then w = (w+a) Mod n a = (a Shl 1) Mod n m = m Shl 1 Wend MnozModulo = w End Function ' Funkcja oblicza a^e mod n '-------------------------- Function PotegujModulo(Byval a As Ulongint, _ Byval e As Ulongint, _ Byval n As Ulongint) _ As Ulongint Dim As Ulongint m, p, w p = a: w = 1: m = 1 While m If e And m Then w = MnozModulo(w, p, n) p = MnozModulo(p, p, n) m = m Shl 1 Wend PotegujModulo = w End Function ' Tablica początkowych 169 liczb pierwszych '------------------------------------------ Dim lp(168) As Integer => {_ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, _ 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, _ 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, _ 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, _ 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, _ 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, _ 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, _ 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, _ 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997, 1009} Dim As Ulongint p, a Dim i As Integer, t As Byte Randomize Open Cons For Input As #1 Input #1, p Close #1 t = 1 For i = 0 To 168 if(p <> lp(i)) And (p Mod lp(i) = 0) Then t = 0: Exit For End If Next if (t = 1) And (p > 1009) Then For i = 1 To 10 a = Losuj(2, p-1) if(NWD(p, a) <> 1) OrElse (PotegujModulo(a, p-1, p) <> 1) Then t = 0: Exit For End If Next End If If t = 1 Then Print "TAK": Else Print "NIE" End |
Python
(dodatek)# Test pierwszości Fermata # Data : 14.02.2024 # (C)2024 mgr Jerzy Wałaszek # --------------------------- import random G = 2**64 # 64 bitowy generator pseudolosowy # -------------------------------- def losuj(a, b): w = 0 for i in range(8): w <<= 8 w |= int(random.random()*256) return a+(w%(b-a)) # Funkcja oblicza NWD # ------------------- def nwd(a, b): while b: t = b b = a % b a = t return a # Funkcja mnoży a×b % n # --------------------- def mnoz_modulo(a, b, n): w, m = 0, 1 while m < G: if b & m: w = (w+a) % n a = (a << 1) % n m <<= 1 return w # Funkcja oblicza a**e % n # ------------------------ def poteguj_modulo(a, e, n): p, w, m = a, 1, 1 while m < G: if e & m: w = mnoz_modulo(w, p, n) p = mnoz_modulo(p, p, n) m <<= 1 return w # Tablica początkowych 169 liczb pierwszych # ------------------------------------------ lp = [ \ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, \ 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, \ 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, \ 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, \ 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, \ 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, \ 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, \ 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, \ 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997, 1009] random.seed(None, 2) p = int(input()) t = True for i in range(169): if (p != lp[i]) and not (p%lp[i]): t = False break if t and (p > 1009): for i in range(10): a = losuj(2, p-1) if (nwd(p, a) != 1) or (poteguj_modulo(a, p-1, p) != 1): t = False break if t: print("TAK") else: print("NIE") |
Wynik: |
9223372036854775783 TAK 9223372036854775787 NIE |
Zespół Przedmiotowy Chemii-Fizyki-Informatyki w I Liceum Ogólnokształcącym im. Kazimierza Brodzińskiego w Tarnowie ul. Piłsudskiego 4 ©2025 mgr Jerzy Wałaszek |
Materiały tylko do użytku dydaktycznego. Ich kopiowanie i powielanie jest dozwolone pod warunkiem podania źródła oraz niepobierania za to pieniędzy.
Pytania proszę przesyłać na adres email:
Serwis wykorzystuje pliki cookies. Jeśli nie chcesz ich otrzymywać, zablokuj je w swojej przeglądarce.
Informacje dodatkowe.