Naszym zadaniem jest zmodyfikować kod z listingu 3...

Linki


» Dzieci to nie książeczki do kolorowania. Nie da się wypełnić ich naszymi ulubionymi kolorami.
»
traci! czas procesora na zbyt cz(ste testy; zbyt d!ugi czas u$pienia b(dzie oznacza!, "e w'tek b(dzie przebywa! w tym stanie nawet po zako&czeniu zadania, na...
»
Tworząc kod obsługi zasad biznesowych dobrą praktyką jest tworzenie funkcji obsługi wszystkich zasad...
»
* Bardzo wyrazistym przykładem wykorzystania zasady skojarzenia na naszym polskim poletku politycznym były wybory do "kontraktowego" parlamentu w 1989 roku, które...
»
218Psychoterapia poznawcza w teorii i w praktycebezradnoci" i zamiast tego stan na wysokoci zadania", mwic o ewentualnym sposobie rozwizania swoich...
»
Kody ramek, paczka (67) KOD-40<table border="0" cellspacing="3" cellpadding="10" width="100%" align="center" bgcolor="#000002"...
»
Potworne to zadanie brodzić w kole, którego środek, używając terminów scholastycznych, jest wszędzie, obwód zaś nigdzie...
»
sprawę, że zgranie załogi to jego zadanie, obojętne jakich musiałby użyć metod...
»
Naszym zdaniem ocena w opisywanym zakresie powinna by dostosowana do potrzeb poszczeglnych pacjentw...
»
Wspominamy o tym wszystkim, aby podkreślić zmienne oddziaływanie nauki i duchowości w naszym życiu...
»
Podczas rozmowy mieliśmy możliwość przyjrzenia się bliżej naszym gospodarzom...

Dzieci to nie książeczki do kolorowania. Nie da się wypełnić ich naszymi ulubionymi kolorami.

1 przedstawiającego randomizujący kod Quicksort, aby drogą analizy sprawdzał średnią liczbę porównań potrzebnych do posortowania tablicy zawierającej unikatowe elementy. Spróbujemy też uzyskać jak najwięcej przy użyciu jak naj-mniejszej ilości kodu, czasu i miejsca.
Aby określić średnią liczbę porównań, najpierw rozszerzymy funkcjonalność programu o możli-wość ich zliczania. W tym celu inkrementujemy zmienną comps przed porównaniem w wewnętrznej pętli (listing 3.2).
2 J. Bentley, M. D. McIlroy, Engineering a sort function, „Software-Practice and Experience”, Vol. 23, No. 11 — przyp. red.
N A J P I Ę K N I E J S Z Y K O D , K T Ó R E G O N I G D Y N I E N A P I S A Ł E M
4 9
L I S T I N G 3 . 2 . Wewnętrzna pętla algorytmu Quicksort przystosowana do zliczania porównań for (i = l+1; i <= u; i++) {
comps++;
if (x[i] < x[l])
swap(++m, i);
}
Jeśli uruchomimy program tylko dla jednego n, dowiemy się, ile porównań to jedno uruchomienie potrzebuje. Jeśli powtórzymy tę operację wielokrotnie dla wielu wartości n i przeprowadzimy statystyczną analizę wyników, uzyskamy wartość średnią. Algorytm Quicksort potrzebuje około 1,4 n × l g ( n ) porównań do posortowania n elementów.
Nie jest to wcale zły sposób na uzyskanie wglądu w działanie programu. Dzięki 13 wierszom kodu i kilku eksperymentom można sporo odkryć. Znane powiedzenie przypisywane pisarzom takim jak Blaise Pascal i T. S. Eliot brzmi: „Gdybym miał więcej czasu, napisałbym Ci krótszy list”. My mamy czas, więc poeksperymentujemy trochę z kodem, aby napisać krótszy (i lepszy) program.
Zagramy w przyspieszanie eksperymentu, próbując zwiększyć statystyczną dokładność i wgląd w dzia-
łanie programu. Jako że wewnętrzna pętla wykonuje dokładnie u-l porównań, możemy nieco przy-spieszyć działanie programu, zliczając te porównania za pomocą pojedynczej operacji poza pętlą.
Po tej zmianie algorytm Quicksort wygląda jak na listingu 3.3.
L I S T I N G 3 . 3 . Algorytm Quicksort po przeniesieniu inkrementacji na zewnątrz pętli comps += u-l;
for (i = l+1; i <= u; i++)
if (x[i] < x[l])
swap(++m, i);
Program ten sortuje tablicę i jednocześnie sprawdza liczbę potrzebnych porównań. Jeśli jednak na-szym celem jest tylko zliczenie porównań, nie musimy sortować tablicy. Na listingu 3.4 zostało usunięte prawdziwe sortowanie i pozostał tylko szkielet różnych wywołań wykonywanych przez program.
L I S T I N G 3 . 4 . Szkielet algorytmu Quicksort zredukowany do zliczania void quickcount(int l, int u)
{ int m;
if (l >= u) return;
m = randint(l, u);
comps += u-l;
quickcount(l, m-1);
quickcount(m+1, u);
}
Program ten działa dzięki losowemu wybieraniu przez Quicksort elementu dzielącego i dzięki za-
łożeniu, że wszystkie elementy są unikatowe. Jest on wykonywany w czasie proporcjonalnym do n.
Podczas gdy program z listingu 3.3 wymagał proporcjonalnej do n ilości miejsca, teraz została ona zredukowana do stosu rekurencji, który średnio jest proporcjonalny do l g ( n ) .
5 0
R O Z D Z I A Ł 3 .
Mimo że indeksy (l i u) tablicy są niezbędne w prawdziwym programie, w tej wersji szkieletu nie mają znaczenia. Można je zastąpić jedną liczbą całkowitą (n), która będzie określała rozmiar podtablicy do posortowania (listing 3.5).
L I S T I N G 3 . 5 . Szkielet algorytmu Quicksort z jednym argumentem określającym rozmiar void qc(int n)
{ int m;
if (n <= 1) return;
m = randint(1, n);
comps += n-1;
qc(m-1);
qc(n-m);
}
Bardziej naturalne teraz będzie przetworzenie tej procedury do postaci funkcji zliczającej porównania (ang. comparison count — cc), która zwraca liczbę porównań użytych przez jedno wykona-nie algorytmu Quicksort. Funkcję tę przedstawia listing 3.6.
L I S T I N G 3 . 6 . Szkielet algorytmu Quicksort zaimplementowany jako funkcja int cc(int n)
{ int m;
if (n <= 1) return 0;
m = randint(1, n);
return n-1 + cc(m-1) + cc(n-m);
}
Przykłady zamieszczone na listingach 3.4, 3.5 i 3.6 rozwiązują ten sam podstawowy problem i potrzebują na to tyle samo czasu i pamięci. Każda kolejna wersja ma poprawioną formę, dzięki czemu jest nieco bardziej przejrzysta i zwięzła od poprzedniej.
Definiując paradoks wynalazcy (ang. inventor’s paradox), George Pólya oznajmia, że: „Bardziej ambitny plan może mieć więcej szans na powodzenie”3. Spróbujemy teraz wykorzystać ten paradoks w analizie Quicksort. Do tej pory zadawaliśmy sobie pytanie, ile porównań potrzebuje algorytm Quicksort do posortowania tablicy zawierającej n elementów. Teraz zadamy bardziej ambitne pytanie: ile średnio porównań potrzebuje algorytm Quicksort do posortowania losowej tablicy o rozmiarze n? Możemy rozszerzyć kod z listingu 3.6, aby uzyskać pseudokod widoczny na listingu 3.7.
L I S T I N G 3 . 7 . Średnia liczba porównań algorytmu Quicksort jako pseudokod float c(int n)
if (n <= 1) return 0
sum = 0
for (m = 1; m <= n; m++)
sum += n-1 + c(m-1) + c(n-m)
return sum/n
Jeśli dane wejściowe zawierają maksymalnie jeden element, Quicksort nie wykonuje żadnych porów-nań, jak w przykładzie z listingu 3.6. W przypadku n o większej wartości kod ten bierze pod uwagę każ-
dą wartość dzielącą (od pierwszego do ostatniego elementu — każdy jest równie prawdopodobny) 3 George Pólya, How to solve it, Princeton University Press, 1945 — przyp. red.
N A J P I Ę K N I E J S Z Y K O D , K T Ó R E G O N I G D Y N I E N A P I S A Ł E M
5 1

Powered by MyScript