Kibővített euklideszi algoritmus

Míg az euklideszi algoritmus csak két egész szám, $a$ és $b$ legnagyobb közös osztóját (GCD) számítja ki, addig a kibővített változat megtalálja a módját annak, hogy a GCD-t $a$ és $b$ tekintetében is ábrázolja, azaz. $x$ és $y$ együtthatókat, amelyekre:

$$$a \cdot x + b \cdot y = \gcd(a, b)$$

Fontos megjegyezni, hogy mindig találunk ilyen ábrázolást, például $\gcd(55, 80) = 5$ tehát $5$-t a $55$ és $80$ kifejezések lineáris kombinációjaként tudjuk ábrázolni: A probléma általánosabb formáját a Lineáris diofantikus egyenletek című cikkben tárgyaljuk.Erre az algoritmusra fog épülni.

Algoritmus

Az $a$ és $b$ GCD-jét ebben a részben $g$-vel jelöljük.

Az eredeti algoritmus módosításai nagyon egyszerűek: Ha felidézzük az algoritmust, láthatjuk, hogy az algoritmus $b = 0$ és $a = g$ értékekkel végződik, és ezekre a paraméterekre könnyen találhatunk együtthatókat, mégpedig $g \cdot 1 + 0 \cdot 0 = g$.

Az együtthatókból kiindulva $(x, y) = (1, 0)$, visszafelé haladhatunk a rekurzív hívásokon.Már csak azt kell kitalálnunk, hogy a $(a, b)$-ről $(b, a \bmod b)$-ra való átmenet során hogyan változnak az $x$ és $y$ együtthatók.

Tegyük fel, hogy megtaláltuk a $(x_1, y_1)$ együtthatókat $(b, a \bmod b)$ esetén:

$$b \cdot x_1 + (a \bmod b) \cdot y_1 = g$$

és meg akarjuk találni a $(x, y)$ párt $(a, b)$ esetén:

$$ a \cdot x + b \cdot y = g$$

A $a \bmod b$-t a következőképpen ábrázolhatjuk:

$$ a \bmod b = a – \left\lfloor \frac{a}{b} \right\rfloor \cdot b$$

Ezt a kifejezést behelyettesítve a $(x_1, y_1)$ együttható egyenletébe megkapjuk:

$$ g = b \cdot x_1 + (a \bmod b) \cdot y_1 = b \cdot x_1 + \left(a – \left\lfloor \frac{a}{b} \right\rfloor \cdot b \right) \cdot y_1$$

és a tagok átrendezése után:

$$g = a \cdot y_1 + b \cdot \left( x_1 – y_1 \cdot \left\lfloor \frac{a}{b} \right\rfloor \right)$$

Megtaláltuk $x$ és $y$ értékét:

$$$\begin{cases}x = y_1 \\\\y = x_1 – y_1 \cdot \left\lfloor \frac{a}{b} \right\rfloor\end{cases} $$$

Implementáció

int gcd(int a, int b, int& x, int& y) { if (b == 0) { x = 1; y = 0; return a; } int x1, y1; int d = gcd(b, a % b, x1, y1); x = y1; y = x1 - y1 * (a / b); return d;}

A fenti rekurzív függvény visszaadja a GCD-t és az együtthatók értékét x és y (amelyeket hivatkozással adunk át a függvénynek).

A kiterjesztett euklideszi algoritmus ezen implementációja negatív egész számokra is helyes eredményeket ad.

Iteratív változat

A kiterjesztett euklideszi algoritmust iteratív módon is meg lehet írni.mivel így elkerüljük a rekurziót, a kód egy kicsit gyorsabban fog futni, mint a rekurzív változatban.

int gcd(int a, int b, int& x, int& y) { x = 1, y = 0; int x1 = 0, y1 = 1, a1 = a, b1 = b; while (b1) { int q = a1 / b1; tie(x, x1) = make_tuple(x1, x - q * x1); tie(y, y1) = make_tuple(y1, y - q * y1); tie(a1, b1) = make_tuple(b1, a1 - q * b1); } return a1;}

Ha jobban megnézzük a a1 és b1 változót, észrevehetjük, hogy pontosan ugyanazokat az értékeket veszik fel, mint a normál euklideszi algoritmus iteratív változatában. Tehát az algoritmus legalább a helyes GCD-t kiszámítja.

Azért, hogy az algoritmus miért számítja ki a helyes együtthatókat is, ellenőrizhetjük, hogy a következő invariánsok bármikor (a while ciklus előtt és minden iteráció végén) érvényesek: $x \cdot a + y \cdot b = a_1$ és $x_1 \cdot a + y_1 \cdot b = b_1$.Triviális látni, hogy ez a két egyenlet az elején teljesül, és ellenőrizhetjük, hogy a ciklus iterációjában a frissítés továbbra is fenntartja ezeket az egyenlőségeket.

A végén tudjuk, hogy $a_1$ tartalmazza a GCD-t, tehát $x \cdot a + y \cdot b = g$.Ami azt jelenti, hogy megtaláltuk a szükséges együtthatókat.

A kódot még tovább optimalizálhatjuk, és a $a_1$ és $b_1$ változókat eltávolíthatjuk a kódból, és csak az $a$ és $b$ értékeket használhatjuk újra.Ha azonban így teszünk, elveszítjük a lehetőséget, hogy az invarianciákról vitatkozzunk.

GYakorlati feladatok

  • 10104 – Euklideszi feladat
  • GYM – (J) Egyszer volt, hol nem volt
  • UVA – 12775 – Ajándékdilemma
(c) 2014-2021 translation by http://github.com/e-maxx-eng

Vélemény, hozzászólás?

Az e-mail-címet nem tesszük közzé.