Wstęp do programowania / Ćwiczenia 2: Różnice pomiędzy wersjami

Z Studia Informatyczne
Przejdź do nawigacjiPrzejdź do wyszukiwania
Do przodu...
Dolozenie zadania o rozkladzie na skladniki, wywalenie parsera
Linia 351: Linia 351:
<math>p(x) = p_l(x) + p_h(x)*x^{N/2}</math> i analogicznie  
<math>p(x) = p_l(x) + p_h(x)*x^{N/2}</math> i analogicznie  
<math>q(x) = q_l(x) + q_h(x)*x^{N/2}</math>, to ich iloczyn można zapisać tak:<br>
<math>q(x) = q_l(x) + q_h(x)*x^{N/2}</math>, to ich iloczyn można zapisać tak:<br>
<math>p(x)*q(x) = r_l(x)+(r_m(x)-r_l(x)-r_h(x))*x^{n/2}+r_h(x)*x^N</math>, gdzie<br>
<math>p(x)*q(x) = r_l(x)+(r_m(x)-r_l(x)-r_h(x))*x^{N/2}+r_h(x)*x^N</math>, gdzie<br>
<math>r_l(x) = p_l(x)*q_l(x)</math><br>
<math>r_l(x) = p_l(x)*q_l(x)</math><br>
<math>r_h(x) = p_h(x)*q_h(x)</math><br>
<math>r_h(x) = p_h(x)*q_h(x)</math><br>
<math>r_m(x) = (p_h(x)+p_l(x))*(q_h(x)+q_l(x))</math><br>
<math>r_m(x) = (p_h(x)+p_l(x))*(q_h(x)+q_l(x))</math><br>
Czyli potrzebujemy 3 mnożeń o połowę mniejszych wielomianów. W sumie uzyskamy liczbę mnożeń rzędu <math>n^{lg 3}</math>. Szczególy można znaleźć w [[Sedgewick Algorithms in C++]], str. 527-530.
Czyli potrzebujemy 3 mnożeń o połowę mniejszych wielomianów. W sumie uzyskamy liczbę mnożeń rzędu <math>n^{lg 3}</math>. Szczególy można znaleźć w [[Sedgewick|Sedgewick Algorithms in C++]], str. 527-530.
</div>
</div>
 
==Zadanie 6 (Suma składników)==
 
Napisz procedurę, która wypisze dla zadanej liczby n jej wszystkie rozkłady na sumy nieujemnych liczb naturalnych większych od 1 ustawionych w kolejności nierosnącej. Np. dla n = 3:
3 = 3
3 = 2+1
3 = 1+1+1
Można założyć, że n jest mniejsza lub równa pewnej stałej maxN.
 
<div class="mw-collapsible mw-made=collapsible mw-collapsed">
'''Wskazówka 1'''
<div class="mw-collapsible-content" style="display:none">
Użyj dodatkowej tablicy do przechowywania początków rozkładów. Uważaj, aby nie kopiować tej tablicy przy każdym wywołaniu rekurencyjnym!
</div>
</div>
 
<div class="mw-collapsible mw-made=collapsible mw-collapsed">
'''Wskazówka 2'''
<div class="mw-collapsible-content" style="display:none">
Funkcja rekurencyjna powinna mieć parametr wskazujący jak duże składniki mogą być użyte do rozkładu pozostałej liczby. </div>
</div>
 
<div class="mw-collapsible mw-made=collapsible mw-collapsed">
'''Rozwiązanie 1'''
<div class="mw-collapsible-content" style="display:none">
 
procedure Suma(maxN:integer; n:integer);
// 1 <= n <= maxN
  procedure rozklad(var T:array[1..maxN] of integer; ti,k,m:integer)
  // 0 <= ti <= maxN
  // ti > 0 --> k=T[ti]
  // 0 <= m
  // m=0 --> ti > 0
  var i:integer
  begin
    if m=0 then begin
      writeln(n,' = ');
      for i=1 to ti-1 do write(T[i],'+');
      writeln(T[ti])
    end       
    else begin
      ti:=ti+1;
      for i=min(k,m) downto 1 do begin
        T[ti]:=i;
        rozklad(T,ti,i,m-i);
      end
    end
  end //rozklad
var T:array[1..maxN] of integer;
begin  
  rozklad(T,0,n,n);
end // Suma
 
''Koszt czasowy:'' wykładniczy względem n
 
''Koszt pamięciowy:'' liniowy względem maxN
 
''Opis:'' Funkcja rekurencyjna 'rozkład' przegląda wszystkie nierosnące rozkłady liczby m używające składników niewiększych niż k. Ponieważ w tablicy T[1..ti] jest już nierosnący ciąg składników o sumie n-m, wydłużenie go o dowolny taki rozkład m będzie dawać poprawny rozkład n.
 
Istotnie, jeśli m=0, w tablicy jest już gotowy rozkład n, więc wypisujemy go. W przeciwnym razie zwiekszamy ti, wszystkie liczby i, które mogą znaleźć się w tym miejscu rozkładu umieszczamy kolejno w T[ti] i wywołujemy rekurencyjnie funkcję 'rozklad', aby do wydłużonego rozkładu dopisała wszystkie rozkłady m-i o pierwszym składniku nieprzekraczającym i.
 
Zwróć uwagę, że tablica T przekazywana jest przez zmienną i dlatego jej zawartość nie jest kopiowana. Tablica T mogłaby też być zmienną  globalną (dla procedury 'Suma'), ponieważ i tak wszystkie wywołania funkcji rozklad korzystają z tej samej tablicy zadeklarowanej w głównej części procedury 'Suma'.
 
Zauważ, że można by też tak napisać procedurę 'rozklad', aby nigdy nie wywoływała się rekurencyjnie dla m=0. Wypisywanie pełnego rozkładu miałoby miejsce wtedy, gdy min(k,m)=m (czyli k>=m) i oczywiscie petla 'for' zaczynalaby sie w takim przypadku od m-1.
 
Druga możliwa modyfikacja polega na zrezygnowaniu z parametru k, ponieważ k albo jest równe T[ti], albo m jeśli ti=0. Mozna by też użyć strażnika T[0]=n i zawsze mielibyśmy k=T[ti].


Uwagi ogólne:
Poza wspomnianymi modyfikacjami całą procedurę 'rozkład' można by napisać w sposób ''rosnący'', tzn. zamiast generować coraz mniejsze składniki - generować coraz większe, aż osiągniemy, bądź przekroczymy n. W takim wypadku składniki należałoby wypisywać w odwrotnej kolejności.
* zwróćmy uwagę na kolejność wyliczania wyrażeń logicznych: standard Pascala jej nie definiuje, zatem AND może być liczony tak, ze najpierw liczy sie _oba_ argumenty a dopiero potem podaje wynik (nie można zakładać krótkiego liczenia od lewej do prawej). Przy wywołaniach rekurencyjnych to ważne szczególnie, bo nie chodzi tylko (czy ) o poprawność (if (i>1) and (tab[i]<>x) jest błędem) ale o efektywność (if droga(i-1,j) or droga(i+1,j) or ... jest nieefektywne).
* podkreślmy wagę procedur otoczek: pozwalają użytkownikowi i nam widzieć nasze procedury z takimi parametrami jakie są potrzebne.
</div>
</div>
</div>
</div>


==Zadanie 6 (Parser)==
<!-- to zadanie tu nie pasuje, wymaga zdecydowanie rekordow z wariantami...
==Zadanie 7 (Parser)==
Dla zadanej poniżej gramatyki języka programowania (będącego uproszczoną wersją Pascala) napisz metodą zejść rekurencyjnych zestaw procedur weryfikujących poprawność programów zapisanych w tym języku. Należy sprawdzić poprawność programu względem podanej gramatyki oraz czy wszystkie zmienne występujące w programie są zadeklarowane i czy wszystkie zadeklarowane zmienne są użyte. Można założyć, że w całym programie nie wystąpi więcej niż Z zmiennych. W przypadku napotkania błędu należy wypisać stosowny komunikat i zakończyć działanie.
Dla zadanej poniżej gramatyki języka programowania (będącego uproszczoną wersją Pascala) napisz metodą zejść rekurencyjnych zestaw procedur weryfikujących poprawność programów zapisanych w tym języku. Należy sprawdzić poprawność programu względem podanej gramatyki oraz czy wszystkie zmienne występujące w programie są zadeklarowane i czy wszystkie zadeklarowane zmienne są użyte. Można założyć, że w całym programie nie wystąpi więcej niż Z zmiennych. W przypadku napotkania błędu należy wypisać stosowny komunikat i zakończyć działanie.
Należy założyć istnienie procedury GetSym(Symbol), która daje kolejny symbol (identyfikator, liczbę, operator (:=,<,>,<=,>=,=,<>),  separator (;, ., (, ) ) z wejścia.  
Należy założyć istnienie procedury GetSym(Symbol), która daje kolejny symbol (identyfikator, liczbę, operator (:=,<,>,<=,>=,=,<>),  separator (;, ., (, ) ) z wejścia.  
Linia 416: Linia 485:
|}
|}


<!-- to między || i pierwszą | to są parametry komórki, więc żeby wpisać w komórce kreskę, to trzeba zacząc komórkę od _spacja_kreska_  :) -->
<!-- to między || i pierwszą | to są parametry komórki, więc żeby wpisać w komórce kreskę, to trzeba zacząc komórkę od _spacja_kreska_  :) - - >
<div class="mw-collapsible mw-made=collapsible mw-collapsed">
<div class="mw-collapsible mw-made=collapsible mw-collapsed">
'''Wskazówka 1'''   
'''Wskazówka 1'''   
Linia 423: Linia 492:
</div>
</div>
</div>
</div>
-->

Wersja z 19:26, 18 lip 2006

To są ćwiczenia z rekurencji.

Zadanie 1 (Labirynt)

Czy istnieje ścieżka miedzy wskazanymi punktami (i1,j1) i (i2,j2) w labiryncie reprezentowanym przez prostokątną tablicę liczb całkowitych o rozmiarze M×N, zawierającą zera (ściana) i jedynki (droga)? Zakładamy, że nie można przechodzić z pola na pole po skosie (np. z (2,5) na (3,6)), a tylko w czterech podstawowych kierunkach (np. z (2,5) na (3,5), (2,4) itd.)

Wskazówka 1

Rozwiązanie 1

Pytanko 1:

Pytanko 2:

Pytanko 3: (trudniejsze)

Odpowiedź:

Dla ciekawskich:

Zadanie 2 (Z górki na pazurki)

W tablicy liczb całkowitych o rozmiarze M×N zapisana jest mapa gór (każdy punkt ma podaną dodatnią wysokość). Sprawdzić, czy da się przejść z punktu startowego (i1,j1) do docelowego (i2,j2) idąc:

  • tylko w dół lub po płaskim
  • tylko w dół

Tak jak w poprzednim zadaniu poruszać się można tylko w czterech kierunkach podstawowych, nie po przekątnej.

Wskazówka 1

Rozwiązanie 1

Rozwiązanie 2

Zadanie 3 (Wieże Hanoi z ograniczeniami)

Na wykładzie były wieże Hanoi. Ciekawa modyfikacja tego zadania polega na zabronieniu ruchów pomiędzy niektórymi pałeczkami, np. z pierwszej na drugą. Zapisać procedurę realizującą to zadanie przy zabronionych niektórych ruchach.

Wskazówka 1

Rozwiązanie 1

Pytanko 1

Odpowiedź

Zadanie 4 (Ustawianie hetmanów)

Napisz procedurę znajdująca wszystkie takie rozstawienia 8 hetmanów na szachownicy, by żadne dwa hetmany się nie atakowały.

Wskazówka 1

Rozwiązanie 1

Zadanie 5 (Mnożenie wielomianów)

Dane są dwie tablice (array[0..N-1] of real) reprezentujące dwa wielomiany stopnia N-1. Należy obliczyć iloczyn tych wielomianów metodą dziel-i-zwyciężaj. Zakładamy, że N jest potęgą dwójki.

Wskazówka 1

Zadanie 6 (Suma składników)

Napisz procedurę, która wypisze dla zadanej liczby n jej wszystkie rozkłady na sumy nieujemnych liczb naturalnych większych od 1 ustawionych w kolejności nierosnącej. Np. dla n = 3: 3 = 3 3 = 2+1 3 = 1+1+1 Można założyć, że n jest mniejsza lub równa pewnej stałej maxN.

Wskazówka 1

Wskazówka 2

Rozwiązanie 1