Tak samo jak w przypadku algorytmu Euklidesa algorytm Steina służy do wyliczania NWD (największego wspólnego dzielnika) dla dwóch liczb naturalnych. Jednak proces przyśpieszenia obliczeń pociąga za sobą szereg warunków co czyni algorytm trudny do zapamiętania.
W celu obliczenia NWD algorytmem Steina przyjmuje się, że:
Kroki od (3.) do (5.) należy wykonywać w pętli, aż a = b lub a = 0. Jeśli po wykonaniu któreś operacji liczba a < b to liczby należy zamienić miejscami. W każdym przypadku znaleziona wartość to 2k·b. Oznacza to, że w podanym algorytmie wszystkie dwójki z rozkładu na czynniki pierwsze są zawsze znajdowane i usuwane z liczb.
Punkt (1.) jest kwestią umowną i wygodną podczas obliczeń. Punkt (2.) wynika z faktu, że 0 dzieli każda liczba. Następny podpunkt (3.) to szybkie wyszukiwanie 2 z rozkładu na czynniki pierwsze z obu liczb. W ten sposób liczby bardzo szybko maleją. Na podobnej zasadzie działa punkt (4.), który z kolei usuwa zbędne wartości 2 z rozkładu na czynniki pierwsze liczby parzystej. Warto tu zauważyć, że jeśli jedna liczba jest parzysta, a druga nie to nigdy obie nie będą podzielne przez 2. W ostatnim podpunkcie (5.) ważny jest fakt, że różnica dwóćh liczb nieparzystych a i b jest parzysta, więc po podzieleniu przez dwa nie mają prawa pojawić się liczby z częścią ułamkową.
Spróbujmy obliczyć podanym algorytmem NWD(34, 99).
a | b | Komentarz |
---|---|---|
36 | 99 | b > a, zamiana liczb miejscami |
99 | 36 | a nieparzyste, b parzyste, więc NWD(99, 36) = NWD(99, 36/2) |
99 | 18 | a nieparzyste, b parzyste, więc NWD(99, 18) = NWD(99, 18/2) |
99 | 9 | a i b nieparzyste, więc NWD(99, 9) = NWD(|99 - 9|/2, 9) |
45 | 9 | a i b nieparzyste, więc NWD(45, 9) = NWD(|45 - 9|/2, 9) |
36 | 9 | a parzyste, b nieparzyste, więc NWD(36, 9) = NWD(36/2, 9) |
18 | 9 | a parzyste, b nieparzyste, więc NWD(18, 9) = NWD(18/2, 9) |
9 | 9 | a = b, więc NWD(99, 36) = 9 |
Podany algorytm można napisać w wersji arytmetycznej oraz przy pomocy operacji binarnych. Ze względu na to, że algorytm będzie wykonywał program to w toerii zdecydowanie lepszy pomysłem jest zastosowanie operacji binarnych. Jednak warto poznać oba rozwiązania.
Przestawiony poniżej kod realizuje metodę Steina w wersji iteracyjnej. Oznacza to, że kroki (3. - 5.) listy kroków są wykonywane w pętli, aż do uzyskania wyniku. Na początku (2. - 4.) sprawdzane są warunki początkowe po których jest zapewnione, że a i b są różne od 0.
(5.) Deklarowana zmienna wynik będzie zbierać wartości 2 z rozkładu, które są zabierane z liczb w przypadku, gdy obie są parzyste. (6.) W pętli dopóki nie jest spełniony warunek początkowy to: (7. - 8.) program upewnia się, że a jest większe, równe b. Następnie w zależności od wartości a i b (9. - 19.) wykonywana jest odpowiednia operacja. Na koniec (21.) należy pamiętać, że rozwiązaniem jest a lub b (mają te same wartości) pomnożone przez wartość zmiennej wynik.
Podczas działania algorytmu wykonywane są jedynie operacje sprawdzania czy liczba jest parzysta oraz operacje dzielenia / mnożenia przez 2. Wszystkie te operacje arytmetyczne można zastąpić przesunięciami bitowymi - odpowiednio o 1 w lewo lub prawo.
Pozostaje jeszcze kwestia sprawdzania parzystości liczb. W najgorszym przypadku parzystość każdej z liczb jest sprawdzana dokładnie cztery razy. Proces ten można udoskonalić sprawdzając tylko raz. Przypuśćmy, że 0 oznacza parzysta, a 1 nieparzysta. Znając parzystości liczb tworzymy dwucyfrową liczbę binarną i na jej podstawie wybieramy z jakim przypadkiem mamy do czynienia czyli np. 10 oznaczałaby a nieparzyste i b parzyste.
Po tych zmianach kod ma podobną strukturę, ale nie używa już żadnych operacji arytmetycznych:
W celu przetestowania napisaną funkcję NWD() można skorzystać z poniższej funkcji main():
Napisz rekurencyjną wersję algorytmu Steina wykorzystują do tego:
Sprawdź poprawnosć napisanych funkcji.