Popis AKS algoritmu

Implementace AKS algoritmu zahrnuje využití dlouhých čísel a vyžaduje znalost základních postupů teorie čísel. Nejprve se podíváme na slovní popis algoritmu, rozdělený do 6ti hlavních bodů:

pro n > 1 na vstupu:

  • n = a^b ⇒ n je SLOŽENÉ
  • Najdi nejmenší celočíselné r, tak aby řád (r mod n) byl větší než </m>(log_2(N))^2</m>
  • Pokud platí 1 < gcd(a,n) < n pro libovolné a <= r ⇒ n je SLOŽENÉ
  • Pokud n <= r ⇒ n je PRVOČÍSLO
  • Pro a=1 až do eulerFunc(r) delim{[}{log2(N)}{]} testuj .. pokud (x + a)^n není kongruentní s x^n + a mod x^(r-1), n ⇒ n je SLOŽENÉ
  • n je PRVOČÍSLO

1. Krok

V prvním kroku algoritmu testujeme, zda n nevzniklo umocněním neznámého základu na neznámý exponent. Pro každý základ menší než odmocnina z n procházíme všechny exponenty, dokud základ umocněný na exponent nepřekročí velikost n, pokud nastane rovnost, n je složené.

  CBigInt a(2);

  // Dokud je a^2 je mensi nez n ... zvetsuj zaklad
  while (a*a <= n) {
    CBigInt b = 2, c = A;

    // Dokud je a^b mensi nez n ... zvetsuj exponent
    while (c < n) {          

      // Pokud N = A^B, konec
      c *= a;
      if (c == n) return false;
    }    
    a++;
  }

2. Krok

V druhém kroku hledáme takové nejmenší, celočíselné r aby řád n modulo r byl větší než druhá mocnina z log_2(n). Příčemž řádem čísla n mod r rozumíme exponent, na který musíme umocnit číslo n, aby byl výsledek kongruentní číslu 1 mod r.

K nalezení řádu n mod r použijeme Eulerovu větu, která nám říká, že pokud r, n jsou nesoudělná čísla a platí n umocněno na fi(r) je kongruentní číslu 1 mod r, pak řád n mod r je menší roven fi(r), kde fi(r) je Eulerova funkce aplikována na r. Eulerova funkce čísla r vrací počet čísel nesoudělných s číslem r z množiny {1,2..r}.

3. Krok

Druhý krok můžeme sloučit s krokem třetím, při hledání r tedy zároveň zkoušíme, zda n není náhodou soudělné s aktuálním hodnotou v r. Vycházíme z toho že dvě čísla jsou soudělná pokud se jejich největší společný dělitel nerovná jedné.

  CBigInt r(2), tmpLog(CBigInt::log2pow(n, 2));

  while (r < n) {

    // Pokud je n soudelne s libovolnym r, konec (n neni prvocislo)
    if (CIntMath::findGCD(r, n) != 1) return false;    

    // Pokud je r soudelne s n, konec, n je slozene 
    CBigInt tmpGCD = CIntMath::findGCD(r, n);

    if ((tmpGCD > 1) && (tmpGCD < n)) return false;
    if ((CIntMath::fastExp(n, d, r) == (CBigInt(1) % r)) && (d > tmpLog)) break;    
    r++;
  }

4. Krok

Ve čtvrtém kroku pouze porovnáme n a námi nalezené r, pokud by bylo n <= r, n je prvočíslo.

  if (n <= r) return true;

5. Krok

Pátý krok představuje časově nejnáročnější část algoritmu, zároveň je i poněkud náročnější na implementaci, než předešlé kroky. Zbývá nám prověřit, zda je polynom (x + a)^n kongruentní s polynomem (x^n + a) v okruhu zbytkových tříd x^(r-1), n. Pokud je tato podmínka splněna pro všechna a z množiny {1,.. fi(r) delim{[}{log_2(n)}{]}}, kde delim{[}{log_2(n)}{]} je celá část z logaritmu n o základu 2, potom n je prvočíslo.

  a = 1; 

  while (a < CIntMath::eulerFunc(r) * log2(n)) {    

    CPolynom<CBigInt, CBigInt> p1, p2;

    // Polynom1 = (x + a)^n
    p1.clearItems();
    p1.setItem(1, 1);
    p1.setItem(0, a);

    // Polynom2 = x^n + a
    p2.clearItems();
    p2.setItem(n, 1);
    p2.setItem(0, a);

    if (CIntMath::fastExp(p1, n, r, n) != CIntMath::fastExp(p2, 1, r, n)) return false;
    a++;
  }  

Personal Tools