i = 1
Transkrypt
i = 1
Poszukiwanie liniowe wśród liczb naturalnych Wiele problemów, dotyczących liczb naturalnych, można rozwiązać idąc w górę od zera (lub czasem od innej liczby) i sprawdzając, czy już. Przykład: (zadane reszty z dzielenia) M Dane są liczby p > q 0 oraz s > r 0; p i s są względnie pierwsze, czyli nie mają wspólnych dzielników większych niż 1. Znaleźć najmniejszą liczbę n > 0, która 1. w dzieleniu przez p daje resztę q, 2. w dzieleniu przez s daje resztę r. Niech n przebiega po kolei wszystkie liczby postaci 0 · p + q, 1 · p + q, 2 · p + q, ... (war. 1), aż natrafi na taką, która spełnia war. 2. Wykład 3. POSZUKIWANIE I TABLICE, str. 2 Poszukiwanie liniowe wśród liczb naturalnych /* p > q 0 & s > r 0 */ n=q; /* n%p = q */ while (n%s != r) { n=n+p; } /* n%p = q & n%s = r */ p q s r 16 11 9 7 p%k — w języku C: reszta z dzielenia p przez k To jest szczególny przypadek „chińskiego twierdzenia o resztach” n 11 27 43 Poszukiwanie liniowe wśród liczb naturalnych Przykład: (liczby pierwsze) M Dane jest n 1. Znaleźć najmniejszą liczbę pierwszą większą niż n. Liczba p jest pierwsza, jeśli nie dzieli się przez żadną liczbę k spełniającą 2 ¬ k < p. Niech p oznacza zawsze „kandydata” na poszukiwaną liczbę pierwszą; zaczniemy od p = n + 1 i będziemy sprawdzać jej podzielność przez liczby k od 2 do p − 1. • Jeśli znajdziemy dzielnik k, to zwiększymy kandydata p o 1 i zaczniemy sprawdzanie znowu od 2. • Jeśli dojdziemy z k do p − 1 i po drodze nie natrafimy na dzielnik p, to p jest liczbą pierwszą. Wykład 3. POSZUKIWANIE I TABLICE, str. 4 Poszukiwanie liniowe wśród liczb naturalnych /* n 1 */ p=n+1; k=2; /* p > n 1 & k ¬ p p nie dzieli się przez i ∈ [2 . . k − 1] */ while (k<p) { if (p%k == 0) { /* p jest złym kandydatem */ p=p+1; k=2; } else /* trzeba sprawdzać dalej */ k=k+1; } /* p > n 1 & p nie dzieli się przez i ∈ [2 . . p − 1] */ p%k — w C: reszta z dzielenia p przez k n 8 p 9 k 2 3 10 2 11 2 3 4 5 6 7 8 9 10 11 Tablice czyli zmienne indeksowane W językach programowania występują tablice — zmienne indeksowane reprezentujące skończone ciągi liczbowe. a: a: a0 = 15 0 15 a1 = 0 1 0 a2 = −24 2 -24 • zamiast ai piszemy a[i], a3 = 66 3 66 • zamiast a2·i−1 piszemy a[2*i-1]. a4 = 0 4 0 Indeksami są dowolne wyrażenie całkowite, w programie ujmuje się je w nawiasy kwadratowe, np. W C indeksowanie tablicy biegnie zawsze od zera; t.zn. tablica n-elementowa a zawiera elementy a[0], a[1], a[2], . . . , a[n-1] Element a[n] nie istnieje. Wykład 3. POSZUKIWANIE I TABLICE, str. 6 Tablice czyli zmienne indeksowane Typowy program z tablicami w C: nagłówek deklaracje wejście (w pętli) obliczenie wyjście (w pętli) - main() { -( int i, a[100]; for (i=0; i<100; i=i+1) scanf ("%i", &a[i]); . . . . . . . . . . . . . . . - . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ( for (i=0; i<100; i=i+1) printf ("a[%i] == %i\n", i, } - . . . a[i]); Iteracja for for (i=A; i<B; i=i+1) C ? i←A - • iterację for można wyrazić przy pomocy while-a: i=A; while (i<B) { C i=i+1; } ? i<B | iB ? C • granice A i B oraz zmienna sterująca i prawidłowo skonstruowanej iteracji nie powinny podlegać zmianie w ciele iteracji C; ? i←i+1 • iteracja for nadaje się idealnie do przeglądania tablicy; ale nie tylko do tego. ? Wykład 3. POSZUKIWANIE I TABLICE, str. 8 Tablice czyli zmienne indeksowane Przykład: (odwracanie ciągu liczb) M Żeby wczytać ciąg n liczb i napisać go od końca, trzeba jego n wyrazów czasowo zapamiętać; można to zrobić na przykład w tablicy: for (i=0; i<n; i=i+1) scanf("%i", &a[i]); for (i=n-1; i>=0; i=i-1) printf (" %i ", a[i]); w 11 2 e j ś c −16 i 0 11 1 2 e 2 4 0 w −16 3 4 4 0 0 4 y j ś c −16 i e 2 11 Tablice czyli zmienne indeksowane Przykład: (maksimum) M Znaleźć największą liczbę w ciągu N -elementowym, którego wszystkie wyrazy są nieujemne: ai 0 dla i ∈ [0 . . . N − 1] . max = 0; for (n=0; n<N; n=n+1) if (max < a[n]) max = a[n]; Samodzielny program, wczytujący liczby do tablicy, obliczający maksimum i drukujący, nie ma wiele sensu. Liczyć maksimum można „w locie”, niczego nie zapamiętując w tablicy. Jednak powyższy fragment programu, liczący maksimum tablicy, można wykorzystać jako fragment większej całości. Wykład 3. POSZUKIWANIE I TABLICE, str. 10 Sortowanie W tablicy a długości n zapisane są liczby. Należy je tak poprzestawiać, żeby a0 ¬ a1 ¬ . . . ¬ an−1 . Sortowanie tablicy a[0 . . n − 1] przez wybór maksimum: 1. znaleźć takie k ∈ [0 . . n − 1], że ak jest maksimum tablicy a[0 . . n − 1] 2. zamienić ak z an−1 3. posortować krótszą tablicę a[0 . . n − 2] for (i=n; i>=2; i=i-1) { znaleźć takie k ∈ [0 . . i − 1],że ∀p∈[0 . . i−1] ap ¬ ak ; zamienić ak z ai−1 ; } Sortowanie 0 1 2 3 4 5 15 −7 23 −7 11 0 i=6 MAX OSTATNI for (i=n; i>=2; i=i-1) { znaleźć takie k ∈ [0 . . i − 1], że ∀p∈[0 . . i−1] ap ¬ ak ; zamienić ak z ai−1 ; } Wykład 3. POSZUKIWANIE I TABLICE, str. 12 Sortowanie 0 1 2 3 4 5 −7 −7 0 11 15 23 i=1 for (i=n; i>=2; i=i-1) { znaleźć takie k ∈ [0 . . i − 1], że ∀p∈[0 . . i−1] ap ¬ ak ; zamienić ak z ai−1 ; } Jak znajdować takie k, żeby ak było maksymalne? — to już wiemy. Jak zamieniać? Sortowanie Jak zamieniać a[k] z a[i-1]? a[k] Przypisanie: ∗ a[i-1] ∗ ŹLE! a[k] = a[i-1]; Wykład 3. POSZUKIWANIE I TABLICE, str. 14 Sortowanie Jak zamieniać a[k] z a[i-1]? a[k] a[i-1] 2 3 1 U pomocnicza Trzy kroki: 1 pomocnicza = a[k]; 2 a[k] = a[i-1]; 3 a[i-1] = pomocnicza; Sortowanie for (i=n; i>=2; i=i-1) { k=0; for (j=1; j<i; j=j+1) if (a[k]<a[j]) k=j; x=a[k]; a[k]=a[i-1]; a[i-1]=x; } znajdowanie maksimum zamiana Ile zamian? W każdym obiegu zewnętrznej pętli: jedna. Więc razem: n − 1. Ile porównań elementów tablicy? W każdym i-tym obiegu zewnętrznej pętli: i − 1. n X n2 n(n − 1) n (i − 1) = = − Więc razem: 2 2 2 i=2 Wykład 3. POSZUKIWANIE I TABLICE, str. 16 Sortowanie Istnieje wiele różnych algorytmów sortowania tablicy, różniących się: • czasem działania w zależności od długości tablicy (w sortowaniu przez maksimum: około n2 ), • zajętością dodatkowej pamięci (w sortowaniu przez maksimum: w miejscu), • „długością” zamian (sortowanie przez maksimum: n − 1), • możliwościami zrównoleglenia działań przy użyciu większej liczby procesorów, • itp. ISTNIEJĄ ZNACZNIE SZYBSZE ALGORYTMY SORTOWANIA Niebezpieczeństwa związane z tablicami Wyjście indeksu poza zakres Znaleźć pierwsze zero w tablicy a[0 . . n-1]: i=0; while (a[i] != 0 && i<n) i=i+1; i=0; while (i<n && a[i] != 0) i=i+1; — jeśli w a[0 . . n-1] nie ma wcale zera, to pętla po lewej nie zatrzyma się na czas i sięgnie po nieistniejący element a[n]. W C można temu zapobiec przez odwrócenie koniunkcji, jak w pętli po prawej. Koniunkcja w C nie jest symetryczna, najpierw liczy się lewy argument. W innych językach bywa różnie. . . & błąd fałsz prawda błąd błąd fałsz błąd fałsz błąd fałsz fałsz prawda błąd fałsz prawda Wykład 3. POSZUKIWANIE I TABLICE, str. 18 Niebezpieczeństwa związane z tablicami Niepewna tożsamość zmiennych Tablica a[0 . . n-1]. Czy te instrukcje for (i=0; i<n; i=i+2) a[i]=0; for (i=n-1; i>=0; i=i-2) a[i]=0; zerują jej wszystkie pola? Jeśli n jest parzyste, tak. Jeśli n jest nieparzyste, nie. Różne zmienne mogą w programie wyglądać tak samo; np. a[i] oznacza różne zmienne, zależnie od wartości i. Ta sama zmienna może w różnych miejscach programu wyglądać różnie; np. a[i+1] i a[j-1] to ta sama zmienna, jeśli j = i + 2. Poszukiwanie liniowe Załóżmy, że tablica a[0 . . n − 1] jest wypełniona liczbami; i mamy daną liczbę x. Wyszukać x w tablicy a; t.zn. znaleźć takie p, że a[p] = x, lub upewnić się, że takie p nie istnieje (czyli x nie występuje w tablicy a). #define FALSE 0 #define TRUE 1 .......... // n 0 znal=FALSE; p=0; // 0 ¬ p ¬ n & // (znal & a[p] = x) ∨ (¬znal & w a[0 . . p − 1] nie ma x) while (!znal && p<n) if (a[p] == x) znal=TRUE; else p=p+1; // (znal & a[p] = x) ∨ (¬znal & w a[0 . . n − 1] nie ma x) Wykład 3. POSZUKIWANIE I TABLICE, str. 20 Metody łapania lwa na pustyni Fizyczna: Przesypać całą pustynię przez sito. To, co przeleci, to piasek; to, co zostanie w sicie, to lew. Matematyczna: Zamknąć się w klatce; następnie dokonać inwersji przestrzeni względem klatki: my znajdziemy się na zewnątrz a lew w klatce. Uwaga! nie stać w środku klatki, bo przy inwersji to miejsce przechodzi na punkt w nieskończoności. Informatyczna: Podzielić pustynię na pół; połowę, w której jest lew, znowu na pół; ćwiartkę, w której jest lew, na pół. . . Tak postępować, aż rozpatrywany fragment pustyni zmaleje do rozmiaru podstawy klatki. Na tym fragmencie pustyni postawić klatkę. Poszukiwanie binarne Zadanie: policzyć b = √ √ 5 10 z dokładnością do 0.1 (czyli |b − 5 10| < 0.1). a = 1 jest za małe, bo 15 = 1 ¬ 10 ; c = 2 jest za duże, bo 10 < 32 = 25 . a5 ¬ 10 < c5 abc 1.000 1.125 1.250 a 1.000 1.375 b= a+c 2 1.500 1.625 c 2.000 b5 b5 ¬ 10 7.594 TAK 16.413 NIE 11.331 NIE 9.313 TAK 10.283 NIE 1.500 1.500 1.750 1.875 2.000 2.000 1.750 1.500 1.750 1.625 1.500 1.625 1.563 1.563 1.625 1.594 Wykład 3. POSZUKIWANIE I TABLICE, str. 22 Poszukiwanie binarne Zadanie: n > 0 i ε > 0√ √ policzyć b = 5 n z dokładnością do ε (czyli |b − 5 n| < ε). a=0; b=n/2; c=n; // a5 ¬ n < c5 & b = while (c-a >= ε) { if (b5 <= n) a=b; else c=b; b = (a+c)/2; } a+b 2 Poszukiwanie binarne Załóżmy, że tablica a[0 . . n − 1] jest wypełniona liczbami i uporządkowana; i mamy daną liczbę x. Wyszukać x w tablicy a. // n > 0 if (x < a[0]) znal=FALSE; else { // n > 0 & a[0] ¬ x < a[n] = +∞ p=0; q=n; // 0 ¬ p < q ¬ n & a[p] ¬ x < a[q] while (q-p > 1) { r=(p+q)/2; if (x < a[r]) q=r; else p=r; } // a[p] ¬ x < a[p + 1] if (x == a[p]) znal=TRUE; else znal=FALSE; } // (znal & a[p] = x) ∨ (¬znal & w a[0 . . n − 1] nie ma x)