Mam trudności z podjęciem decyzji, jaka jest złożoność czasowa największego wspólnego algorytmu mianownika Euclid. Ten algorytm w pseudokodzie to:
function gcd(a, b)
while b ≠ 0
t := b
b := a mod b
a := t
return a
Wydaje się, że zależy to od a i b . Myślę, że złożoność czasowa wynosi O (a% b). Czy to jest poprawne? Czy jest lepszy sposób, aby to napisać?
algorithm
big-o
time-complexity
iteration
Donald Taylor
źródło
źródło
a%b
. W najgorszym przypadku sąa
ib
są to kolejne liczby Fibonacciego.Odpowiedzi:
Jedna sztuczka do analizy złożoności czasowej algorytmu Euclid polega na śledzeniu tego, co dzieje się w dwóch iteracjach:
Teraz a i b będą się zmniejszać, a nie tylko jeden, co ułatwia analizę. Możesz podzielić to na przypadki:
2a <= b
2b <= a
2a > b
alea < b
2b > a
aleb < a
a == b
Teraz pokażemy, że każdy pojedynczy przypadek zmniejsza sumę
a+b
o co najmniej jedną czwartą:b % (a % b) < a
i2a <= b
takb
jest zmniejszona o co najmniej połowę, a więca+b
zmniejszona co najmniej o25%
a % b < b
i2b <= a
taka
jest zmniejszone o co najmniej połowę, a więca+b
zmniejszone co najmniej o25%
b
stanie sięb-a
mniejsze niżb/2
, zmniejszająca+b
się co najmniej o25%
.a
stanie sięa-b
mniejsze niża/2
, zmniejszająca+b
się co najmniej o25%
.a+b
spada do0
, co oczywiście zmniejszaa+b
się co najmniej o25%
.Dlatego w analizie przypadku każdy podwójny krok zmniejsza
a+b
się co najmniej o25%
. Istnieje maksymalna liczba przypadków, w których może się to zdarzyć, zanima+b
zostanie zmuszony do zejścia poniżej1
. Całkowita liczba kroków (S
) do osiągnięcia 0 musi spełnić(4/3)^S <= A+B
. Teraz po prostu pracuj:Tak więc liczba iteracji jest liniowa względem liczby cyfr wejściowych. W przypadku liczb mieszczących się w rejestrach procesora rozsądne jest modelowanie iteracji jako zajmujących stały czas i udawanie, że całkowity czas działania gcd jest liniowy.
Oczywiście, jeśli masz do czynienia z dużymi liczbami całkowitymi, musisz wziąć pod uwagę fakt, że operacje modułu w każdej iteracji nie mają stałego kosztu. Z grubsza rzecz biorąc, całkowity asymptotyczny czas działania będzie n ^ 2 razy większy niż współczynnik polilogarytmiczny. Coś jak
n^2 lg(n) 2^O(log* n)
. Można uniknąć współczynnika polilogarytmicznego, używając zamiast tego binarnego gcd .źródło
x % y
nie może być więcej niżx
i musi być mniejsze niży
. Taka % b
jest co najwyżeja
, zmuszanieb % (a%b)
do bycia poniżej czegoś, co jest najwyżej,a
a zatem ogólnie jest mniejsze niża
.Właściwym sposobem analizy algorytmu jest określenie jego najgorszych scenariuszy. Najgorszy przypadek Euklidesa GCD ma miejsce, gdy w grę wchodzą pary Fibonacciego.
void EGCD(fib[i], fib[i - 1])
, gdzie i> 0.Na przykład wybierzmy przypadek, w którym dywidenda wynosi 55, a dzielnik 34 (przypomnijmy, że nadal mamy do czynienia z liczbami Fibonacciego).
Jak możesz zauważyć, ta operacja kosztowała 8 iteracji (lub wywołań rekurencyjnych).
Wypróbujmy większe liczby Fibonacciego, a mianowicie 121393 i 75025. Możemy również zauważyć, że zajęło to 24 iteracje (lub wywołania rekurencyjne).
Możesz również zauważyć, że każda iteracja daje liczbę Fibonacciego. Dlatego mamy tak wiele operacji. Nie możemy uzyskać podobnych wyników tylko z liczbami Fibonacciego.
Stąd złożoność czasowa będzie tym razem reprezentowana przez małe Oh (górna granica). Dolna granica jest intuicyjnie Omega (1): na przykład przypadek 500 podzielony przez 2.
Rozwiążmy relację powtarzania:
Można więc powiedzieć, że Euclidean GCD może co najwyżej wykonać operację log (xy) .
źródło
Świetne spojrzenie na to w artykule na Wikipedii .
Ma nawet ładny wykres złożoności dla par wartości.
Nie jest
O(a%b)
.Wiadomo (patrz artykuł), że nigdy nie wykona więcej kroków niż pięciokrotność liczby cyfr w mniejszej liczbie. Zatem maksymalna liczba kroków rośnie wraz z liczbą cyfr
(ln b)
. Koszt każdego kroku również rośnie wraz z liczbą cyfr, więc złożoność jest ograniczona przezO(ln^2 b)
gdzie b jest mniejszą liczbą. To górna granica, a rzeczywisty czas jest zwykle krótszy.źródło
n
oznacza?n = ln b
. Jaka jest regularna złożoność modułu dla dużych int? Czy to O (log n log ^ 2 log n)Zobacz tutaj .
W szczególności ta część:
Więc
O(log min(a, b))
jest to dobra górna granica.źródło
Oto intuicyjne zrozumienie złożoności algorytmu Euclid w czasie wykonywania. Formalne dowody są omówione w różnych tekstach, takich jak Wprowadzenie do algorytmów i TAOCP tom 2.
Najpierw zastanów się, co by było, gdybyśmy spróbowali wziąć gcd z dwóch liczb Fibonacciego F (k + 1) i F (k). Możesz szybko zauważyć, że algorytm Euclid iteruje na F (k) i F (k-1). Oznacza to, że z każdą iteracją przesuwamy się o jedną liczbę w dół w szeregu Fibonacciego. Ponieważ liczby Fibonacciego to O (Phi ^ k), gdzie Phi to złoty współczynnik, możemy zobaczyć, że czas wykonania GCD wynosił O (log n), gdzie n = max (a, b), a log ma podstawę Phi. Następnie możemy udowodnić, że byłby to najgorszy przypadek, obserwując, że liczby Fibonacciego konsekwentnie tworzą pary, w których reszty pozostają wystarczająco duże w każdej iteracji i nigdy nie osiągają zera, dopóki nie dojdziesz do początku szeregu.
Możemy sprawić, że O (log n), gdzie n = max (a, b) związane będzie jeszcze mocniej. Załóżmy, że b> = a, więc możemy zapisać ograniczenie w O (log b). Najpierw zauważ, że GCD (ka, kb) = GCD (a, b). Ponieważ największe wartości k to gcd (a, c), możemy zamienić b na b / gcd (a, b) w naszym środowisku wykonawczym, prowadząc do ściślejszego ograniczenia O (log b / gcd (a, b)).
źródło
Najgorszym przypadkiem algorytmu Euclid jest sytuacja, w której reszty są największe z możliwych na każdym kroku, tj. dla dwóch kolejnych wyrazów ciągu Fibonacciego.
Gdy n i m są liczbą cyfr a i b, zakładając n> = m, algorytm wykorzystuje O (m) działek.
Zwróć uwagę, że złożoność jest zawsze podawana w postaci rozmiarów danych wejściowych, w tym przypadku liczby cyfr.
źródło
Najgorszy przypadek pojawi się, gdy n i m będą kolejnymi liczbami Fibonacciego.
gcd (Fn, Fn − 1) = gcd (Fn − 1, Fn − 2) = ⋯ = gcd (F1, F0) = 1, a n-ta liczba Fibonacciego to 1,618 ^ n, gdzie 1,618 to złoty współczynnik.
Tak więc, aby znaleźć gcd (n, m), liczba wywołań rekurencyjnych będzie wynosić Θ (logn).
źródło
Oto analiza w książce Data Structures and Algorithm Analysis in C autorstwa Marka Allena Weissa (drugie wydanie, 2.4.4):
Oto kod:
Oto TEOREM , którego zamierzamy użyć:
DOWÓD:
Możemy więc wyciągnąć następujące wnioski:
źródło
Twierdzenie Gabriela Lame'a ogranicza liczbę kroków przez log (1 / sqrt (5) * (a + 1/2)) - 2, gdzie podstawą logu jest (1 + sqrt (5)) / 2. Dotyczy to najgorszego scenariusza dla algorytmu i występuje, gdy wejściami są kolejne liczby Fibanocciego.
Nieco bardziej liberalne ograniczenie to: log a, gdzie podstawą logu jest (sqrt (2)) wynika z Koblitz.
Do celów kryptograficznych zwykle bierzemy pod uwagę złożoność bitową algorytmów, biorąc pod uwagę, że rozmiar bitu jest określony w przybliżeniu przez k = loga.
Oto szczegółowa analiza złożoności bitowej algorytmu Euclid:
Chociaż w większości odniesień bitowa złożoność algorytmu Euclid jest określona przez O (loga) ^ 3, istnieje ściślejsza granica, która jest O (loga) ^ 2.
Rozważać; r0 = a, r1 = b, r0 = q1. r1 + r2. . . , ri-1 = qi.ri + ri + 1,. . . , rm-2 = qm-1.rm-1 + rm rm-1 = qm.rm
zauważ, że: a = r0> = b = r1> r2> r3 ...> rm-1> rm> 0 .......... (1)
a rm jest największym wspólnym dzielnikiem a i b.
Stwierdzeniem w książce Koblitza (Kurs teorii liczb i kryptografii) można udowodnić, że: ri + 1 <(ri-1) / 2 ................. ( 2)
Ponownie w Koblitz liczba operacji bitowych wymaganych do podzielenia k-bitowej liczby całkowitej dodatniej przez l-bitową liczbę całkowitą dodatnią (zakładając k> = l) jest podana jako: (k-l + 1). L ...... ............. (3)
Przy (1) i (2) liczba podziałów wynosi O (loga), a więc przy (3) całkowita złożoność wynosi O (loga) ^ 3.
Teraz można to zredukować do O (loga) ^ 2 przez uwagę w Koblitz.
rozważ ki = logri +1
przez (1) i (2) mamy: ki + 1 <= ki dla i = 0,1, ..., m-2, m-1 i ki + 2 <= (ki) -1 dla i = 0 , 1, ..., m-2
a przez (3) całkowity koszt m działek jest ograniczony przez: SUMA [(ki-1) - ((ki) -1))] * ki dla i = 0,1,2, .., m
przekształcając to: SUMA [(ki-1) - ((ki) -1))] * ki <= 4 * k0 ^ 2
Więc bitowa złożoność Algorytmu Euklidesa wynosi O (loga) ^ 2.
źródło
Jednak dla algorytmu iteracyjnego mamy:
W przypadku par Fibonacciego nie ma różnicy między
iterativeEGCD()
iiterativeEGCDForWorstCase()
gdzie to ostatnie wygląda następująco:Tak, z parami Fibonacciego
n = a % n
in = a - n
to jest dokładnie to samo.Wiemy również, że we wcześniejszej odpowiedzi na to samo pytanie, jest tam panujące czynnikiem zmniejszającym:
factor = m / (n % m)
.Dlatego, aby ukształtować iteracyjną wersję euklidesowego GCD w zdefiniowanej formie, możemy przedstawić jako „symulator” w następujący sposób:
Opierając się na pracy (ostatni slajd) dr Jauhar Ali, powyższa pętla jest logarytmiczna.
Tak, mały Oh, ponieważ symulator mówi co najwyżej liczbę iteracji . Pary inne niż Fibonacciego wymagałyby mniejszej liczby iteracji niż Fibonacci, gdy są badane na Euklidesowym GCD.
źródło
Na każdym kroku są dwa przypadki
b> = a / 2, a następnie a, b = b, a% b da b co najwyżej połowę swojej poprzedniej wartości
b <a / 2, to a, b = b, a% b będzie stanowić co najwyżej połowę swojej poprzedniej wartości, ponieważ b jest mniejsze niż a / 2
Tak więc na każdym kroku algorytm zmniejszy co najmniej jedną liczbę do co najmniej połowy mniej.
W co najwyżej kroku O (log a) + O (log b) zostanie to zredukowane do prostych przypadków. Co daje algorytm O (log n), gdzie n jest górną granicą a i b.
Znalazłem to tutaj
źródło