<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="pl">
	<id>http://brain.fuw.edu.pl/edu/index.php?action=history&amp;feed=atom&amp;title=TI%2FAlgorytmy_i_struktury_danych%2FZ%C5%82o%C5%BCono%C5%9B%C4%87_obliczeniowa_i_pami%C4%99ciowa</id>
	<title>TI/Algorytmy i struktury danych/Złożoność obliczeniowa i pamięciowa - Historia wersji</title>
	<link rel="self" type="application/atom+xml" href="http://brain.fuw.edu.pl/edu/index.php?action=history&amp;feed=atom&amp;title=TI%2FAlgorytmy_i_struktury_danych%2FZ%C5%82o%C5%BCono%C5%9B%C4%87_obliczeniowa_i_pami%C4%99ciowa"/>
	<link rel="alternate" type="text/html" href="http://brain.fuw.edu.pl/edu/index.php?title=TI/Algorytmy_i_struktury_danych/Z%C5%82o%C5%BCono%C5%9B%C4%87_obliczeniowa_i_pami%C4%99ciowa&amp;action=history"/>
	<updated>2026-05-03T16:24:27Z</updated>
	<subtitle>Historia wersji tej strony wiki</subtitle>
	<generator>MediaWiki 1.34.1</generator>
	<entry>
		<id>http://brain.fuw.edu.pl/edu/index.php?title=TI/Algorytmy_i_struktury_danych/Z%C5%82o%C5%BCono%C5%9B%C4%87_obliczeniowa_i_pami%C4%99ciowa&amp;diff=1938&amp;oldid=prev</id>
		<title>Jarekz: Utworzono nową stronę &quot; &lt;!--= szkic = * '''trochę o tym, jak taki program będzie się wykonywał w uproszczony sposób: zaujmuje jakieś miejsce w RAM oraz wykonuje różne instreukcje po ko...&quot;</title>
		<link rel="alternate" type="text/html" href="http://brain.fuw.edu.pl/edu/index.php?title=TI/Algorytmy_i_struktury_danych/Z%C5%82o%C5%BCono%C5%9B%C4%87_obliczeniowa_i_pami%C4%99ciowa&amp;diff=1938&amp;oldid=prev"/>
		<updated>2015-05-23T13:51:28Z</updated>

		<summary type="html">&lt;p&gt;Utworzono nową stronę &amp;quot; &amp;lt;!--= szkic = * &amp;#039;&amp;#039;&amp;#039;trochę o tym, jak taki program będzie się wykonywał w uproszczony sposób: zaujmuje jakieś miejsce w RAM oraz wykonuje różne instreukcje po ko...&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Nowa strona&lt;/b&gt;&lt;/p&gt;&lt;div&gt;&lt;br /&gt;
&amp;lt;!--= szkic =&lt;br /&gt;
* '''trochę o tym, jak taki program będzie się wykonywał w uproszczony sposób: zaujmuje jakieś miejsce w RAM oraz wykonuje różne instreukcje po kolei'''&lt;br /&gt;
* '''różne &amp;quot;linijki&amp;quot; programu trwają jakąś jednostkę czasu. zakładamy, ze każda instrukcja trwa taką samą. Kilka przykładów w którcych studenci mają wskazać co jest instrukcjami które będziemy zliczać. Bardzo prostych -- w czasie stałycm albo liniowym. Niech będzie choćby wyszukiwanie . NA razie obliczanie ile to zajmie w przypadku jakichś koonkretnych danych wejściowych'''&lt;br /&gt;
* koszt obliczeniowy pesymistyczny i oczekiwany. Wskazywanie najbardziej pechowych danych wejściowych. Zgadywanie jaki rozkład mogą mieć dane wejściowe.&lt;br /&gt;
* dalej przykład trudniejszy do oszacowania. Od niego przejście do szacowania i porównywania podstawowych rzędów wielkości funkcji (przykład – 10 minut na zastanowienie, 10 minut na omowienie odpowiedzi, 25 minut na porównywanie różnych rzędów wielkośći)&lt;br /&gt;
* przykłady programów/algorytmów w których te rzędy wielkości ładnie widać&lt;br /&gt;
* '''złożoność pamięciowa -- rozmiar danych wejściowych oraz struktury alokowane w trakcie działania programu. To będzie przydatne w tłumaczeniu po co są listy. O tym chyba dokładniej przy omawianiu struktur danych'''&lt;br /&gt;
* proste obliczanie kosztu zamortyzowanego&lt;br /&gt;
* Szacowanie ile czasu rzeczywistego zajmie wykonanie programu, '''nauczenie intuicji jak długo trwa instrukcja wypisania/prostego obliczenia itp'''&lt;br /&gt;
* '''to, że w praktyce jeśli robimy jakieś wypisanie, to to trwa dużo dłużej niż jakieś pojedńcze przypisanie/obliczenie, różne ćwiczenia ile w praktyce trwa wykonanie programu'''&lt;br /&gt;
*'''Problemy P i np.'''&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Złożoność obliczeniowa i pamięciowa = &lt;br /&gt;
&lt;br /&gt;
== Złożoność obliczeniowa -- założenia  ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Jak wiadomo, czas wykonania programu może się różnić w zależności od tego na jak silnym komputerze zostanie on wykonany. Dlatego w dla potrzeb teoretycznego porównywania złożoności czasowej różnych algorytmów, nie mierzymy jej w sekundach, tylko abstrakcyjnych jednostkach -- ilości &amp;quot;operacji dominujących&amp;quot;, czyli takich, które znacząco wpływają na czas wykonania programu.&lt;br /&gt;
&lt;br /&gt;
Wskaż operacje dominujące w poniższych programach:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def suma(a,b):&lt;br /&gt;
   wynik = a + b&lt;br /&gt;
   return wynik&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def operacje(a,b):&lt;br /&gt;
   suma = a+b&lt;br /&gt;
   iloczyn = a*b&lt;br /&gt;
   iloraz = a/b&lt;br /&gt;
   roznica = a - b&lt;br /&gt;
   return suma, iloczyn, roznica, iloraz&lt;br /&gt;
&amp;lt;/source&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def wypisz(a,b):&lt;br /&gt;
   print operacje(a,b)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
(Wywołanie funkcji która ma 4 instrukcje, będzie trwać 4 instrukcje)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Znajdowanie Maximum: Danych jest N różnych liczb, nazwanych A[1], ... , A[N]. Wskaż największą.&lt;br /&gt;
&lt;br /&gt;
 m = A[1]&lt;br /&gt;
 imax = 1 &lt;br /&gt;
 for i = t to N do:&lt;br /&gt;
    if A[i] &amp;gt; m:&lt;br /&gt;
        m = A[i]&lt;br /&gt;
        imax = i&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Problem: Wskaż index wystapienia liczby 0 w danej tablicy. Wiadomo, że występuje dokładnie raz.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
 for i=1 1 to N do:&lt;br /&gt;
  if A[i] = 0:&lt;br /&gt;
   return i&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jaki będzie koszt dla danych:&lt;br /&gt;
 A[1] =  1, A[2] = 0, A[3] = 1, A[1000] = 1&lt;br /&gt;
&lt;br /&gt;
A dla danych:&lt;br /&gt;
 A[1] = 1, ... , A[999] = 1, A[1000] = 0&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Sortowanie bąbelkowe:&lt;br /&gt;
&lt;br /&gt;
 sort(T)&lt;br /&gt;
   for i=0 to n-2 do&lt;br /&gt;
        for j=n-1 downto i+1 do&lt;br /&gt;
             if (t[j-1]&amp;gt;t[j])&lt;br /&gt;
                  zamień t[j] i t[j-1]&lt;br /&gt;
&lt;br /&gt;
* Która operacja jest tu operacją, którą bierzemy pod uwage przy szacowaniu kosztu?&lt;br /&gt;
* Ile będzie trwało sortowanie dla tablicy:&lt;br /&gt;
** t[1] = 2, t[2] = 3, t[3] = 1, t[4] = 4, ..., t[100] = 100&lt;br /&gt;
** t[1] = 2, t[2] = 3, t[3] = 1, t[4] = 4, t[5] = 6, t[6] = 5, ..., t[100] = 100&lt;br /&gt;
&lt;br /&gt;
* A jakie są najgorsze możliwe dane? Ile wtedy będzie trwało wykonanie programu?&lt;br /&gt;
&lt;br /&gt;
Na tym właśnie polega szacowanie kosztu pesytmistycznego -- czas trwania programu dla najgorszych możliwych danych.&lt;br /&gt;
&lt;br /&gt;
== Koszt pesymistyczny i oczekiwany ==&lt;br /&gt;
&lt;br /&gt;
Dla zainteresowanych koszty różnych operacji w pythonie [http://wiki.python.org/moin/TimeComplexity tutaj]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Ćwiczenia ===&lt;br /&gt;
&lt;br /&gt;
* Jakie będą złożoności obliczeniowe następujących pętli:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
    for i in range(n)&lt;br /&gt;
        print i&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    for j in range(n)&lt;br /&gt;
        for i in range(n)&lt;br /&gt;
             print i, j&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    for j in range(n):&lt;br /&gt;
        for i in range(j):&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    n=1&lt;br /&gt;
    while(n &amp;lt; =x):&lt;br /&gt;
        print n&lt;br /&gt;
        n=2*n&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
    h=n&lt;br /&gt;
    while(h &amp;gt; 0):&lt;br /&gt;
        for i in range(n):&lt;br /&gt;
            print i&lt;br /&gt;
        h=h/2&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Notacja duże &amp;quot;O&amp;quot; i rzędy wielkości ===&lt;br /&gt;
Notacja duże &amp;quot;O&amp;quot; (inaczej: notacja asymptotyczna, notacja Landaua) służy do opisywania asymptotycznego zachowania funkcji (czyli zachowania funkcji wraz ze wzrostem jej argumentów).&lt;br /&gt;
&lt;br /&gt;
Definicja:&lt;br /&gt;
&lt;br /&gt;
 f(x) = O(g(x)) &amp;lt;=&amp;gt; istnieje c&amp;gt;0 i x_0 &amp;gt; 0 takie, że dla każdego x &amp;gt; x_0 zachodzi f(x) &amp;lt;= cg(x) &lt;br /&gt;
&lt;br /&gt;
Oznacza to, że f jest co najwyżej rzędu g.&lt;br /&gt;
&lt;br /&gt;
Porównywanie złożoności:&lt;br /&gt;
&lt;br /&gt;
 1 &amp;lt; lg(lg(n)) &amp;lt; lg(n) &amp;lt; sqrt(n) &amp;lt; n &amp;lt; nlg(n) &amp;lt; n^2 &amp;lt; n^3 &amp;lt; n^{lg(n)} &amp;lt; 2^n &amp;lt; n! &amp;lt; n^n &amp;lt;2^{2^n}&lt;br /&gt;
&lt;br /&gt;
==== Ćwiczenie ====&lt;br /&gt;
&lt;br /&gt;
* Załóżmy, że algorytmy A i B rozwiązują ten sam problem. Niech program A ma koszt pesymistyczny 100*n*log2(n), a program B n^2.&lt;br /&gt;
&lt;br /&gt;
** Czy zawsze bardziej się opłaca użyć algorytmy A?&lt;br /&gt;
** Co będzie dla danych, gdzie n &amp;gt;&amp;gt; 10000 ?&lt;br /&gt;
** Co będzie dla danych, gdzie n &amp;lt;&amp;lt; 100 ?&lt;br /&gt;
&lt;br /&gt;
=== Sortowanie przez wstawianie ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Elementy tablicy od 1 do i - 1 są posortowane, w kolejnym kroku należy w odpowiednie miejsce wstawić element a[i], w tym celu szukamy pierwszego elementu mniejszego od a[i] występującego przed nim i aktualny element wstawiamy bezpośrednio za nim (wymaga to przesunięcia wszystkich elementów za znalezionym o jedno miejsce w prawo)&lt;br /&gt;
&lt;br /&gt;
 for i := 2 to n do&lt;br /&gt;
 begin&lt;br /&gt;
  x := a[i];&lt;br /&gt;
  j := i - 1;&lt;br /&gt;
  while (j &amp;gt; 0) and (a[j] &amp;gt; x) do&lt;br /&gt;
  begin&lt;br /&gt;
   a[j - 1] := a [j];&lt;br /&gt;
   j := j - 1;&lt;br /&gt;
  end;&lt;br /&gt;
  a[j] := x;&lt;br /&gt;
 end;&lt;br /&gt;
&lt;br /&gt;
Operacjami dominującymi są przypisania w liniach 3 i 7&lt;br /&gt;
&lt;br /&gt;
Gdy tablica a na początku jest już posortowana rosnąco to operacja dominująca wykona się dokładnie n - 1 = O(n) razy (nigdy nie będzie spełniony warunek a[j] &amp;gt; x), a w sytuacji gdy będzie ona posortowana malejąco to warunek ten będzie zawsze spełniony, zatem dla k-tego przebiegu for pętli pętla while wykona się k - 1 razy, zatem złożoność obliczeniowa to: 1 + 2 + 3 + ... + (n - 1) + n = n(n - 1) / 2 + n - 1 = O(n^2), widać, zatem, że złożoność może zależeć od danych wejściowych&lt;br /&gt;
&lt;br /&gt;
Na szczęście gorzej już się nie da - o drugim przypadku mówimy, że jest pesymistyczny, a jego złożoność nazywamy złożonością pesymistyczną.&lt;br /&gt;
&lt;br /&gt;
==== Złożoność oczekiwana ====&lt;br /&gt;
&lt;br /&gt;
W codziennej praktyce spotykamy dość rzadko dane realizujące złożoność pesymistyczną, zatem nasuwa się pytanie jak szybko będzie działał nasz program zazwyczaj, czyli chcemy znać złożoność oczekiwaną bądź średnią.&lt;br /&gt;
&lt;br /&gt;
Zastanówmy się najpierw od czego zależy liczba przypisań w linii 7, jest ona związana z liczbą inwersji, czyli liczbą takich par (i,j), że j &amp;gt; i i a[j] &amp;lt; a[i], oznaczmy ją inv(a), wtedy liczba wykonań operacji dominującej to n - 1 + inv(a), zatem jeśli inv(a) = O(n) to złożoność całego algorytmu jest O(n), a gdy inv(a) = O(n^2) to taka jest również złożoność algorytmu.&lt;br /&gt;
&lt;br /&gt;
Postarajmy się teraz obliczyć średnią liczbę inwersji w losowej permutacji.&lt;br /&gt;
&lt;br /&gt;
Do obliczania złożoności oczekiwanej przydatna jest znajomość rachunku prawdopodobieństwa, n-elementowych permutacji mamy n!, załóżmy, że każda jest równie prawdopodobna&lt;br /&gt;
&lt;br /&gt;
Pozostaje teraz policzyć ile jest permutacji mających dokładnie k inwersji, pomocnym do tego będzie wprowadzenie pojęcia wektora inwersji permutacji, czyli ciąg w[i], gdzie w[i] to liczba elementów występujących na lewo od a[i] w ciągu a i większych od a[i], czytelnikowi pozostawiamy udowodnienie bijekcji między permutacją i odpowiadającym jej wektorem inwersji, oczywiste jest, że suma elementów wektora inwersji jest równa liczbie inwersji permutacji, na pozycji i w wektorze inwersji mogą się znaleźć (z równym prawdopodobieństwem wynoszącym 1/i) liczby od 0 (gdy a[i] jest większe od wszystkich poprzednich) do i - 1 (gdy a[i] jest mniejsze od wszystkich poprzednich), zatem wartość oczekiwana i-tego elementu to (0 + 1 + 2 + ... + (i - 1)) / i = (i - 1) / 2, a wartość oczekiwana sumy elementów wektora inwersji to 0 + 1 / 2 + 2 / 2 + ... + (n - 1) / 2 = n(n - 1) / 4&lt;br /&gt;
&lt;br /&gt;
Zatem dla losowej permutacji a mamy inv(a) = n(n - 1) / 4, zatem oczekiwana złożoność algorytmu sortowania przez wstawianie to: n(n - 1) / 4 + n - 1 = O(n^2)&lt;br /&gt;
&lt;br /&gt;
== Zadania ==&lt;br /&gt;
&lt;br /&gt;
* wymyśl program, którego złożoność będzie wynosiła:&lt;br /&gt;
** O(n^2) (np. psortowanie bąbelkowe, sortowanie przez wstawianie (insertion sort), sortowanie przez wybór (selection sort))&lt;br /&gt;
** O(n!) (dopasowywanie kwadracików z dzióbkami - układanka, problem skoczka)&lt;br /&gt;
** O(n)  (szukanie lidera, szukanie najdłuższego niespójnego ciągu rosnącego)&lt;br /&gt;
** O(1) &lt;br /&gt;
** O(2^n) (obliczanie liczby Fibonacciego, wieże Hanoi)&lt;br /&gt;
** O(lg(n)) (wyszukiwanie binarne)&lt;br /&gt;
** O(nlg(n)) &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- zakładając, że pojedyncza instrukcja trwa mikrosekundę to... policzyć ile będzie trwało wykonanie programu dla n = 10, 20, 40, 80, 160, 320, 640 instrukcji przy różnych złożonościach, zrobić wykresy funkcji w gnuplocie--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Przykłady ===&lt;br /&gt;
&lt;br /&gt;
Problem sortowania: dana jest tablica należy ją posortować, czyli ustawić jej elementy w kolejności rosnącej (albo malejącej)&lt;br /&gt;
&lt;br /&gt;
==== Sortowanie bąbelkowe ==== &lt;br /&gt;
Badamy tablicę od początku jeśli znajdziemy takie i, że a[i + 1] &amp;lt; a[i] to zamieniamy elementy, po jednym przejściu tablicy na ostatnim miejscu otrzymujemy największy element tablicy, problem redukuje się zatem do tablicy krótszej o 1&lt;br /&gt;
&lt;br /&gt;
 for i := 1 to n - 1 do&lt;br /&gt;
 begin&lt;br /&gt;
  for j := 1 to n - i + 1 do &lt;br /&gt;
  begin&lt;br /&gt;
   if a[j + 1] &amp;lt; a[j] then&lt;br /&gt;
    swap(a[j + 1], a[j]);&lt;br /&gt;
  end;&lt;br /&gt;
 end;&lt;br /&gt;
&lt;br /&gt;
==== Sortowanie przez wstawianie ==== &lt;br /&gt;
&lt;br /&gt;
patrz opis złożoności pesymistycznej i oczekiwanej&lt;br /&gt;
&lt;br /&gt;
==== Sortowanie przez wybór ====&lt;br /&gt;
Przechodzimy tablicę szukając elementu największego zamieniamy go z ostatnim po czym problem redukuje się do sortowania tablicy krótszej o 1&lt;br /&gt;
&lt;br /&gt;
 for i := 1 to n do&lt;br /&gt;
 begin&lt;br /&gt;
  max := a[i];&lt;br /&gt;
  maxIndex := i;&lt;br /&gt;
  for j := 1 to n - i + 1 do&lt;br /&gt;
  begin&lt;br /&gt;
   if a[j] &amp;gt; max then&lt;br /&gt;
   begin&lt;br /&gt;
    max := a[j];&lt;br /&gt;
    maxIndex := j;&lt;br /&gt;
   end;&lt;br /&gt;
  end;&lt;br /&gt;
  swap(a[maxIndex], a[n - i + 1]);&lt;br /&gt;
 end;&lt;br /&gt;
&lt;br /&gt;
==== Dopasowanie kwadracików z dzióbkami :-)====&lt;br /&gt;
M&lt;br /&gt;
amy dane m = n^2 kwadracików przy każdym boku kwadratu wewnątrz niego znajduje się kolorowy trójkąt, należy sprawdzić czy istnieje takie ułożenie kwadratów w jeden duży o boku n, że wszystkie trójkąty pasują do siebie - najprostsze rozwiązanie to sprawdzenie wszystkich możliwości których jest m!&lt;br /&gt;
&lt;br /&gt;
==== Szukanie lidera ==== &lt;br /&gt;
&lt;br /&gt;
dany jest ciąg a[1..n], problem polega na znalezieniu elementu występującego w nim więcej niż n/2 razy, rozwiązanie brutalne to sprawdzenie liczności występowania każdego z elementów - złożoność kwadratowa, można to jednak zrobić w czasie liniowym :-)&lt;br /&gt;
&lt;br /&gt;
 wystapienia := 0;&lt;br /&gt;
 for i := 1 to n - 1 do&lt;br /&gt;
 begin&lt;br /&gt;
  if wystapienia = 0 then&lt;br /&gt;
  begin&lt;br /&gt;
   kandydat := i;&lt;br /&gt;
   wystapienia := 1;&lt;br /&gt;
  end;&lt;br /&gt;
  if a[i] = a[i + 1] then&lt;br /&gt;
   wystapienia := wystapienia + 1;&lt;br /&gt;
  else&lt;br /&gt;
   wystapienia := wystapienia - 1;&lt;br /&gt;
 end;&lt;br /&gt;
&lt;br /&gt;
w zaskakujący automagiczny sposób jeśli w tablicy występował lider to jest nim a[kandydat], sprawdzić czy jest on liderem możemy oczywiście w czasie liniowym&lt;br /&gt;
&lt;br /&gt;
==== Szukanie najdłuższego niespójnego podciągu rosnącego (malejącego) ====&lt;br /&gt;
&lt;br /&gt;
Najbardziej brutalna metoda to wygenerowanie wszystkich 2^n podciągów i sprawdzanie po kolei, ale da się to zrobić w czasie O(nlogn).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--pomyślmy, że mamy tablicę t w której na miejscu k-tym jest minimalna wartość na której kończy się jakiś z k-elementowych podciągów ciągu a, jeśli takiego ciągu nie ma to w tablicy jest inf, teraz rozwiązanie jest trywialne - szukamy największego k, takiego, że t[k] != inf, zastanówmy się jak zbudować taką tablicę na pewno dobrym początkiem będzie t[1] := a[1], teraz patrzymy na kolejny element jeśli jest on mniejszy od t[1] to należy wykonać t[1] := a[2], a jeśli większy to t[2] := a[2] no to już wiemy: --&amp;gt;&lt;br /&gt;
&amp;lt;!-- t[1] := a[1]; --&amp;gt;&lt;br /&gt;
&amp;lt;!-- for i := 1 to n do&lt;br /&gt;
 begin&lt;br /&gt;
  j := i - 1;&lt;br /&gt;
  while (j &amp;gt; 0) and (a[i] &amp;lt; a[j]) do&lt;br /&gt;
   j := j - 1;&lt;br /&gt;
  if j = 0 then&lt;br /&gt;
   j := 1;&lt;br /&gt;
  t[j] := a[i];&lt;br /&gt;
 end;&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
 odp = 1&lt;br /&gt;
 for i in range(n): &lt;br /&gt;
  x = a[i]&lt;br /&gt;
  a[i] = 0&lt;br /&gt;
  # poniezej szukamy minimalnego j, t. ze j&amp;lt;= i, oraz x &amp;gt;= a[j]&lt;br /&gt;
  &lt;br /&gt;
  while (j &amp;gt;= 0) and (x &amp;gt;= a[j]):&lt;br /&gt;
   j := j - 1&lt;br /&gt;
  a[j] = x&lt;br /&gt;
  odp = max(j, odp)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Powyższy algorytm rozwiązuje problem wciąż w czasie O(n^2), ale można go łatwo sprowadzić do O(nlogn) dzięki temu, że początek tablicy, w którym wyszukujemy minimum, będzie zawsze posortowany i możemy zastosować przeszukiwanie binarne.&lt;br /&gt;
&lt;br /&gt;
==== Obliczanie liczby Fibonacciego ====&lt;br /&gt;
Liczbę Fibonacciego definiuje się rekurencyjnie: &amp;lt;math&amp;gt;F_0 = 0; F_1 = 1; F_{n} = F_{n - 1} + F_{n - 2}&amp;lt;/math&amp;gt;, można ją liczyć z definicji daje to &amp;lt;math&amp;gt;2^n&amp;lt;/math&amp;gt; dodawań, są do tego inne techniki można na przykład zaalokować dodatkową zmienną i trzymać w niej poprzedni wynik korzystając z niego i aktualizując go za każdym razem &amp;amp;mdash; daje to złożoność liniową, można robić to metodą mnożenia macierzy, co przy zastosowaniu metody mnożenia chłopków syberyjskich da złożoność logarytmiczną i w końcu dla większych &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; można skorzystać ze wzoru Bineta i dostać przybliżony wynik w czasie stałym.&lt;br /&gt;
[[Tytuł linku]]&lt;br /&gt;
&lt;br /&gt;
==== Wieże Hanoi ==== &lt;br /&gt;
Wyobraźcie sobie trzy słupy na jeden z nich są nasunięte krążki o malejącym idąc w górę promieniu, zasady: możecie kłaść krążek tylko na krążek o większej średnicy albo wkładać na pusty słup, zadanie dla Was przełożyć n krążków ze słupa numer 1 na słup numer 2 korzystając ze słupa numer 3, czas start! jeśli przełożycie 64 takich krążków to zgodnie z wierzeniami tybetańskich mnichów nastąpi koniec świata (biorąc pod uwagę złożoność jest to możliwe), jak to zrobić? jednym ze sposobów jest podejście rekurencyjne, zadanie sprowadza się do:&lt;br /&gt;
&lt;br /&gt;
przełożyć n - 1 krążków ze słupa numer 1 na słup numer 3 korzystając ze słupa numer 2&lt;br /&gt;
przełożyć krążek ze słupa numer 1 na słup numer 2&lt;br /&gt;
przełożyć n - 1 krążków ze słupa numer 3 na słup numer 2 korzystając ze słupa numer 1&lt;br /&gt;
&lt;br /&gt;
daje to złożoność 2^n&lt;br /&gt;
&lt;br /&gt;
==== Wyszukiwanie binarne ==== &lt;br /&gt;
Legenda głosi, że był kiedyś teleturniej w którym występowały dwie drużyny, jedna wymyślała słowo, a druga miała za zadanie zadając określoną liczbę pytań odgadnąć to słowo, pech chciał, że do gry zgłosili się kiedyś studenci MIMUW, pierwsze ich pytanie brzmiało: &amp;quot;czy to słowo to żaba?&amp;quot; przeciwnicy odetchnęli z ulgą, najwyraźniej jacyś mocno nieogarnięci przyszli - pomyśleli i odpowiedzieli, że nie, po tym dzielni studenci otworzyli słownik w środku i zadali pytanie: &amp;quot;czy to słowo jest w słowniku przed czy po X&amp;quot;, gdzie X to słowo znajdujące się w okolicy połowy słownika, po odpowiedzi wiedzieli już w której części słownika należy szukać słowa, robiąc tak dalej odgadli słowo bez problemu&lt;br /&gt;
&lt;br /&gt;
==== Problem skoczka ====&lt;br /&gt;
Mamy szachownicę n na n pól i pytanie: czy jest możliwe aby skoczek odwiedził wszystkie pola dokładnie raz? rozwiązanie brutalne wymaga dodatkowej tablicy n x n w której będziemy zaznaczać czy odwiedziliśmy już dane pole czy nie i sprawdzenia wszystkich możliwości&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--algorytm potęgowania chłopków syberyjskich :-) czyli potęgowanie w czasie logarytmicznym przez zamianę wykładnika na postać binarną i korzystanie z poprzedniego wyniku&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Złożoność pamięciowa ==&lt;br /&gt;
&lt;br /&gt;
Złożoność pamięciową mierzymy w liczbie komórek pamięci komputera zajmowanych przez program. Tu znów dla uproszczenia będziemy się skupiać na tym ile zajmują &amp;quot;właściwe&amp;quot; dane, na których program wykonuje operacje oraz -- żeby uniknąć problemów związanych z typami i reprezentacją liczba -- dla uproszczenia zakładamy, że jedna liczba / komórka tablicy / innej struktury zajmuje jedną &amp;quot;jednostkę&amp;quot;. (Chyba że w zadaniu jest explicite powiedziane, że jest inaczej.)&lt;br /&gt;
Także omówiony wyżej program, który ma znajdywać maximum w tablicy N elementowej będzie miał złożoność pamięciową O(N).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Przykłady: ===&lt;br /&gt;
&lt;br /&gt;
==== Ciekawostka -- oszczędzanie na każdej zmiennej ====&lt;br /&gt;
&lt;br /&gt;
Napisz program, który zamieni wartościami zmienne a i b. Tzn jeśli na początku a = 1, b = 2, to wynikiem działania programu ma być a = 2, b = 1.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
 c = a&lt;br /&gt;
 a = b&lt;br /&gt;
 b = c&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Potrzebujemy aż 3 komórek pamięci. Czy da się oszczędniej?&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
 a = a + b &lt;br /&gt;
 b = a - b &lt;br /&gt;
 a = a - b&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Sortowanie przez zliczanie: ====&lt;br /&gt;
&lt;br /&gt;
Mamy tablicę t, N liczb naturalnych do posortowania. Poznaliśmy już kilka algorytmów, które nam pozwolą ją posortować w czasię O(N^2).&lt;br /&gt;
Teraz załóżmy, że mamy dodatkową informację o tych liczbach -- są one z zakresu 1 do M. Możemy zastosować taki trick:&lt;br /&gt;
* alokujemy tablicę pom, długości M, wypełnioną zerami.&lt;br /&gt;
* przechodzimy tablicę t, i wykonujemy operację pom[t[i]] += 1&lt;br /&gt;
* teraz wystarczy przejść raz tablicę pom, i utworzyć tablicę która ma na pierwszych pom[1] miejscach jedynki, pom[2] miejscach dwójki, itd, która będzie naszym wynikiem&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Jaki wpływ ma ten trick na złożoność obliczeniową, a jaki na pamięciową sortowania?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
W przypadku gdy na przykład M = O(NlogN) poprawia to złożoność czasową algorytmu, natomista pogarsza złożoność pamięciową. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Cobędzie, gdy M &amp;lt;&amp;lt; N?&lt;br /&gt;
&lt;br /&gt;
Załóżmy teraz, że M &amp;lt;&amp;lt; N. Wtedy takie sortowanie poprawia złożoność czasową nie pogarszając pamięciowej O(M + N).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Załóżmy, że tym razem jednak interesuje nas zachowanie pierwotnej kolejności tych liczb. To znaczy dwie liczby &amp;quot;3&amp;quot; traktujemy jako w pewien sposób różne, i mają pozostać względem siebie w pierwotnej kolejności (&amp;quot;stabilność&amp;quot; sortowania). Czyli liczby są pewnego rodzaju obiektami. Zaalokujmy zatem w tablicy pom tablice, w których będizemy przechowywać obiekty, w takiej kolejności, w jakiej je spotkamy, a już nie tylko ilość ich wystąpień. &lt;br /&gt;
Jaka będzie w tej sytuacji złożoność pamięciowa i obliczeniowa?&lt;br /&gt;
&lt;br /&gt;
Złożoność czasowa pozostaje taka sama, natomiast złożoność pamięciowa zwiększa się do O(M * N).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Jakie złożoności pamięciowe mają poznane algorytmy sortowania:&lt;br /&gt;
&lt;br /&gt;
* bąbelkowe (przykład wyżej)&lt;br /&gt;
* przez wstawianie (przykad wyżej)&lt;br /&gt;
* selection sort (przez wybór za każdym razem najmniejszego elementu i wstawienie go w odpowiednie miejsce)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Koszt zamortyzowany ==&lt;br /&gt;
&lt;br /&gt;
Czasami w praktyce dana operacja &amp;quot;na ogół&amp;quot; będize miała koszt 1, ale w najgorszym przypadku może mieć koszt O(N). Jednak wiemy, że zdarzy się to raz na N przypadków, i wiemy to w sposób deterministyczny. Wtedy ten koszt O(N) rozłoży się równomienie na te N przypadków, co spowoduje, że każdą operację będizemy mogli traktować jakby miała koszt O(2), w każdym razie stały. Mówimy wtedy o koszcie zamortyzowanym.&lt;br /&gt;
&lt;br /&gt;
Dobrym przykładem jest tablica dynamiczna, jej implementacja polega na tym, że początkowo bierzemy tablicę pewnej długości na przykład 2, wstawiamy do niej elementy (w czasie stałym), a gdy pojemność tablicy się kończy to alokujemy nową dwukrotnie większą i tu musimy przepisać wszystkie elementy (w czasie liniowym), zatem wkładając n elementów mamy 2 + 4 + 8 + ... + floor(lg(n)) + n = 2(floor(lg(n)) - 1) + n przypisań przy n operacjach, zatem koszt zamortyzowany to 1 + 1 / n + 2(floor(lg(n)) - 1)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- == Problemy P i NP ==&lt;br /&gt;
Klasą P nazywamy zbiór tych zadań algorytmicznych, dla których istnieją algorytmy rozwiązujące je w czasie wielomianowym. &lt;br /&gt;
Klasą NP nazywamy zbiór tych zadań algorytmicznych, które można weryfikować w czasie wielomianowym, tzn znając rozwiązanie można sprawdzić je w czasie wielomianowym. Skróty P i NP pochodzą z angielskiego, odpowiednio, polynomial time i nondeterministic polynomial time.&lt;br /&gt;
&lt;br /&gt;
Algorytmy klasy NP są bardzo użyteczne w kryptografii.&lt;br /&gt;
--&amp;gt;&lt;/div&gt;</summary>
		<author><name>Jarekz</name></author>
		
	</entry>
</feed>