Heapsort rekurencja ogonowa
Mariusz:
W pseudokodzie Cormena dla sortowania stogowego
występuje rekurencja ogonowa
Jak się jej pozbyć
Heapify(A,i,heapsize)
l←2i
r←2i+1
if((l≤heapsize)∧(A[l]>A[i]))
then largest←l
else largest←i
if((r≤heapsize)∧(A[r]>A[largest]))
then largest←r
if largest ≠ i
then A[i]↔A[largest]
Heapify(A,largest,heapsize)
BuildHeap(A,length)
for i←[length/2] downto 1
do Heapify(A,i,length)
HeapSort(A,length)
BuildHeap(A,length)
for i←length downto 2
do A[1]↔A[i]
Heapify(A,1,i−1)
11 mar 00:02
jc: Naprawianie kopca z góry w dół. Nie wiem, skąd Cormenowi wzięla się rekurencja.
Zasada jest prosta. pomyśl i napisz sam. Uwaga − wygodnie numerować od 1, a nie od zera.
A poniżej mój kod sprzed lat.
x[ ] − tablica globalna
wdol(int k, int n){
int m = 2*k;
int t = x[k];
while(m <= n){
if ( n > m and x[m] < x[m+1] ) m++;
if ( t >= x[m] ) break;
x[k] = x[m];
k = m;
m = 2*k;
}
x[k] = t;
}
// A tu już sortowanie
for(int i = n/2; i >= 1; i−−) wdol(i,n);
for(int i = n; i >= 2; i−−){
swap(x[1],x[i]);
down(1,i−1);
}
11 mar 00:16
Mariusz:
Ja próbowałem blok z
if largest≠i zastąpić pętlą while
ale wtedy program się nie zatrzymywał
Cormen ogólnie ma dziwne pseudokody
np w przypadku sortowania przez scalanie używa wartowników
11 mar 01:31
jc: Mariusz
Cormen nie potrafi opisywać algorytmów, za to kody podaje poprawne i łatwe do przepisania.
A co do sortowania. Potrzebujesz funkcji naprawiajacej kopiec począwszy od jakiegoś miejsca.
Schodzisz w dół. Kończysz, elementy poniżej są większe, bądź ich w ogóle nie ma.
W przeciwnym wypadku zamieniasz element z miejsca, w którym się znajdujesz z mniejszym
z dwóch poniżej (lub jedynym poniżej). Przechodzisz wraz z zaminianianym elementem niżej.
Jak to mozna zrobić?
Zaczynasz od miejsca wskazanego jako argument funkcji.
Czy jest coś poniżej? Jeśli nie, to koniec.
Czy są dwa elementy poniżej? Jesli tak, to wybierz mniejszy.
Jeśli wybrany element jest większy od elementu, przy którym stoisz, zakończ pracę.
Zamień miejscami wybrany element z tym, przy którym stałeś, przjedź do nowego miejsca.
Napisz, jak Ci się uda poprawnie zapisać.
11 mar 12:47
Mariusz:
Znalazłem pewną propozycję na anglojęzycznym forum chociaż pewnie
można by dać lepsze warunki logiczne bo koleś używa instrukcji łamiącej pętle
Gdybym przeanalizował kod tej rekurencyjnej funkcji Cormena linia po linii
to może sam bym coś wymyślił
12 mar 00:30
Trivial:
Pozbywanie sie rekurencji ogonowej jest bardzo proste. Wystarczy ustawić argumenty i zrobić
goto na początek funkcji, np.:
int tailrec(int x) {
something(x);
if (x < 5) {
return tailrec(x+10);
} else {
return x;
}
}
Zamieniamy na
int tailrec(int x) {
start:
something(x);
if (x < 5) {
x = x + 10;
goto start;
} else {
return x;
}
}
12 mar 08:28
jc: W opisie odwróciłem uporządkowanie kopca. Na górze mam najmniejszy element. W opisanym
sortowaniu potrzebujemy na odwrót.
W procedurze Cormena razi mnie nazwa indeksu: przecież to element ma być większy,
nie indeks!
Czy chcesz zmienić kod Cormena − Trivial napisał, jak to zrobić − czy chcesz zrozumieć
algorytm?
O kopcu dowiedziałem się z książki Perełki oprogramowania Bentleya − na prawdę
z przyjemnością się czyta.
12 mar 10:14
Mariusz:
No tak ale wolałbym aby wyjście z pętli było sterowanie warunkiem logicznym a nie
instrukcją złamania pętli
void heapify(double*A,int i,int heapsize)
{
bool s;
double temp;
s=(i<=heapsize);
while(s)
{
l=2*i;
r=2*r+1;
if((l<=heapsize)&&(A[l]>A[i]))
largest=l;
else largest=i
if((r<=heapsize)&&(A[r]>A[largest]))
largest=r;
if(largest!=i)
{
temp=A[i];
A[i]=A[largest];
A[largest]=temp;
i=largest;
}
else s=false;
}
}
Ostatnio widziałem całkiem ładny kod sortowania przez kopcowanie
chociaż nie przypominał on tego z Cormena
13 mar 01:12
jc: W czym przeszkadza Ci wyskoczenie z pętli? W każdym języku masz odpowiednią instrukcję
(break). Możesz też opusćić procedurę (return). Jak inaczej zakonczysz pracę?
13 mar 01:35
Mariusz:
Warunek logiczny w pętli powinien o tym decydować
Ja tutaj spróbowałem wprowadzić zmienną logiczną do sterowania pętlą
13 mar 02:13
Mariusz:
Z instrukcjami break to nie jest tak jak z przytoczonym przez Triviala goto
13 mar 02:15
jc: A taki kod Ci odpowida? (Twoje nazwy w mojej procedurze)
void heapify(double* A, int i, int heapsize){
int m = 2*i;
double t = A[i];
while(m <= heapsize){
if ( m < heapsize && A[m] < A[m+1] ) m++;
if ( t >= A[m] ) break;
A[i] = A[m];
k = m;
m = 2*i;
}
A[i] = t;
}
13 mar 08:44
jc: W jednym miejscu nie zmieniłem literki; poniżej poprawiony tekst.
void heapify(double* A, int i, int heapsize){
int m = 2*i;
double t = A[i];
while(m <= heapsize){
if ( m < heapsize && A[m] < A[m+1] ) m++;
if ( t >= A[m] ) break;
A[i] = A[m];
i = m;
m = 2*i;
}
A[i] = t;
}
13 mar 08:46
Mariusz:
jc jeśli nie da się lepszego warunku na pętlę dać to niech będzie chociaż ja bym dał
zmienną logiczną do sterowania pętlą w takim przypadku
Co zrobić aby sortował malejąco
13 mar 17:59
jc: Mariusz. Można trochę ładniej zakończyć pętlę, ale z pewną stratą.
Zwróć uwagę na liczbę podstawień: u mnie jest jeden cykl, a w Cormena i u Ciebie złożenie
pewnej liczby transpozycji. W przypadku cyklu o długości n, u mnie jest n+1 podstawień, u
Ciebie 3n.
Przy okazji: nie byłoby ładniej pisać a→b (a wkładamy do b)
zamiast b←a (do b wkładamy a)? lub po prostu b = a.
13 mar 18:58
Mariusz:
Tak uważasz no to może być jakiś argument za twoim kodem
Ostatnio widziałem kod w którym koleś budował kopiec i przywracał własność kopca
w jednej funkcji i pętle miał jakoś ładnie zakończone
Nadal Jakub ma problemy ze stroną więc zapytam o procedurę scalania
przedstawioną u Cormena
Merge(A,p,q,r)
n1←q−p+1
n2←r−q
for i←p to q
do B[i−p+1]←A[i]
for i←q+1 to r
do C[i−q]←A[i]
B[n1+1]←∞
C[n2+1]←∞
i←1
j←1
for k←p to r
do if(B[i]<=C[j])
then A[k]←B[i]
i←i+1
else A[k]←C[j]
j←j+1
MergeSort(A,p,r)
if(p<r)
q←[p+r/2]
MergeSort(A,p,q)
MergeSort(A,q+1,r)
Merge(A,p,q,r)
Po co tutaj Cormen dał wartowników ?
Wydaje mi się że lepiej by było z nich nie korzystać
Podobno tutaj także da się napisać kod bez rekurencji
14 mar 00:14
Laura: ∫x2 * cos( x3 ) dx
14 mar 00:39
Mariusz:
Co do pętli to taka pętla będzie się wykonywać n log n razy
(heapsort i mergesort wykonuje się O(n log n) razy)
for i:=1 to n do
begin
j:=n;
while(j>0) then
begin
(*Tutaj mamy instrukcje wykonywane w pętli
instrukcje te muszą być jednak wykonane w stałym czasie *)
j:=j div 2;
end
end;
14 mar 20:49
14 mar 21:40
Mariusz:
void heapify(double* A, int i, int heapsize){
int m = 2*i;
double t = A[i];
int isheap=0;
while((m <= heapsize)&&(!isheap))
{
if ( m < heapsize && A[m] < A[m+1] ) m++;
if ( t >= A[m] ) isheap=1;
else
{
A[i] = A[m];
i = m;
m = 2*i;
}
}
A[i] = t;
}
Coś takiego mogłoby być ok chociaż na stack overflow znalazłem kod bliższy
pseudokodowi Cormena
Może napisałbyś coś o złożoności czasowej tego kodu oraz o przypadkach brzegowych
(np co jeśli gdzieś w kodzie nastąpi przekroczenie zakresu typu int)
11 gru 13:05