Warning
This page is located in archive. Go to the latest version of this course pages. Go the latest version of this page.

Cvičení 11 Objekty, Halda, Asociativní pole, Stromy

náplň cvičení

Objekty

Třídy pro komplexní čísla:

  • Třída obsahuje dvě proměnné real a imag
  • Konstruktor (metoda _init_) nastavuje tyto proměnné defaultně na 0
  • Pro přímý výpis funkcí print je vhodné definovat metodu _repr_, která vrací string
  • Pozor: init a repr má v názvu dvě podtržítka!!!

class Complex:
    def __init__(self, real=0, imag=0):
        self.real = real
        self.imag = imag
 
    def amplitude(self):
        return self.real*self.real + self.imag*self.imag
 
    def add(self, rhs):
        self.real += rhs.real
        self.imag += rhs.imag
 
    def sub(self, rhs):
        self.real -= rhs.real
        self.imag -= rhs.imag
 
    def __repr__(self):
        sign = "+";
        if (self.imag < 0):
            sign = "-";
        return str(self.real) + sign + str(abs(self.imag)) + "i"
 
    def mul(self, rhs):
        r = self.real*rhs.real - self.imag*rhs.imag;
        i = self.real*rhs.imag + self.imag*rhs.real;
        self.real = r
        self.imag = i 
 
if __name__=="__main__":
    a = Complex()
    print("a=",a)
 
    b = Complex(1,-1)
    print("b=",b)
 
    a.add(b)
    print("a=",a)
 
    a.mul(b)
    print("a=",a)
 
    print("|a|=",a.amplitude())
    print("|b|=",b.amplitude())

Binární halda

Binární halda je binární stromová datová struktura. Je tvořena uzly, které mají max. dva potomky (levý a pravý potomek) (odtud přídavné jméno binární), pričemž potomek je opět uzel. Její důležitou vlastností je, že:

  • hodnota každého uzlu je rovna nebo menší než hodnoty jejich potomků.
  • Pokud je tato vlastnost splněna tak platí, že prvek v tzv. kořenu stromu obsahuje nejmenší prvek mezi všemi prvky.
  • V tomto cvičení budeme předpokládat tuto variantu.
  • Takové haldě se někdy říká min-halda.

Binární haldu lze samozřejmě realizovat i s opačnou vlastností:

  • hodnota každého uzlu je rovna nebo větší než hodnoty jejich potomků.
  • Pokud je tato vlastnost splněna tak platí, že prvek v tzv. kořenu stromu obsahuje největší prvek mezi všemi prvky.
  • Takové haldě se říká max-halda.

Použití binární haldy:

  • Pro realizaci prioritní fronty, v důsledku toho např. pro hledání cest v grafech, mapách, plánování pohybu robotů

Binární halda: vyjmutí prvku

Předpokládejme, že máme existující binární haldu. Při vyjmutí prvky stačí vzít prvek v kořeni stromu, neboť ten již z definice obsahuje nejmenší hodnotu mezi všemi uzly. Po odebrání prvku je ale nutné zbylé prvky přeskupit a určit nový kořen haldy. Postup je:

  • Vyjmout prvek z kořene haldy ( prvek s nejmenší hodnotou )
  • Vzít poslední prvek v poslední úrovni a přesunout na pozici kořene.
  • Nyní je třeba nahrat prvky v haldě tak, aby byla splněna vlastnost min-haldy. Jelikož budeme začínat od kořene a procházet strom směrem dolu, říká se tomuto postupu tzv. bubble-down.
Bubble-down:
  • Předpokládejme, že jsme v uzlu $U$.
  • Porovnáme hodnotu $U$, $U$.left a $U$.right. Pokud je splěna vlastnost min-haldy (tj. hodnota $U$ je menší nebo rovna hodnotám jejích potomků), končíme.
  • Pokud ne, vybereme toho potomka, který je menší než $U$. Vyměníme hodnotu $U$ s tímto potomek.
  • Pokračujeme bubble-down z tohoto potomka.
  • Algoritmus končí, pokud už jsme narazili na uzel bez potomka.

Binární halda: vložení prvku

Předpokládejme, že máme existující binární haldu. Vložení prvku se provede takto:

Bubble-up

  • Vložíme prvek na poslední nejpravější místo v poslední úrovni.
  • Porovnáme hodnotu tohoto prvku s jeho rodičem. Pokud je splněna vlastnost haldy (tj. u min-haldy: hodnota prvku je větší nebo rovna hodnotě jeho rodiče), pak končíme.
  • Pokud ne, vyměníme hodnotu prvku za hodnotu rodiče a opakujeme tento postup od změněného rodiče.

Tento algoritmus se nazývá bubble-up, jelikož při něm procházíme haldu ze spodní úrovně nahoru.

Realizace binární haldy na poli

Nejjednodušší realizací binární haldy je implementaci na poli. Použijeme jednoduchý trik:

  • Nechť uzel má v poli index $i$.
  • Jeho levý potomek má v poli index $2i+1$.
  • Jeho pravý potomek má v poli index $2i+2$.

  • Jaký je index rodiče, pokud má potomek index v poli $i$?

Implementace haldy z přednášky

# Implementace haldy
#
# http://interactivepython.org/runestone/static/pythonds/Trees/BinaryHeapImplementation.html
# Jan Kybic, 2016
 
class MinHeap:
  """ binarni halda __init__ konstruktor """
  def __init__(self):
     self.heap = [] # indexujeme od nuly
 
  def bubble_up(self,i):
    """ probubla prvek i nahoru, zajisti splneni podminek haldy """
    while i>0:
      j=(i-1)//2 # index rodice
      if self.heap[i] >= self.heap[j]:
        break
      self.heap[j],self.heap[i]=self.heap[i],self.heap[j]
      i = j
 
  def insert(self,k):
    """ vloz prvek do haldy """    
    self.heap+=[k]
    self.bubble_up(len(self.heap)-1)
 
  def peek(self):
    """ vrati nejmensi prvek """
    return self.heap[0]
 
  def size(self):
    """ vrati pocet prvku v halde """
    return len(self.heap)
 
  def is_empty(self):
    """ je halda prazdna? """
    return self.size()==0 
 
  def bubble_down(self,i):
     """ probublej prvek dolu """
     n=self.size()
     while 2*i+1 < n:
        j=2*i+1 # zjisti index mensiho syna
        if j+1 < n and self.heap[j] > self.heap[j+1]:
          j+=1
        if self.heap[i]>self.heap[j]:
          self.heap[i],self.heap[j]=self.heap[j],self.heap[i]
        i=j
 
  def pop(self):
    """ odebere nejmensi prvek a uprav haldu """
    element=self.heap[0]
    self.heap[0]=self.heap[-1]
    self.heap.pop() # smaz posledni prvek
    self.bubble_down(0)
    return element

Úkol 1: implementace funkce delete

Implementujte metody pro odebrání prvku na pozici i z binární haldy:

  • Metodu pojmenujte delete(i)
  • metoda dále smaže tento prvek z haldy
  • ošetřete tuto metodu tak, aby ji bylo možné volat i na prázdnou haldu, případně pokud je i větší než velikost haldy

Pomocí této funkce smažte z haldy vytvořené z pole všechna sudá čísla (Nejdříve haldu vytvořte se všemi čísly a pak smažte všechna sudá čísla z haldy):

pole=[10,21,7,11,31,6,1,-11,31,42,-12,80,25,-7,-12,9,14]

Úkol 2: Karty v haldě

  • Upravte implementaci haldy tak, aby byla realizována min-halda s kartami ve formátu cvičení 8 příklad 1.
  • Vytvořte hladu z následujících karet:

cards = [[0, 'Q'], [2, '6'], [1, 'K'], 
         [1, '8'], [2, '10'], [2, '4'], 
         [3, '4'], [0, '4'], [1, '3'], 
         [2, '5'], [0, 'K'], [3, 'A'], 
         [1, 'J'], [0, '3'], [0, '9']]

  • V cvičení 8 jsme pro porovnání karet využívali funkci index a dvojího porovnání (nejdříve barvu a pak hodnotu). Nyní definujte pořadí pomocí asociativního pole a operací sčítání a násobení.

Úkol 3: Asociativní pole a římská čísla

  • Využijte následující asociativní pole k převodu římského čísla na dekadické číslo:

conv={'I':1,'V':5,'X':10,'L':50,'C':100,'D':500,'M':1000}

  • Převeďte na číslo např. MCMXCIX

Prémie navíc: zobrazení přes dot format

Uložení načtených dat do ' Dot ' souboru, který lze pak vykreslit do png nástrojem dot z balíku nástrojů Graphviz:

dot -Tpng family.dot  > family.png

Příklad family.dot:

digraph G {
Jana[ color=red];
Jana->Martin [label="child"];
Jana->Robert [label="child"];
Jana->Jan[color=blue; penwidth=4];
Jan[ color=green];
Jan->Petra [label="child"];
Jan->Uxana [label="child"];
Jan->Jana[color=blue; penwidth=4];
Martin[ color=green];
Martin->Jiri [label="child"];
Martin->Rudolf [label="child"];
Martin->Jana [style=dashed];
...
}

Domácí úkol

Lehká varianta:

  • Napište program print.py, který implementuje prioritní tiskovou frontu.
  • Program čte ze standardního vstupu, na každé řádce je:
    • jedna tisková dávka ve tvaru: číslo mezera řetězec. Číslo reprezentuje prioritu dávky, řetězec její obsah.
    • požadavek od tiskárny na vytisknutí ve tvaru:-1
    • pro načítání použijte smyčku

for line in sys.stdin:
  # zpracuj line

  • Program ukládá tiskové dávky do prioritní fronty. Pokud je požadavek od tiskárny, odebere nejdříve dávku s nejvyšší prioritou, vytiskne její obsah na standardní výstup.
  • Pokud je standardní vstup prázdný, program vyprázdní frontu v pořadí priorit na standardní výstup.
  • Váš program print.py odevzdejte do odevzdávacího systému jako HW10.
Příklad

60 se 
58 zbraní 
90 zelnou 
38 mě 
100 Polévku
57 střelnou 
80 jedla 
-1
-1
-1
32 polila 
20 skolila
51 pasem
24 ji 
75 jsem
-1
-1
40 kdyby 
55 za
-1
-1 
25 bych 
30 na
-1
-1 
35 byla 
27 místě 
má výstup:
Polévku
zelnou
jedla
jsem
se
zbraní
střelnou
za
pasem
kdyby
mě
byla
polila
na
místě
bych
ji
skolila

Těžší varianta:

  • Napište program viginere.py, který najde klíč pro Vigenèrův kód pokud víme, že zakódovaná zpráva obsahuje slovo 'NEJEFEKTIVNEJSI' a délka klíče je v rozmezí 7-10 znaků.
  • Vstup
    • zakódovaný textový řetězec
  • Výstup
    • dekódovaný řetězec
  • Kódování:
    • Ve zprávě se vyskytují pouze znaky 'A'-'Z' a ' '.
    • Pod zprávu si opakovaně napíšeme klíč pro kódování a danné písmeno klíče slouží k výběru kódu:
      • Při písmenu klíče 'A' nedochází ke změně písmen
      • Pří písmenu klíče 'B' je 'A' kódováno písmenem 'B', 'B'→'C', …, 'Z'→' ',' '→'A'
      • Pří písmenu klíče 'C' je 'A' kódováno písmenem 'C', 'B'→'D', …, 'Z'→'A',' '→'B'
    • Příklad - zakódujte řetězec 'NEJEFEKTIVNEJSI ALGORITMUS' pomocí klíče 'PROGRAM'
      • NEJEFEKTIVNEJSI ALGORITMUS
        PROGRAMPROGRAMPROGRAMPROGR
      • Výsledek kódování je:
        BVXKWEWHZITVJDXQORXOCXJ  I
  • Jak zjistit klíč?
    • klíč nelze určit zkoušením všech možností klíče, těch je mnoho až 26**10, což je 141167095653376 možností.
    • návod na získání klíče pokud znáte jedno slovo ze zprávy je na stránkách Vigenèrova kódu

Příklad

Vstup (klíč 'BFLMPSVZ'):

DMNQAWUXJXVMHRPXECKZTAZDFPDUJEZHTNKM YIPJYXFG
Výstup:
CHCEME ZISKAT VZDY NEJEFEKTIVNEJSI ALGORITMUS

Vstup (klíč 'XFDNSERUVS'):

JJMDPGYEZAONCOLHVTJIKEROISLLESWIDFSDDYDWBJNF ZDYDJEEDYYSHBNDQX
Výstup:
NEJRYCHLEJSI BUDE PRO OBROVSKA DATA NEJEFEKTIVNEJSI ALGORITMUS

courses/b3b33alp/cviceni/t11.txt · Last modified: 2020/12/07 19:40 by stepan