Tukaj so zbrane naloge iz Toma za predmet Programiranje 1.

Okolje

Naloge na Tomotu rešujete tako, da v datoteko (v prazen prostor za komentarjem, ki vsebuje navodila) vpišete rešitev in pritisnete tipko F5. S tem se vaš program naloži v konzolo in zaženejo se testi, ki preverijo pravilnost rešitve.


Oddajanje rešitev

Naloge na Tomotu rešujete tako, da v datoteko (v prazen prostor za komentarjem, ki vsebuje navodila) vpišete rešitev in pritisnete tipko F5. S tem se vaš program naloži v konzolo in zaženejo se testi, ki preverijo pravilnost rešitve.

1. podnaloga

Vpišite poljubno celo število ter nalogo pošljite na Tomota.

Uradna rešitev

192837465

2. podnaloga

Vpišite celo število, a ne kateregakoli. Če želite ugotoviti, katero je pravo, berite opozorila, ki jih sporoča Tomo.

Uradna rešitev

314

3. podnaloga

Vpišite število, ki je uradna rešitev prve podnaloge.

Uradno rešitev lahko na Tomotu vidite šele takrat, ko podnalogo pravilno rešite. Uradno rešitev se vam vedno splača pogledati in jo primerjati s svojo rešitvijo.

Uradna rešitev

192837465

Izpiši


Smrečice

1. podnaloga

Petvrstično smrečico lahko v Pythonu izpišemo z naslednjim programom:

print("*")
print("**")
print("***")
print("****")
print("*****")

Sestavite program, ki izpiše osemvrstično smrečico.

Uradna rešitev

print("*")
print("**")
print("***")
print("****")
print("*****")
print("******")
print("*******")
print("********")

2. podnaloga

Program za izpis osemvrstične smrečice popravite tako, da namesto niza z $n$ zvezdicami '**...*' uporabite izraz n * '*', na primer namesto niza '*****' uporabite izraz 5 * '*'.

Uradna rešitev

print(1 * "*")
print(2 * "*")
print(3 * "*")
print(4 * "*")
print(5 * "*")
print(6 * "*")
print(7 * "*")
print(8 * "*")

3. podnaloga

Osemvrstično smrečico izpišite z enim samim klicem funkcije print.

Namig: znak za prelom vrstice je "\n".

Uradna rešitev

print(1 * znak + "\n" +
      2 * znak + "\n" +
      3 * znak + "\n" +
      4 * znak + "\n" +
      5 * znak + "\n" +
      6 * znak + "\n" +
      7 * znak + "\n" +
      8 * znak)

4. podnaloga

Osemvrstično smrečico izpišite še desno poravnano. Petvrstična desno poravnana smrečica izgleda takole:

    *
   **
  ***
 ****
*****

Namig: na začetku vsake vrstice izpišite ustrezno število presledkov.

Uradna rešitev

znak = "*"
print(7 * " " + 1 * znak + "\n" +
      6 * " " + 2 * znak + "\n" +
      5 * " " + 3 * znak + "\n" +
      4 * " " + 4 * znak + "\n" +
      3 * " " + 5 * znak + "\n" +
      2 * " " + 6 * znak + "\n" +
      1 * " " + 7 * znak + "\n" +
      8 * znak)

5. podnaloga

Osemvrstično smrečico izpišite še sredinsko poravnano in z razmaki. Petvrstična sredinsko poravnana smrečica z razmaki izgleda takole:

    *
   * *
  * * *
 * * * *
* * * * *

Namig: levi rob je enak kot pri desno poravnani smrečici, za vsakim osnovnim znakom pa je presledek.

Uradna rešitev

znakp = "* "
print(7 * " " + 1 * znakp + "\n" +
      6 * " " + 2 * znakp + "\n" +
      5 * " " + 3 * znakp + "\n" +
      4 * " " + 4 * znakp + "\n" +
      3 * " " + 5 * znakp + "\n" +
      2 * " " + 6 * znakp + "\n" +
      1 * " " + 7 * znakp + "\n" +
      8 * znakp)

Šahovnice

1. podnaloga

Izpišite poln kvadrat, sestavljen iz znaka "+". Velikost kvadrata naj bo shranjena v spremenljivki n. Na primer, če je n = 4, se naj izpiše

++++
++++
++++
++++

Kvadrat naj bo velikosti vsaj $7 \times 7$ znakov.

Uradna rešitev

n = 7
vrstica = n * '+' + '\n'
kvadrat = n * vrstica
print(kvadrat)

2. podnaloga

Izpišite še prazen kvadrat, obrobljen z znakom "+". Velikost kvadrata naj bo shranjena v spremenljivki n. Na primer, če je n = 4, se naj izpiše

++++
+  +
+  +
++++

Kvadrat naj bo velikosti vsaj $7 \times 7$ znakov.

Uradna rešitev

n = 7
polna = n * '+' + '\n'
prazna = '+' + (n - 2) * ' ' + '+' + '\n'
kvadrat = polna + (n - 2) * prazna + polna
print(kvadrat)

3. podnaloga

Napišite program, ki izpiše šahovsko desko razsežnosti $2n \times 2n$. Črna polja naj bodo sestavljena iz znakov "@", bela polja pa iz znakov ".". Velikost polj naj bo shranjena v spremenljivki m. Tudi spremenljivka n mora biti podana.

Na primer, če je m = 2 in n = 3, naj se izpiše:

@@..@@..@@..
@@..@@..@@..
..@@..@@..@@
..@@..@@..@@
@@..@@..@@..
@@..@@..@@..
..@@..@@..@@
..@@..@@..@@
@@..@@..@@..
@@..@@..@@..
..@@..@@..@@
..@@..@@..@@

Uradna rešitev

m = 2
n = 3
vzorec1 = ((m * '@' + m * '.') * n + '\n') * m
vzorec2 = ((m * '.' + m * '@') * n + '\n') * m
kvadrat = (vzorec1 + vzorec2) * n
print(kvadrat)

4. podnaloga

Napišite program, ki izpiše šahovsko desko razsežnosti $2n \times 2n$, na kateri so črna polja oblike

####
####
####
####

bela pa oblike

++++
+  +
+  +
++++

Velikost polj naj bo shranjena v spremenljivki m. Tudi spremenljivka n mora biti podana.

Na primer, če je m=3 in n=2, naj se izpiše:

###+++###+++
###+ +###+ +
###+++###+++
+++###+++###
+ +###+ +###
+++###+++###
###+++###+++
###+ +###+ +
###+++###+++
+++###+++###
+ +###+ +###
+++###+++###

Uradna rešitev

m=3
n=2
crna = m * '#'
bela = m * '+'
prazna = '+' + (m - 2) * ' ' + '+'
vzorec1 = ((crna + bela) * n + '\n' +
           ((crna + prazna) * n + '\n') * (m-2) +
           (crna + bela) * n + '\n')
vzorec2 = ((bela + crna) * n + '\n' +
           ((prazna + crna) * n + '\n') * (m-2) +
           (bela + crna) * n + '\n')
kvadrat = (vzorec1 + vzorec2) * n
print(kvadrat)

Popravi program


Za ogrevanje

1. podnaloga

Program vpraša za dve števili in za njun produkt, ter izpiše uporabnikove odgovore in pravi produkt. Vendar ne dela!

  ime = input("Kako ti je ime? ")
  print("Pozdravljen, ', ime, 'bi vadil poštevanko? ")
  int(input("Pa dajva. Vpiši prvi faktor: "))
  int(input("Pa še drugi faktor: "))
  rezultat = a*b
  c = input("Koliko, misliš, znaša produkt? ")
  print("Napisal si, da je", a, "krat", b, "enako", c)
  print("Pravilen odgovor je", rezulat)

Skopiraj ga, poskusi prevesti in popravi!

Uradna rešitev

ime = input("Kako ti je ime? ")
print("Pozdravljen,", ime, "bi vadil poštevanko? ")
a = int(input("Pa dajva. Vpiši prvi faktor: "))
b = int(input("Pa še drugi faktor: "))
rezultat = a * b
c = input("Koliko, misliš, znaša produkt? ")
print("Napisal si, da je", a, "krat", b, "enako", c)
print("Pravilen odgovor je", rezultat)

2. podnaloga

Razsute kartice

Ko sem bil jaz bruc (ja, v prazgodovini ;-) ) smo računalniške programe pisali na kartice. Na vsaki kartici je bil en stavek. In ni bilo tako redko, da si se spotaknil, razsul šop kartic in ... Potem je bilo potrebno zadevo ustrezno urediti. In sedaj imamo podoben problem. Virus je v naši datoteki premešal vrstice.

  #preberemo podatke
  print ('Program ti bo spremenil sekunde v dneve, ure, minute in sekunde')
  print (sekZacasne,'sekund je natanko')
  print (dnevi, 'dni,', ure, 'ur,', minute, 'minut in', sekunde,'sekund')
  minZacasne = sekZacasne // 60   #celi del deljenja so vse minute
  minute = minZacasne % 60        #ostanek od deljenja z 60 so minute ostalo že ure
  sekunde = sekZacasne % 60       #ostanek od deljenja z 60 so sekunde
  ure = ureZacasne % 24           #ostanek od deljenja z 24 so ure ostalo dnevi
  ureZacasne = minZacasne // 60   #celi del od deljenja z 60 so vse ure
  dnevi = ureZacasne // 24        #celi del deljenja z 24 so dnevi
  sekZacasne = int(input('Vnesi sekunde: '))
  #izpišemo rezultat

Uredi jih tako, da bo program smiselno deloval!

Uradna rešitev

#preberemo podatke
print ('Program ti bo spremenil sekunde v dneve, ure, minute in sekunde')
sekZacasne = int(input('Vnesi sekunde: '))
sekunde = sekZacasne % 60       #ostanek od deljenja z 60 so sekunde
minZacasne = sekZacasne // 60   #celi del deljenja so vse minute
minute = minZacasne % 60        #ostanek od deljenja z 60 so minute ostalo že ure
ureZacasne = minZacasne // 60   #celi del od deljenja z 60 so vse ure
ure = ureZacasne % 24           #ostanek od deljenja z 24 so ure ostalo dnevi
dnevi = ureZacasne // 24        #celi del deljenja z 24 so dnevi
#izpišemo rezultat
print (sekZacasne,'sekund je natanko')
print (dnevi, 'dni,', ure, 'ur,', minute, 'minut in', sekunde,'sekund')

Hišnik

Hišnik mora vsak mesec očistiti bazen. Da ga lahko očisti, mora najprej iz njega izčrpati vodo. Ker je bolj lene sorte, pa bi med iztakanjem vode (ki traja kar nekaj časa) raje odšel v bližnjo kavarno na pogovor s prijateljem, namesto da bi stražil bazen. Tukaj je program, ki bo za bazen velikosti 2 m x 3m x 9m izračunal, koliko sekund se bo praznil bazen, če vsako sekundo izteče iz bazena 23l vode. Če ste že pozabili: kubični decimeter vode = 1l vode.

  #Vnos dimenzij bazena.
  globina = 2
  širina = 3
  dolžina = 9
  # Pretvorba dimenzij bazena v dm.
  dolžinaVdm = dolžina * 10
  širinaVdm = širina * 10
  globinaVdm = globina * 10
  # Izračun prostornine bazena v kubičnih dm.
  prostorninaVdm = dolžinaVdm * širinaVdm * globinaVdm
  # Vnos pretoka vode v sekundi.
  prostorninaIztekaV1sek = 23
  # Izračun časa praznjenja bazena.
  časPraznjenjaVsek = prostorninaVdm / prostorninaIztekaV1sek
  # Izpis rezultata.
  print('Bazen velikosti', dolžina, 'm ×', širina, 'm ×', globina, 'm', 'se bo praznil', časPraznjenjaVsek, 'sekund.')

1. podnaloga

Spremeni zgornji program tako, da boš dimenzije bazena in hitrost iztekanja prebral! Dimenzije so v celih metrih!

Uradna rešitev

#Vnos dimenzij bazena.
globina = int(input('Vnesi globino bazena '))
širina = int(input('Vnesi širino bazena '))
dolžina = int(input('Vnesi dolžino bazena '))
# Pretvorba dimenzij bazena v dm.
dolžinaVdm = dolžina * 10
širinaVdm = širina * 10
globinaVdm = globina * 10
# Izračun prostornine bazena v kubičnih dm.
prostorninaVdm = dolžinaVdm * širinaVdm * globinaVdm
# Vnos pretoka vode v sekundi.
prostorninaIztekaV1sek = int(input('Koliko l vode v sekundi izteče: '))
# Izračun časa praznjenja bazena.
časPraznjenjaVsek = prostorninaVdm / prostorninaIztekaV1sek
# Izpis rezultata.
print('Bazen velikosti', dolžina, 'm ×', širina, 'm ×', globina, 'm', 'se bo praznil', časPraznjenjaVsek, 'sekund.')

2. podnaloga

Hišnik je zamenjal službo. A tudi tam imajo bazen in hišnik ga mora spet čistiti. Že je ves vesel zagnal tvoj program, a kaj, ko je pri merjenju ugotovil, da je globina bazena 2.34m. Pomagaj mu in program popravi tako, da bo sedaj sprejel podatke v obliki decimalnih števil. Potrudi se in rezultate izpiši lepo. Namig: oglej si, kaj počne float(12.45) in print("%.2f" % 1.2399) ali pa print("{:.2f}".format(3.1415926)), oziroma na spletu poišči "float Python 3" in "format string Python 3"

Uradna rešitev

#Vnos dimenzij bazena.
globina = float(input('Vnesi globino bazena '))
širina = float(input('Vnesi širino bazena '))
dolžina = float(input('Vnesi dolžino bazena '))
# Pretvorba dimenzij bazena v dm.
dolžinaVdm = dolžina * 10
širinaVdm = širina * 10
globinaVdm = globina * 10
# Izračun prostornine bazena v kubičnih dm.
prostorninaVdm = dolžinaVdm * širinaVdm * globinaVdm
# Vnos pretoka vode v sekundi.
prostorninaIztekaV1sek = float(input('Koliko l vode v sekundi izteče: '))
# Izračun časa praznjenja bazena.
časPraznjenjaVsek = prostorninaVdm / prostorninaIztekaV1sek
# Izpis rezultata.
print('Bazen velikosti', "{:.2f}".format(dolžina), 'm ×', "{:.2f}".format(širina), 'm ×',
      "{:.2f}".format(globina), 'm', 'se bo praznil', "{:.2f}".format(časPraznjenjaVsek), 'sekund.')

3. podnaloga

Hišnik je z našim programom sicer zelo zadovoljen, a zadnjič se je v pretvarjanju sekund v minute uštel in je zato prišel nazaj h bazenu cele pol ure prezgodaj. Zato prosi, če mu namesto časa v sekundah izpišeš čas v urah, minutah in sekundah. Tukaj boste morali sekunde iz decimalne oblike pretvoriti v celo število. Ugotovi, kako to storimo v Pythonu. Namig: https://docs.python.org/3/library/functions.html

Uradna rešitev

#Vnos dimenzij bazena.
globina = float(input('Vnesi globino bazena '))
širina = float(input('Vnesi širino bazena '))
dolžina = float(input('Vnesi dolžino bazena '))
# Pretvorba dimenzij bazena v dm.
dolžinaVdm = dolžina * 10
širinaVdm = širina * 10
globinaVdm = globina * 10
# Izračun prostornine bazena v kubičnih dm.
prostorninaVdm = dolžinaVdm * širinaVdm * globinaVdm
# Vnos pretoka vode v sekundi.
prostorninaIztekaV1sek = float(input('Koliko l vode v sekundi izteče: '))
# Izračun časa praznjenja bazena.
časSek = round(prostorninaVdm / prostorninaIztekaV1sek)
časMinut = časSek // 60
časSek = časSek % 60
časUr = časMinut // 60
časMinut = časMinut % 60
# Izpis rezultata.
print('Bazen velikosti', "{:.2f}".format(dolžina), 'm ×', "{:.2f}".format(širina), 'm ×',
      "{:.2f}".format(globina), 'm', 'se bo praznil',
      časUr, "ur,", časMinut, "minut in", časSek, "sekund.")

Pretvori

1. podnaloga

Naslednji program naj bi seštel dva kota, podana v stopinjah, minutah in sekundah

 print('Prvi kot\n=========')
 stopinje1 = int(input('stopinje: '))
 minute1 = int(input('minute: '))
 sekunde1 = int(input('sekunde: '))
 print('Drugi kot\n=========')
 stopinje2 = int(input('stopinje: '))
 minute2 = int(input('minute: '))
 sekunde2 = int(input('sekunde: '))
 stopinje = stopinje1 + stopinje2
 minute = minute1 + minute2
 sekunde = sekunde1 + sekunde2
 minute = minute + sekunde // 60
 minute = minute % 60
 print('Vsota kotov je', stopinje, 'stopinj,', minute, 'minut in', sekunde, 'sekund.')

Žal ne dela pravilno. Popravi ga!

Uradna rešitev

print('Prvi kot\n=========')
stopinje1 = int(input('stopinje: '))
minute1 = int(input('minute: '))
sekunde1 = int(input('sekunde: '))
print('Drugi kot\n=========')
stopinje2 = int(input('stopinje: '))
minute2 = int(input('minute: '))
sekunde2 = int(input('sekunde: '))
stopinje = stopinje1 + stopinje2
minute = minute1 + minute2
sekunde = sekunde1 + sekunde2
minute = minute + sekunde // 60
sekunde = sekunde % 60
stopinje = stopinje + minute // 60
minute = minute % 60
print('Vsota kotov je', stopinje, 'stopinj,', minute, 'minut in', sekunde, 'sekund.')

2. podnaloga

Spodaj je napisan program, ki kot v radianih (realno število) pretvori in izpiše v stopinjah in minutah (kot celi števili; za minute naj program ne uporabi zaokroženja ampak naj samo odreže decimalna mesta).

Primer: Za

kot = 2.185

naj program izpiše

2.185 radianov je 125 stopinj in 11 minut.

Žal ne deluje povsem pravilno. Popravi ga!

 import math # Število π je shranjeno v math.pi

 kot = input('Kot: ')
 stopinje = kot * 360 / math.pi
 minute = 60 * stopinje
 stopinje = int(stopinje)
 print('{:.6}'.format(kot), 'radianov je', stopinje, 'stopinj in', minute, 'minut

Uradna rešitev

import math # Število π je shranjeno v math.pi
    
kot = float(input('Vnesi kot: '))
stopinje = kot * 360 / (2 * math.pi)
minute = int(60 * (stopinje - int(stopinje)))
stopinje = int(stopinje)
print('{:.6}'.format(kot), 'radianov je', stopinje, 'stopinj in', minute, 'minut.')

3. podnaloga

Program

 ura = int(input('Ur: '))
 minuta = int(input('Minut: '))
 alfa = 60 * (3 * minuta) # Kot minutnega kazalca v kotnih minutah.
 beta = 60 * (ura + minuta) # Kot urnega kazalca v kotnih minutah.
 minute = abs(alfa - beta)
 minute = min(minute, 360 * 60 - minute) # Vzamemo manjšega od obeh kotov.
 minute = minute - int(minute) # Odrežemo decimalni del.
 stopinje = minute // 60
 print('Kot med urnim in minutnim kazalcem je', stopinje, 'stopinj in', minute, 'minut.')

naj bi izračunal manjšega izmed kotov med urnim in minutnim kazalcem ob danem času. Čas je podan z uro in minutami. Upoštevajte, da se vsako minuto tudi urni kazalec malo prestavi. Trenutno uro in minuto imate podano v spremenljivkah z imenoma ura in minuta. Kot naj bo izpisan v obliki

Kot med urnim in minutnim kazalcem je x stopinj in y minut.

kjer sta x in y celi števili (minut ne zaokrožuj, ampak samo odreži decimalni del).

Primer: za vrednosti

ura = 17
minuta = 47

naj program izpiše

Kot med urnim in minutnim kazalcem je 108 stopinj in 30 minut.

Uradna rešitev

ura = int(input('Ur: '))
minuta = int(input('Minut: '))
ura = ura % 12
alfa = 60 * (6 * minuta) # Kot minutnega kazalca v kotnih minutah.
beta = 60 * (30 * ura + 0.5 * minuta) # Kot urnega kazalca v kotnih minutah.
minute = abs(alfa - beta)
minute = min(minute, 360 * 60 - minute) # Vzamemo manjšega od obeh kotov.
minute = int(minute) # Odrežemo decimalni del.
stopinje = minute // 60
minute = minute % 60
print('Kot med urnim in minutnim kazalcem je', stopinje, 'stopinj in', minute, 'minut.')

Funkcije - prve naloge

Tu je sklop osnovnih nalog za spoznavanje funkcij v Pythonu


Delamo s števili

Sestavite funkciji, ki vrneta zahtevani sredini dveh števil.

1. podnaloga

Zapišite funkcijo aritmeticna(a, b), ki za števili $a$ in $b$ vrne aritmetično sredino teh dveh števil, tj. $\frac{a + b}{2}$.

Uradna rešitev

def aritmeticna (a, b):
    '''Funkcija vrne aritmetično sredino števil a in b.'''
    return (a + b)/2

2. podnaloga

Zapišite funkcijo geometricna(a, b), ki za števili $a$ in $b$ vrne geometrično sredino teh dveh števil, tj. $\sqrt{ab}$.

Namig - oglej si dokumentacijo in zgled

Uradna rešitev

import math # da bomo lahko uporabili vgrajeno funkcijo za korenjenje
def geometricna (a, b):
    '''Funkcija vrne geometrično sredino števil a in b.'''
    return math.sqrt(a*b)

3. podnaloga

Dano imamo decimalno število. Sestavite funkcijo desetinke(x), ki vrne desetinke števila x. Na primer:

>>> desetinke(3.7)
7
>>> desetinke(6.127)
1

Uradna rešitev

def desetinke(x):
     '''vrne desetinke števila x'''
     stevilo = 10 * abs(x) # premik decimalne pike v desno. Da ne bo kaj čudnega, delamo z abs
     celiDel = int(stevilo)
     deset = celiDel % 10 # desetinke so enice novega števila
     return deset

4. podnaloga

Dano imamo decimalno število. Funkcija celiDecimalni(x) naj bi vrnila celi in prvi dve decimalki števila x tako, kot kažejo primeri Na primer:

>>> celiDecimalni(3.7)
(3, 70)
>>> celiDecimalni(-6.127)
(-6, 12)
>>> celiDecimalni(12.0127)
(12, 1)

A funkcija

def celiDecimalni(x):
     '''vrne celi in decimalni del števila x'''
     celiDel = int(x)
     decimalni = 100 * (x - celiDel)
     return celiDel, decimalniDel

ne dela vedno tako. Poišči in odpravi napake!

Uradna rešitev

def celiDecimalni(x):
     '''vrne celi in decimalni del števila x'''
     celiDel = int(x)
     x = abs(x)
     decimalniDel = int((x - abs(celiDel))*100)
     return celiDel, decimalniDel

Posoda z vodo

V posodo, katere dno ima obliko kvadrata, nalijemo vodo vse do roba.

1. podnaloga

Če vanjo vržemo kroglo z radijem 5cm, nam funkcija

import math
def kolikoOstalo(širina, višina):
   '''Na tri decimalke natančno vrne, koliko litrov vode je ostalo v posodi'''
   volumenKrogle = 4/ 3 * math.pi * 0.125
   volumenPosode = širina ** 2 * višina / 1000
   ostanekVode = round(volumenPosode - volumenKrogle, 3)
   return ostanekVode

vrne, koliko litrov vode ostane v posodi (vse mere so podane v cm). Spremeni to funkcijo v kolikoOstalo(širina, višina, polmer), ki izračuna koliko litrov vode je ostalo v posodi, če vanjo damo kroglo s polmerom polmer.

Uradna rešitev

import math
def kolikoOstalo(širina, višina, polmer):
    '''Na tri decimalke natančno vrne, koliko litrov vode je ostalo v posodi'''
    volumenKrogle = 4/ 3 * math.pi * (polmer / 10)**3
    volumenPosode = širina ** 2 * višina / 1000
    ostanekVode = round(volumenPosode - volumenKrogle, 3)
    return ostanekVode

2. podnaloga

V posodo sedaj položimo stožec, katerega premer je enak širini posode, višina pa enaka globini posode. Napišite funkcijo kolikoOstaloSt(širina, višina, n), ki izračuna koliko litrov vode je ostalo v posodi ter vrne rezultat, zaokrožen na n decimalk. Funkcija sprejme širino in višino posode v cm.

Uradna rešitev

import math
def kolikoOstaloSt(širina, višina, n):
    '''Na n decimalk natančno vrne, koliko litrov vode je ostalo v posodi'''
    volumenStozca = 1 / 3 * math.pi * (širina / 2) ** 2 * višina / 1000
    volumenPosode = širina ** 2 * višina / 1000
    ostanekVode = round(volumenPosode - volumenStozca, n)
    return ostanekVode

Ustvari niz, izpiši niz ....

1. podnaloga

Radi bi sestavili niz, ki ga sestavlja n enakih znakov. Zato sestavi funkcijo kopija, ki vrne niz, ki ga sestavlja n kopij znaka. Če torej napišemo program:

print(kopija('X', 5))
črta = kopija('-', 3)
print(črta + " : " + str(len(črta)))

bo izpis:

XXXXX
--- : 3

Uradna rešitev

def kopija(znak, n):
    '''Vrne niz, ki ga sestavlja n kopij znaka n'''
    niz = znak * n 
    return niz

2. podnaloga

Sestavi funkcijo črkaH(n, znak), ki za podatek n izpiše na zaslon veliko črko H, visoko 2n + 1 znakov in široko n + 2 znakov. Primer:

 >>>črkaH(2, 'o')
 o  o
 o  o
 oooo
 o  o
 o  o

Uradna rešitev

def črkaH(n, znak):
    '''Izpišimo črko H'''
    # zgornji del
    vrstica = znak + ' ' * n + znak + '\n'
    print(n * vrstica, end = '') # na koncu smo že skočili v novo vrsto
    # srednji del
    print(znak * (n + 2))
    # spodnji del
    vrstica = znak + ' ' * n + znak + '\n'
    print(n * vrstica, end = '') # na koncu smo že skočili v novo vrsto

Varčevanje na obroke

Pri varčevanju na obroke na začetku vplačamo nek znesek, nato pa vsak mesec doplačujemo mesečne obroke. Zaradi enostavnosti predpostavimo, da so vplačila vedno prvi dan v mesecu, vrednost varčevalnega računa pa se vedno računa ob koncu meseca. Denar, ki ga imamo na varčevalnem računu, se obrestuje po fiksni obrestni meri.

1. podnaloga

V bančništvu pogosto navajamo letno obrestno mero, obrestujemo pa mesečno. Zato je pred računanjem potrebno obrestno mero primerno spremeniti.

Napišite funkcijo mesecna_obrestna_mera(letna_obrestna_mera), ki kot parameter sprejme letno obrestno mero v odstotkih, vrne pa ustrezno mesečno obrestno mero kot decimalno število (torej letni obrestni meri 12% ustreza mesečna obrestna mera 0.01).

Uradna rešitev

def mesecna_obrestna_mera(r):
    '''Letna obrestna mera v obliki mesečne decimalne oblike.'''
    return r / 1200

2. podnaloga

Pri enostavnem obrestovanju s fiksno letno obrestno mero $r$ se vedno obrestuje samo glavnica, ne pa tudi obresti. Tako je vrednost na računu pri začetnem pologu $A_0$ po $n$ mesecih podana s formulo $$A = A_0 * (1 + m * n),$$ kjer je $m$ pripadajoča mesečna obrestna mera.

Sestavite funkcijo enostavno_obrestovanje(polog, letna_obrestna_mera, st_mesecev), ki za enostavno obrestovanju izračuna vrednost denarja na računu pri danem začetnem pologu, letni obrestni meri in številu mesecev.

Primeri:

>>> enostavno_obrestovanje(100, 10, 0)
100.0
>>> enostavno_obrestovanje(100, 12, 1)
101.0
>>> enostavno_obrestovanje(100, 10, 12)
110

Uradna rešitev

def enostavno_obrestovanje(polog, letna_obrestna_mera, st_mesecev):
    '''Vrednost obrestovanega denarja pri enostavnem obrestovanju.'''
    return polog * (1 + mesecna_obrestna_mera(letna_obrestna_mera) * st_mesecev)

3. podnaloga

Pri obrestno obrestnem računu s fiksno letno obrestno mero $r$ se zraven glavnice obrestujejo tudi obresti. Za glavnico $A_0$ je vrednost po $n$ mesecih podana s formulo $$A = A_0(1 + m)^n,$$ kjer je $m$ pripadajoča mesečna obrestna mera.

Sestavite funkcijo obrestno_obrestovanje(polog, letna_obrestna_mera, st_mesecev), ki izračuna vrednost denarja na računu pri danem začetnem pologu, letni obrestni meri in številu mesecev, pri čemer se obresti računajo po obrestno obrestnem računu.

Primeri:

>>> obrestno_obrestovanje(100, 10, 0)
100.0
>>> obrestno_obrestovanje(100, 10, 1)
100.83333333333333
>>> obrestno_obrestovanje(100, 12, 12)
112.68250301319698

Uradna rešitev

def obrestno_obrestovanje(polog, letna_obrestna_mera, st_mesecev):
    '''Vrednost obrestovanega denarja pri obrestno obrestnem obrestovanju.'''
    return polog * (1 + mesecna_obrestna_mera(letna_obrestna_mera)) ** st_mesecev

Funkcije - kultura


Grupa TNT

Za uvod si najprej preberite nekaj o stripu Alan Ford (in še nekaj).

1. podnaloga

Lakota

Broj 1 je z zaskrbljenostjo potresel škatlo. Le nekaj fižolovih zrn je še ostalo. Spet lakota v Cvetličarni. Začel je deliti - eno zrno za Alana, eno za Jeremijo, eno zanj, eno za Grunfa. In spet eno zrno za Alana, eno za Jeremijo, eno zanj, eno za Grunfa in ... Ko so mu ostala manj kot 4 zrna, je zavzdihnil in vsa pospravil na svoj kupček. Že je želel poklicati k večerji, ko je v kotu zastokal Jeremija in se prebudi. Ah, tudi on mora dobiti svoj delež. Zrna na kup in eno za Alana, eno za Grunfa, ... Seveda, ko je bilo manj kot pet zrn, gredo spet vsa na kupček Broja 1. In ko je bil že skoraj pri koncu, se od nekje pojavita Oliver in Bob Rock. In spet od začetka ...

Da bo šlo hitreje, sestavi funkcijo zrna(fižoli, številoOseb), ki za dano število zrn in oseb vrne koliko zrn dobijo člani skupine TNT in koliko Broj 1. Npr.

     >>>zrna(72, 5)
     14, 16

Uradna rešitev

def zrna(koliko, osebe):
    '''Koliko zrn dobi osebe oseb, če vsi dobijo v načelu enako,
    morebiten preostanek pa pripade dodatno eni osebi'''
    večina = koliko // osebe
    en = koliko - večina * (osebe - 1)
    return večina, en

2. podnaloga

sir Oliver, izposojena ura in jašek

Na sprehodu sir Oliver čisto slučajno zavije v zlatarno in čez nekaj trenutkov je že zunaj, s kričečim prodajalcem za sabo. Par hitrih zavojev in ko že kaže, da je sir Oliver ušel, se znajde v slepi ulici. Gentleman kot sir Oliver definitivno je, si ne bo dovolil, da bi ga zasačili z uro, ki si jo je sposodil, zato jo hitro odvrže v navpični jašek na dnu katerega je malo vode. Skrbno posluša in čez nekaj časa zasliši čof. Pomagaj mu in napiši funkcijo globinaJaška(čas), ki za podatek koliko časa je minilo od trenutka, ko je sir Oliver spustil uro, do trenutka, ko je reklo čof, vrne na tri decimalke natančno globino jaška v metrih. Če ne poznaš enačb, si pomagaj z wikipedijo. Za gravitacijski pospešek vzemi 9.81 m/s.

Namig: Python pozna funkcijo round(x, n). Kaj pa dela, ugotovi v dokumentaciji ali pa bo dovolj, da pogledaš naslednjo nalogo.

Uradna rešitev

def globinaJaška(t):
    '''Kako globok jke jašek, če premet pada t sekund'''
    g = 9.81
    globina = 0.5 * g * t**2
    rez = round(globina, 3)
    return rez

3. podnaloga

Grunf topničar

Grunf strelja s topom na domovanje Superhika, a ga nikakor ne more zadeti.

Tukaj je funkcija, ki izračuna maksimalno višino leta krogle,

      import math
      def maxViš(v, kot):
         '''Maks. višina pri poševnem metu'''
         kotRad = kot * math.pi / 180
         najV = v**2 * math.sin(kotRad)**2
         g = 10.0
         zaokr = round(najV / (2*g), 1)
         return zaokr

a kaj ko potrebuje domet! Pomagaj mu in sestavi funkcijo domet(v, kot), ki na eno decimalko natančno izračuna domet krogle, če s hitrostjo v ustrelimo pod kotom kot stopinj. Če si pozabil, gre za poševni met Za g vzemi kar 10 m/s^2, zračni upor pa zanemari.

>>> domet(10, 45)
10.0
>>> domet(10, 60)
8.7

Uradna rešitev

import math
def domet(v, kot):
    '''Domet pri poševnem metu'''
    kotRad = kot * math.pi / 180
    dom = v*v*math.sin(2*kotRad)
    g = 10.0
    zaokr = round(dom / g, 1)
    return zaokr

Mirko in Slavko

Za uvod si preberite nekaj o Mirku in Slavku:

1. podnaloga

Prevara s čekom

Kurirji so iznajdljivi. Seveda Mirko in Slavko pa sploh. V roke sta dobila ček, kjer banka prinositelju izplača vrednos na njem napisanega tromestnega zneseka. Ampak Mirko ne bi bil Mirko, če ne bi kaj hitro ugotovil, da z malo spretnosti in ostrim nožičem lahko ček neopazno spremeni tako, da premeša števke na znesku. A kaj, ko je Mirko bolj slab v matematiki, rad pa bi ček popravil tako, da bo dobil kar se da veliko. Potoži se Slavku. Ah, to pa ja ni problem. Poglej to funkcijo. Malo jo spremeni, pa boš dobil največje možno število. Mirko sedaj proučuje

   def kajDelam(n):
       '''Pozor, n je obvezno tromestno naravno število
          Spremenljivke imajo namenoma čudna imena.
       '''
       a = n - n % 10
       b = a // 10  % 10
       c = n - a
       d = n // 100
       e = max(b, c, d)
       f = min(b, c, d)
       g = b + c + d - e - f
       return f, g, e

Premisli kaj funkcija počne in na osnovi ideje v njej namesto Mirka sestavi funkcijo najTromestno, ki iz danega tromestnega števila sestavi največje možno tromestno število

>>> najTromestno(137)
731
>>> najTromestno(625)
652

Uradna rešitev

def najTromestno(n):
    '''Iz števk danega tromestnega števila naredi največje možno število'''
    enice = n % 10
    desetice = n // 10 % 10
    stotice = n // 100
    najmanjše = min(enice, desetice, stotice)
    največje = max(enice, desetice, stotice)
    srednje = enice + desetice + stotice - najmanjše - največje # srednje po velikosti
    število = največje * 100 + srednje * 10 + najmanjše
    return število

2. podnaloga

Mirko, pazi metak!

Verjetno najbolj znamenit dialog med Mirkom in Slavkom je:

  • Slavko: Mirko, pazi metak!
  • Mirko: Hvala ti, Slavko. Spasio si mi život.

Kako hitro se mora Mirko skloniti, če Slavko strel opazi n metrov proč

Sestavi funkcijo skloniSe(kolikoM, hitrostMetka), ki pove, v koliko sekundah, desetinkah in stotinkah se mora Mirko skloniti, če Slavko opazi metek kolikoM metrov proč in ima hitrost hitrostMeta m/s. Pazi, da bodo stotinke ustrezno zaokrožene!

Uradna rešitev

def skloniSe(kolikoM, hitrostMetka):
     '''V koliko sekundah, destinkah in stotinkah se mora Mirko skloniti, če Slavko opazi metek `kolikoM` metrov proč in
        ima hitrost `hitrostMeta` m/s.'''
     čas = kolikoM/hitrostMetka
     časStotink = int(čas * 100 + 0.5) # če prištejemo 0.5, bomo prav zaokrožili
     stotink = časStotink % 10
     desetink = (časStotink // 10) % 10
     sekund = časStotink // 100
     return (sekund, desetink, stotink)

3. podnaloga

Metak mrzkega neprijatelja

Slavka skrbi, če bo tudi on dovolj hiter, da bo ubežal krogli. Zato mu sestavi program, ki bo meril njegov rekacijski čas.

Če na začetek programa napišemo

  import time

bomo, med drugim, dobili funkcijo time, ki s klicem time.time() vrne čas v sekundah od nekega trenutka v davni preteklosti. Napiši program, ki bo izpisal Slavko, pazi metak! in izmeril, koliko časa je trajalo, da je Slavko pritisnil na tipko Enter in potem izpisal, koliko sekund je Slavko potreboval za pritisk na tipko. Namig: če veš, koliko je bila ura pred klicem funkcije input in koliko je bila ura po klicu, znaš izračunati, koliko časa je minilo vmes.

>>>Slavko, pazi metak!
Slavko je reagiral v 2.503019332885742 s.

Uradna rešitev

import time
začetek = time.time()
input('Slavko, pazi metak!')
konec = time.time()
print('Slavko je reagiral v', konec - začetek, 's.')
print('\n')

4. podnaloga

Mirko in Slavko ob pomoči Dimnjačara pečeta palačinke

Mirko in Slavko sta v uspešni akciji zaplenila velike količine moke in jajc. Zato bosta za celo vas Glavuša na Kozari napekla palačinke.

Ampak kaj, ko ne vesta koliko. Dimnjačar jima je zato napisal funkcijo A kaj, ko mu je tik preden je odšel, izpod listov trpotca, ki jih je imel na čelu namesto obveze padlo par kapelj krvi in je sedaj funkcija

   def kolikoPalačink(steviloOdraslih, steviloOtrok):
      '''Vrne število paslačink potrebnih, da nasiti vse povabljene'''
      PACKA
      PACKA
      palacinkeOdrasli = steviloOdraslih * odrasel
      palacinkeOtroci = steviloOtrok * otrok
      palacinkeSkupaj = palacinkeOdrasli + palacinkeOtroci + PACKA # še malo za rezervo
      return palacinkeSkupaj

Na srečo pa so ostali še trije izpisi, ko jima je pokazal delovanje funkcije. Pomagaj Mirku in Slavku in dopolni funkcijo, da se bo obnašala kot prej, če so izpisi

   Koliko je odraslih: 12
   Koliko otrok: 5
   Napeči je potrebno 77 palačink.
   Koliko je odraslih: 21
   Koliko otrok: 14
   Napeči je potrebno 140 palačink.
   Koliko je odraslih: 10
   Koliko otrok: 15
   Napeči je potrebno 87 palačink.

Uradna rešitev

def kolikoPalačink(steviloOdraslih, steviloOtrok):
   '''Vrne število paslačink potrebnih, da nasiti vse povabljene'''
   odrasel = 5
   otrok = 2
   palacinkeOdrasli = steviloOdraslih * odrasel
   palacinkeOtroci = steviloOtrok * otrok
   palacinkeSkupaj = palacinkeOdrasli + palacinkeOtroci + 7 # še malo za rezervo
   return palacinkeSkupaj

5. podnaloga

Slavko peče torte

Po velikem uspehu s peko palačink, se je Slavko spravil peči torte. Po receptu za torto potrebujemo 0.8 kg margarine, 2 kg moke in 1.5 kg sladkorja. Sedaj Slavka zanima, kakšno je, glede na količine sestavin, ki jih ima na razpolago, največje možno število tort, ki jih lahko naredi.

Namig: Funkcija min vrne najmanjšega izmed svojih parametrov.

Primer: Če imamo 5kg margarine, 7kg moke in 3.5kg sladkorja, lahko spečemo dve torti.

Slavku sestavi funkcijo kolikoTort(margarina, moka, sladkor), ki glede na dani recept določi največje možno število tort.

Uradna rešitev

def kolikoTort(margarina, moka, sladkor):
    '''Koliko lahko največ spečemo tort'''
    receptMargarina = 0.8 # količina margarine po receptu
    receptMoka = 2 # količina moke za eno torto po receptu
    receptSladkor = 1.5 # količina sladkorja po receptu
    # koliko tort glede na posamezno sestavimo
    izMoke = int(moka/receptMoka)
    izMargarine = int(margarina/receptMargarina)
    izSladkorja = int(sladkor/receptSladkor)
    možnoTort = min(izMoke, izMargarine, izSladkorja)
    return možnoTort

6. podnaloga

Mirko in smučarski skoki

Medtem, ko Slavko peče torte, je Mirko organiziral tekmovanje v smučarskih skokih.

Pri smučarskih skokih so točke skoka vsota:

  • točk za daljavo in
  • točk sloga (ki jih določijo sodniki).

Vsak od petih sodnikov lahko skakalcu dodeli največ 20 točk, ki so odvisne od položaj smuči med letom, ravnotežja med letom, položaja telesa, pristanka ipd. Točke sloga so vsota točk posamičnih sodnikov, pri čemer se najboljša in najslabša ocena ne upoštevata.

Mirko ima več kot dovolj dela z iskanjem primernih sodnikov (pa še z njegovo matematiko je bolj tako, tako ...). Zato mu pomagaj in sestavi funckijo točkeSlog(oc1,oc2, oc3, oc4, oc5), ki za danih 5 ocen določi točke za slog. Namig: Poleg funkcije min Python pozna tudi funkcijo max, ki se obnaša podobno!

Uradna rešitev

def točkeSlog(oc1,oc2, oc3, oc4, oc5):
    '''Koliko točk za slog je dobil skakalec'''
    vseSkupaj = oc1 + oc2 + oc3 + oc4 + oc5
    najslabša = min(oc1,oc2, oc3, oc4, oc5)
    najboljša = max(oc1,oc2, oc3, oc4, oc5)
    return vseSkupaj - najslabša - najboljša

Pogojni stavki I


Indeks telesne mase

1. podnaloga

ITM oz. indeks telesne mase je številka, s katero lahko v grobem ocenimo stanje prehranjenosti pri odraslih ljudeh. Če je $m$ masa človeka v kilogramih in $h$ njegova višina v metrih, izračunamo ITM po naslednji formuli: $ITM = \frac{m}{h^2}$.

Težo, ki je podana v kilogramih, imamo zapisano v spremenljivki masa. Višino, ki je podana v centimetrih, pa imamo v spremenljivki visina. Sestavite program, ki prebere težo in višino, izračuna ITM in vam pove, v kateri razred prehranjenosti spadate. Izračunani ITM izpišite na dve decimalni mesti natančno, tako da uporabite funkcijo dve_decimalki

  def dve_decimalki(x):
     """ Funkcija vrne niz, ki vsebuje zapis decimalnega števila x
         na dve decimalni mesti natančno.  """
     return '{0:.2f}'.format(x)

Izpis naj bo sledeč:

  • $ITM < 18.5$: Ti si podhranjen.
  • $18.5 \leq ITM < 25$: Ti si normalno prehranjen.
  • $25 \leq ITM < 30$: Ti si čezmerno prehranjen.
  • $30 \leq ITM$: Ti si debel.

Primer: Za podatke masa = 82.5, visina = 185.3 naj program izpiše:

>>>Vnesi težo: 82.5
Vnesi višino: 185.3
Tvoj ITM: 24.03
Ti si normalno prehranjen.

Uradna rešitev

def dve_decimalki(x):
     """
     Funkcija vrne niz, ki vsebuje zapis decimalnega števila x na dve
     decimalni mesti natančno.
     """
     return '{0:.2f}'.format(x)

masa = float(input('Vnesi težo: '))
visina = float(input('Vnesi višino: '))

itm = masa / (visina / 100)**2
print('Tvoj ITM:', dve_decimalki(itm))
if itm < 18.5:
    print('Ti si podhranjen.')
elif itm < 25:
    print('Ti si normalno prehranjen.')
elif itm < 30:
    print('Ti si čezmerno prehranjen.')
else:
    print('Ti si debel.')

Ednina, dvojina, množina

1. podnaloga

Napiši funkcijo finStanje(n), ki kot argument sprejme količino denarja na bančnem računu, podano v evrih (celih) in slovničnemu številu ustrezno vrne opis finančnega stanja.

 >>>finStanje(1)
 Stanje: 1 evro.
 >>>finStanje(1002)
 Stanje: 1002 evra.
 >>>finStanje(-203)
 Stanje: -203 evri.
 >>>finStanje(215)
 Stanje: 215 evrov.

Primer, če je vneseno stanje večje ali enako 1000000 evrov:

 >>>finStanje(1000002)
 Tajkun!

Primer, če je vneseno stanje nižje od -300 evrov:

 >>>finStanje(-302)
 Ti si navadna zguba!

Uradna rešitev

def finStanje(n):
    '''vrne opis finančnega stanja'''
    if n <-300:
        return 'Ti si navadna zguba!'
    if n >= 1000000:
        return 'Tajkun!'
    if abs(n)%100 == 1:
        return 'Stanje: ' + str(n) +' evro.'
    if abs(n)%100 == 2:
        return 'Stanje: ' + str(n) + ' evra.'
    if abs(n)%100 == 3 or abs(n)%100 == 4:
        return 'Stanje: ' + str(n) + ' evri.'
    return 'Stanje: ' + str(n) + ' evrov.'

2. podnaloga

Sestavi funkcijo stLjudi(n), ki kot argument sprejme poljubno naravno število in nato v slovnično pravilni obliki vrne opis števila ljudi v dvorani kulturnega doma (glej zglede). Dvorana sprejme največ 500 ljudi. Podatki so simeslni, torej celo število, večje ali enako 0. Primer za n=0:

 Dvorana je prazna.

Primer za n=1:

 V dvorani je 1 človek.

Primer za n=303:

 V dvorani so 303 ljudje.

Primer za n=500:

 Dvorana je polna.

Primer za n=502:

 Dvorana je polna. Zunaj sta ostala 2 človeka.

Uradna rešitev

def stLjudi(n):
    '''vrne niz z opisom števila ljudi v dvorani'''

    if n == 0:
        return 'Dvorana je prazna.'
    if n < 500:
        if n%100 == 1:
            return 'V dvorani je '+ str(n) + ' človek.'
        elif n%100 == 2:
            return 'V dvorani sta '+ str(n) + ' človeka.'
        elif n%100 == 3 or n%100==4:
            return 'V dvorani so '+ str(n) + ' ljudje.'
        else:
            return 'V dvorani je '+ str(n) + ' ljudi.'
    if n==500:
        return 'Dvorana je polna.'
    # več kot 500!
    if n%100 == 1:
        return 'Dvorana je polna. Zunaj je ostal '+ str(n-500)+' človek.'
    elif n%100 == 2:
        return 'Dvorana je polna. Zunaj sta ostala '+ str(n-500)+' človeka.'
    elif n%100 == 3 or n%100 == 4:
        return 'Dvorana je polna. Zunaj so ostali '+ str(n-500)+' ljudje.'
    else:
        return 'Dvorana je polna. Zunaj je ostalo '+ str(n-500)+' ljudi.'

3. podnaloga

Sestavite program, ki prebere naravno število n in nato izpiše niz oblike

Koliko imaš limon: 103
Imaš 103 limone.
Koliko imaš limon: 0
Imaš 0 limon.

pri čemer mora biti seveda vse pravilno sklanjano.

Uradna rešitev

n = int(input('Koliko imaš limon: '))
ostanek = n % 100
if ostanek == 1:
    koncnica = 'o'
elif ostanek == 2:
    koncnica = 'i'
elif ostanek == 3 or ostanek == 4:
    koncnica = 'e'
else:
    koncnica = ''
print("Imaš {0} limon{1}.".format(n, koncnica))

Biokosmiči

Skupina slovenskih nadebudnih mladih inovatorjev se je domislila projekta biokosmiči. Vrečke, v kateri so shranjeni kosmiči, ni potrebno odpirati, saj se v vodi raztopi. Vrečka je užitna in brez okusa.

1. podnaloga

Sestavite funkcijo nakup(imam, rabim, vrecka), ki izračuna, najmanj koliko vrečk kosmičev moramo kupiti, če jih že imamo imam gramov, potrebujemo jih rabim gramov, v posamezni vrečki pa je zapakirano vrecka gramov. Primer:

>>> nakup(4, 10, 3)
2

Uradna rešitev

def nakup(imam, rabim, vrecka):
    '''Koliko vrečk kosmičev je potrebno še kupiti'''
    if imam >= rabim:
        return 0 # imam več kot rabim ali ravno toliko 
    if (rabim - imam) % vrecka == 0: # v vrečkah je za naše potrebe po idealno kosmičev
        return (rabim - imam) // vrecka
    return (rabim - imam) // vrecka + 1 # če se ne izide, je potrebno kupiti še dodatno

2. podnaloga

Vsaka vrečka vsebuje 125 g kosmičev, ki so sestavljeni iz 80 g ovsenih kosmičev, 20 g rozin in 25 g oreščkov. Imamo že kosmici gramov ovsenih kosmičev, rozine gramov rozin in orescki gramov oreščkov. Sestavite funkcijo napolni(kosmici, rozine, orescki, n), ki izračuna, koliko gramov ovsenih kosmičev, rozin in oreščkov moramo še kupiti, da bomo lahko napolnili n vrečk kosmičev (funkcija mora vrniti trojico vrednosti). Primer:

>>> napolni(300, 0, 65, 3)
(0, 60, 10)

Uradna rešitev

def napolni(kosmici, rozine, orescki, n):
    '''Koliko sestavin je še potrebnih za n vrečk'''
    vreckaKosmici = 80
    vreckaRozine = 20
    vreckaOrescki = 25
    n1 = max(0, vreckaKosmici * n - kosmici)
    n2 = max(0, vreckaRozine * n - rozine)
    n3 = max(0, vreckaOrescki * n - orescki)
    return n1, n2, n3

3. podnaloga

Sestavite funkcijo napolni2(kosmici, rozine, orescki), ki izračuna, najmanj koliko vrečk kosmičev moramo napolniti, da bomo porabili vse sestavine. Imamo že kosmici gramov ovsenih kosmičev, rozine gramov rozin in orescki gramov oreščkov. Manjkajoče sestavine lahko pri tem dokupimo. Primer:

>>> napolni2(100, 40, 65)
3

Uradna rešitev

def napolni2(kosmici, rozine, orescki):
    '''Koliko vrečk potrebujemo, da porabimo prav vse sestavine'''
    vreckaKosmici = 80
    vreckaRozine = 20
    vreckaOrescki = 25
    potrebnoZaKosmice = kosmici // vreckaKosmici
    potrebnoZaRozine = rozine // vreckaRozine
    potrebnoZaOrescke = orescki // vreckaOrescki
    # če se s sestavinami ne sezide, bomo potrebovali še eno vrečko
    if kosmici % vreckaKosmici > 0: potrebnoZaKosmice += 1
    if rozine % vreckaRozine > 0: potrebnoZaRozine += 1
    if orescki % vreckaOrescki > 0: potrebnoZaOrescke += 1
    # vzamemo največje število potrebnih vrečk
    return max(potrebnoZaKosmice, potrebnoZaRozine, potrebnoZaOrescke)

Dolžinske enote

1. podnaloga

Sestavite funkcijo milimetri(kolicina, enota), ki razdaljo, podano z argumentoma kolicina in enota, pretvori v milimetre. Pri tem je kolicina neko število, enota pa je eden od nizov 'm', 'dm', 'cm' ali 'mm', ki predstavlja enoto. Primer:

>>> milimetri(3, 'm')
3000
>>> milimetri(15, 'cm')
150

Uradna rešitev

def milimetri(kolicina, enota):
    '''Koliko mm je to?'''
    if enota == 'm':
        odg = 1000 * kolicina
    elif enota == 'dm':
        odg = 100 * kolicina
    elif enota == 'cm':
        odg = 10 * kolicina
    else:
        odg = kolicina
    return odg

def milimetriV2(kolicina, enota):
    '''Koliko mm je to?'''
    if enota == 'm':
        odg = 1000 * kolicina
    elif enota == 'dm':
        odg = 100 * kolicina
    elif enota == 'cm':
        odg = 10 * kolicina
    else:
        odg = kolicina
    return odg

2. podnaloga

Sestavite funkcijo milimetriV2(kolicina, enota), ki razdaljo, podano z argumentoma kolicina in enota, pretvori v milimetre. Pri tem je kolicina neko število, enota pa je zagotovo eden od nizov 'm', 'dm', 'cm' ali 'mm', ki predstavlja enoto. Primer:

>>> milimetri(3, 'm')
3000
>>> milimetri(15, 'cm')
150

Pri tem ne smeš uporabiti ne gnezdenih stavkov if, ne dela else. Namig: v funkciji imamo lahko več ukazov return

Uradna rešitev

def milimetriV2(kolicina, enota):
    '''Koliko mm je to?'''
    if enota == 'm':
        return 1000 * kolicina
    # če smo prišli sem - m zagotovo niso
    if enota == 'dm':
        return 100 * kolicina
    if enota == 'cm':
        return 10 * kolicina
    # torej gre za mm
    return kolicina

3. podnaloga

Sestavite funkcijo primerjaj(mera1, enota1, mera2, enota2), ki za dve podani razdalji pove, katera je večja. Prva razdalja je podana v spremenljivkah mera1 in enota1, druga pa v spremenljivkah mera2 in enota2

Funkcija naj vrne prva, če je prva razdalja večja od druge; enaki, če sta razdalji enaki in druga, če je druga razdalja večja od prve. Primer:

>>> primerjaj(2, 'm', 3, 'm')
druga
>>> primerjaj(1, 'm', 100, 'cm')
enaki

Uradna rešitev

def primerjaj(mera1, enota1, mera2, enota2):
    '''Katera razdalja je večja'''
    # pretvorimo obe razdalji v mm
    razlika = milimetri(mera1, enota1) - milimetri(mera2, enota2)
    if razlika > 0:
        return 'prva'
    if razlika < 0:
        return 'druga'
    # ni se izvedel noben izmed gornjih ukazov return, torej ni bil izpolnjen noben pogoj...
    return 'enaki'

4. podnaloga

Sestavite funkcijo primerjajEnoti(enota1, enota2), ki za dve dolžinski enoti pove, katera je večja. Funkcija naj vrne 1, če je prva enota večja od druge, 0, če sta enoti enaki, in -1, če je druga enota večja od prve. Primer:

>>> primerjajEnoti('dm', 'cm')
1
>>> primerjajEnoti('mm', 'm')
-1

Namig: Neposredno primerjanje nizov bo dalo napačen rezultat!

Uradna rešitev

def primerjajEnoti(enota1, enota2):
    '''1 - večja je enota1
       0 - enaki enoti
       -1 - večja je enota2 '''
    primerjava = primerjaj(1, enota1, 1, enota2) # uporabimo funkcijo prejšnje naloge
    if primerjava == 'prva':
        return 1
    if primerjava == 'druga':
        return -1
    return 0

Indiana Jones

1. podnaloga

Indiana Jones najde v starem templju tri zlate kipce, ker pa ima samo dve roki, mora enega, katerega vrednost je najmanjša, pustiti tam. Sestavi funkcijo indiana(stevilo1, stevilo2, stevilo3), ki kot argumente sprejme vrednosti kipcev ter vrne podatek (niz) katera dva kipca se najbolj splača vzeti s sabo.

Možne rešitve so:

če so vrednosti kipcev različne, ali če se dve vrednosti ponovita in sta večji od tretje:

"Vzeti mora kipca 2 in 3."
"Vzeti mora kipca 1 in 3."
"Vzeti mora kipca 1 in 2."

če so vrednosti vseh kipcev enake:

"Vzame lahko katerakoli dva kipca."

če se dve vrednosti ponovita in sta manjši od tretje:

"Vzeti mora kipec 3 ter enega od preostalih dveh."
"Vzeti mora kipec 2 ter enega od preostalih dveh."
"Vzeti mora kipec 1 ter enega od preostalih dveh."

Uradna rešitev

def indiana(stevilo1, stevilo2, stevilo3):
    '''vrne podatek, katera kipca se splača vzeti'''
        
    #če so vrednosti vseh kipcev enake, vzamemo poljubna kipca
    if stevilo1 == stevilo2 == stevilo3:
        return "Vzame lahko katerakoli dva kipca."
    
    #če je vrednost prvega kipca manjša od ostalih, izločimo prvi kipec
    #podobno naprej
    if stevilo1 < min(stevilo2, stevilo3):
        return "Vzeti mora kipca 2 in 3."
    if stevilo2 < min(stevilo1, stevilo3):
        return "Vzeti mora kipca 1 in 3."
    if stevilo3 < min(stevilo1, stevilo2):
        return "Vzeti mora kipca 1 in 2."

        
    #če se dve vrednosti ponovita, potem ločimo primer, ko je tretja vrednost ali večja ali manjša od prvih dveh
    #če je manjša, vzamemo kipca s prvima dvema vrednostima
    #če je večja, vzamemo kipec s tretjo vrednostjo in izbiramo med ostalima kipcema
    if (stevilo1 == stevilo2) and (stevilo3 != stevilo2):
        if stevilo1 < stevilo3:
            return "Vzeti mora kipec 3 ter enega od preostalih dveh."
        else:
            return "Vzeti mora kipca 1 in 2."
    if (stevilo1 == stevilo3) and (stevilo3 != stevilo2):
        if stevilo1 < stevilo2:
            return "Vzeti mora kipec 2 ter enega od preostalih dveh."
        else:
            return "Vzeti mora kipca 1 in 3."
    if (stevilo2 == stevilo3) and (stevilo1 != stevilo2):
        if stevilo2 < stevilo1:
            return "Vzeti mora kipec 1 ter enega od preostalih dveh."
        else:
            return "Vzeti mora kipca 2 in 3."

2. podnaloga

Sedaj pa sestavi funkcijo izguba(stevilo1, stevilo2, stevilo3), ki kot argumente sprejme vrednosti kipcev ter vrne koliko bo Indiana Jones žal moral pustiti v templju Tako:

     >>>izguba(7, 2, 3)
     2
     >>>izguba(7, 8, 7)
     7

Uradna rešitev

def izguba(stevilo1, stevilo2, stevilo3):
    '''vrne vrednost, ki jo bo Indiana Jones moral pustiti'''
    return min(stevilo1, stevilo2, stevilo3)

3. podnaloga

Sedaj pa sestavi funkcijo kateriVrednosti(stevilo1, stevilo2, stevilo3), ki kot argumente sprejme vrednosti kipcev ter vrne po velikosti urejen par vrednosti kipcev, ki ju bo Indiana Jones vzel Tako:

     >>>kateriVrednosti(7, 2, 3)
     (3, 7)
     >>>kateriVrednosti(7, 8, 7)
     (7, 8)

Uradna rešitev

def kateriVrednosti(stevilo1, stevilo2, stevilo3):
    '''vrne vrednost, ki jo bo Indiana Jones moral pustiti'''
    najv = max(stevilo1, stevilo2, stevilo3)
    najm = min(stevilo1, stevilo2, stevilo3)
    srednje = stevilo1 + stevilo2 + stevilo3 - najv - najm
    return (srednje, najv)

Preseki in unije pravokotnikov

1. podnaloga

Sestavite funkcijo nad_interval(a1, b1, a2, b2), ki izračuna najmanjši interval, ki vsebuje intervala $[a_1, b_1]$ in $[a_2, b_2]$. Funkcija naj vrne par števil, ki predstavlja krajišča intervala.

Primer:

>>> nad_interval(3, 7, 1, 2)
(1, 7)
>>> nad_interval(3, 7, 1, 5)
(1, 7)
>>> nad_interval(3, 7, 7, 8)
(3, 8)

Uradna rešitev

def nad_interval(a1, b1, a2, b2):
    '''interval, ki vsebuje oba dana intervala'''
    a3 = min(a1, a2)
    b3 = max(b1, b2)
    return (a3, b3)

2. podnaloga

Sestavite funkcijo presek_intervalov(a1, b1, a2, b2), ki izračuna največji interval, vsebovan v intervalih $[a_1, b_1]$ in $[a_2, b_2]$, torej njun presek. Če je presek neprazen, naj funkcija vrne par števil, ki predstavlja krajišča intervala, sicer pa naj funkcija vrne par (0,-1).

Primer:

>>> presek_intervalov(3, 7, 1, 2)
(0,-1)
>>> presek_intervalov(3, 7, 1, 5)
(3, 5)
>>> presek_intervalov(3, 7, 7, 8)
(7, 7)

Uradna rešitev

def presek_intervalov(a1, b1, a2, b2):
    '''Vrne presek intervalov (če obstaja), sicer pa (0,-1)''' 
    a3 = max(a1, a2)
    b3 = min(b1, b2)
    if a3 > b3:
        return (0,-1)
    else:
        return (a3, b3)

3. podnaloga

Sestavi funkcijo pravokotnika(a1x, a1y, b1x, b1y, a2x, a2y, b2x, b2y), ki bo izračunala obseg in površino unije dveh pravokotnikov. Stranice pravokotnikov so vzporedne koordinatnima osema. Vsak pravokotnik je podan s koordinatami (katerihkoli) dveh nasprotnih si oglišč.

Primer:

>>> pravokotnika(1, 1, 5, 5, 6, 8, 8, 3)
(30, 26)
>>> pravokotnika(1, 1, 5, 5, 4, 8, 8, 3)
(28, 34)

Uradna rešitev

def pravokotnika(a1x, a1y, b1x, b1y, a2x, a2y, b2x, b2y):
    '''Izračuna obseg in površino unije dveh pravokotnikov'''
    # poskrbimo, da je A1 levo spodaj in B1 desno zgoraj
    if a1x > b1x:
        a1x, b1x = b1x, a1x
    if a1y > b1y:
        a1y, b1y = b1y, a1y
    # poskrbimo, da je A2 levo spodaj in B3 desno zgoraj
    if a2x > b2x:
        a2x, b2x = b2x, a2x
    if a2y > b2y:
        a2y, b2y = b2y, a2y
        
    # določimo točki, ki določata presek A3 je levo spodaj in B3 desno zgoraj
    # če preseka ni, sta seveda ti točki "čudni"
    ax3 = max(a1x, a2x)
    ay3 = max(a1y, a2y)
    bx3 = min(b1x, b2x)
    by3 = min(b1y, b2y)

    # izračunamo ploščino prvega in drugega pravok
    pl1 = (b1x - a1x) * (b1y - a1y)
    pl2 = (b2x - a2x) * (b2y - a2y)
    ob1 = 2 * ((b1x - a1x) + (b1y - a1y))
    ob2 = 2 * ((b2x - a2x) + (b2y - a2y))

    # izračunamo pl in obseg preseka
    pl3 = 0
    ob3 = 0
    # pogledamo, če sploh presek je! Dovolimo tudi samo črto (torej če ene stranuice "ni")!
    if bx3 >= ax3 and by3 >= ay3:
        pl3 = (bx3 - ax3) * (by3 - ay3)
        ob3 = 2 * ((bx3 - ax3) + (by3 - ay3))

    obsegRezultata = ob1 + ob2 - ob3
    ploscinaRezultata = pl1 + pl2 - pl3
    

    return(obsegRezultata, ploscinaRezultata)

Bratska igra

Za nalogo potrebuješ le znanje definiranja funkcij in stavka if

1. podnaloga

Matej se rad igra s kamenčki in jih cel dan nabira naokrog. Zvečer pa ga neskončno zabava, ko njegov mljši brat poskuša uganiti, koliko jih je nabral.

Če brat pove premajhno število, mu Matej odgovori 'več', če pove preveliko število, mu Matej odgovori 'manj', če pa število ugane mu Matej odgovori 'točno'.

Setavite funkcijo ugibaj(matej, brat), ki simulira en korak zgornje igre. Funkcija naj Matejev odgovor vrne kot niz.

Primer:

>>> ugibaj(8, 5)
'več'

Uradna rešitev

def ugibaj(matej, brat):
  '''Kako odgovori Matej'''
  if matej > brat:
    return 'več'
  elif matej < brat:
    return 'manj'
  else:
    return 'točno'

2. podnaloga

Da bi igro popestril, je Matej razdelil kamenčke v obe roki, brat pa mora uganiti število kamenčkov v vsaki roki posebej. Ob vsakem poskusu Matej za vsako izmed rok izračuna absolutno razliko med pravim številom kamenčkov in bratovim poskusom ter bratu pove manjše izmed teh dveh števil.

Sestavite funkcijo ugibaj1(matejLR, matejDR, bratLR, bratDR), ki sprejme štiri nenegativna cela števila in Matejev odgovor vrne kot število. Če brat ugane obe števili, naj funkcija vrne -100. Primer:

>>> ugibaj1(8, 10, 5, 3)
5

Uradna rešitev

import math 

def ugibaj1(matejLr, matejDr, bratLr, bratDr):
  '''Kakšna je največja razlika'''
  razlika1 = abs(matejLr - bratLr)
  razlika2 = abs(matejDr - bratDr)
  najvecja = min(razlika1, razlika2)
  if najvecja == 0: # uganjeni sta obe
    return -100
  return najvecja

3. podnaloga

Zgornja igra pa ni bila preveč všeč Matejevemu bratu, saj je običajno trajala predolgo in je zato brat še pred koncem igre že zaspal. Zato je od Mateja zahteval, da mu za vsako roko pove še, ali je število kamenčkov v njej večje, manjše oz. enako ugibanemu. Mateju pa se je to zdelo preveč, zato sta se odločila za kompromis: Matej bo bratu povedal le, ali je brat obakrat povedal preveliko število, obakrat premajhno ali pa enkrat premajhno in drugič preveliko. Ustrezni Matejevi odgovori so 'Obakrat preveč.', 'Obakrat premalo.' in 'Preveč in premalo.'. Če brat ugane eno izmed števil, drugega pa ne, se Matej pretvarja, kot da je brat namesto pravega števila povedal preveliko število.

Sestavite funkcijo ugibaj2(matejLr, matejDr, bratLr, bratDr), ki sprejme štiri nenegativna cela števila in Matejev odgovor vrne kot niz. Če brat ugane obe števili, naj funkcija vrne niz 'Bravo!'

Primer:

>>> ugibaj2(4, 3, 4, 6)
'Obakrat preveč'

Uradna rešitev

def ugibaj2(matejLr, matejDr, bratLr, bratDr):
  '''Kaj odgovori Matej'''
  razlika1 = bratLr - matejLr
  razlika2 = bratDr - matejDr
  
  if razlikaLr == 0 and razlikaDr == 0:
    return 'Bravo!'
  elif razlikaLr >= 0 and razlikaDr >= 0:
    return 'Obakrat preveč.'
  elif razlikaLr < 0 and razlikaDr < 0:
    return 'Obakrat premalo.'
  else:
    return 'Preveč in premalo.'

def ugibaj2(matejLr, matejDr, bratLr, bratDr):
  '''Kaj odgovori Matej'''
  # za razliko od prej, upoštevamo, da return zaključi funkcijo, torej ne potrebujemo elif

  razlikaLr = bratLr - matejLr
  razlikaDr = bratDr - matejDr
  
  if razlikaLr == 0 and razlikaDr == 0:
    return 'Bravo!'
  if razlikaLr >= 0 and razlikaDr >= 0:
    return 'Obakrat preveč.'
  if razlikaLr < 0 and razlikaDr < 0:
    return 'Obakrat premalo.'
  return 'Preveč in premalo.'

Naključna števila, modul time ...


Hitri prsti

Če na začetek programa napišemo

  import time

bomo, med drugim, dobili funkcijo time, ki s klicem time.time() vrne čas v sekundah od nekega trenutka v davni preteklosti.

1. podnaloga

Kako hitro razmišljamo

Napiši program, ki uporabnika vpraša, koliko je a krat b. pri tem sta a inb dve naključni enomestni naravni števili. Uporabnik bo premislil in vpisal odgovor. Program naj izpiše, koliko sekund je človek potreboval za razmišljanje, ter, če je odgovor pravilen. Namig: če veš, koliko je bila ura pred klicem funkcije input in koliko je bila ura po klicu, znaš izračunati, koliko časa je minilo vmes.

>>>Koliko je 6 x 7: 42
Čestitam! Odgovor je pravilen. Za odgovor si potreboval 2.503019332885742 s.
  • Naloga nima testnega programa! *

Uradna rešitev

import time
import random
a = random.randint(1, 9)
b = random.randint(1, 9)
začetek = time.time()
odg = int(input('Koliko je ' + str(a) + ' x ' + str(b) + ': '))
konec = time.time()
if odg == a * b:
    print('Čestitam! Odgovor je pravilen.', end = " ")
else:
    print('Žal narobe! Prav je ' + str(a*b) + '!', end = " ")
print('Za odgovor si potreboval', konec - začetek, 's')

2. podnaloga

Misleči stroj I

Oglej si, kaj počne naslednji program:

import time

a = int(input('Prvo število: '))
b = int(input('Drugo število: '))
print('Računam ...')
time.sleep(3)
print('Uf, je bilo težko. V 3 sek so se moji čipi pošteno ogreli. Rezultat je:', a*b)

nato ga spremeni tako, da bo računalnik razmišljal med 1 in 5 sekundami! Pri tem lahko čaka tudi npr. 4.371232 sekunde ali pa točno 5! Oglej si dokumentacijo in poišči ustrezno funkcijo. Čas razmišljanja izpiši zaokroženo na tisočinke!

Uradna rešitev

import time
import random

a = int(input('Prvo število: '))
b = int(input('Drugo število: '))
print('Računam ...')
časČakanja = random.uniform(1, 5)
time.sleep(časČakanja)
print('Uf, je bilo težko. V', round(časČakanja,3), 'sek so se moji čipi pošteno ogreli. Rezultat je:', a*b)

3. podnaloga

Misleči stroj II

Večji kot je produkt, težji je račun. Računalnik naj zato čaka toliko sekund, kolikor je velik produkt, deljeno z 10. Če mu damo množiti 6 in 7, naj pred izpisom počaka 4.2 sekunde. Če je katero od števil negativno, naj vedno čaka 2 sekundi več, kot bi čakal pri pozitivnih številih. V nobenem primeru pa čas čakanja ne sme biti več kot 10 sekund.

Izpis naj bo tak, kot ga predvideva zgornji program!

Uradna rešitev

import time

a = int(input('Prvo število: '))
b = int(input('Drugo število: '))
print('Računam ...')
časČakanja = abs(a * b) / 10
if a < 0 or b < 0: # za negativna števila
    časČakanja = časČakanja + 2
if časČakanja > 10:
    časČakanja = 10
time.sleep(časČakanja)
print('Uf, je bilo težko. V', časČakanja, 'sek so se moji čipi pošteno ogreli. Rezultat je:', a*b)

Okulist Tone

1. podnaloga

Okulist Tone želi svojim strankam poslati program za kontrolo vida. Napiši mu program, ki bo izpisal med 10 in 20 znakov *, ločenih s presledki. Uporabnik jih mora prešteti in vnesti odgovor. Na podlagi odgovora naj program izpiše, ali mora uporabnik k okulistu Tonetu ali ne.

    >>>* * * * * * * * * *
    Koliko * vidite: 11
    Prosim, zglasite se pri okulistu Tonetu.

    >>>* * * * * * * * * * * * *
    Koliko * vidite: 13
    Vaš vid je še dober.

Uradna rešitev

import random

koliko = random.randint(10, 20)
vzorec = '* ' * koliko
print(vzorec)
odg = int(input('Koliko * vidite: '))
if odg == koliko:
    print('Vaš vid je še dober.')
else:
    print('Prosim, zglasite se pri okulistu Tonetu.')

Zanka while I


Podražitve

1. podnaloga

Nek izdelek naj bi se vsak teden podražil za 2%. Da bi razumeli, kaj to pomeni, napiši funkcijo kolikoTednov(cena), ki za dano začetno ceno (v €) tega izdelka vrne število tednov, ko bo cena dosegla ali presegla 1000€.

V dobrih 6 letih bi se to zgodilo s kavo, ki sedaj stane 1€!

Za šaljivce, ki bodo funkcijo poklicali z negativno ceno, vrni -1

Uradna rešitev

def kolikoTednov(cena):
    ''' v koliko tednih se ob 2% tedenski podražitvi cena dvigne nad 1000€'''
    končnaCena = 1000
    trenutnaCena = cena
    tednov = 0
    if trenutnaCena < 0:
        return -1
    while trenutnaCena < končnaCena:
        trenutnaCena = trenutnaCena * 1.02
        tednov += 1 # to je isto kot tednov = tednov + 1
    return tednov

Bakterije

1. podnaloga

Bakteriolog Bine preučuje bakterije. Naredil je sledeči poizkus: bakterije je zaprl v škatlico pravokotne oblike s pregrado na sredini, ki razdeli škatlico na dva kvadratna dela - poimenujmo ju regija A in regija B. Zunanje stene te škatlice so polprepustne, kar pomeni, da bacili lahko uidejo ven, vendar se ne morejo vrniti v škatlico, pregrada pa je prepustna, tj. bacili lahko prehajajo v obe smeri. Bine predvideva, da v neki časovni enoti 12 % vseh bacilov ostane v svoji regiji, po 22 % pa jih zapusti svojo regijo v vsaki od štirih smeri. Binetu sestavi program, ki bo prebral začetni masi bacilov (bacili so premajhni, da bi jih # šteli posamično, zato jih "štejemo" kar v gramih) v vsaki od regij in izpisal število korakov, dokler skupna masa bacilov v obeh regijah ne pade pod 0.001 grama. Tako bo lahko Bine primerjal teoretični izračun z izmerjenimi podatki in preveril svojo hipotezo. Predpostavi, da so podatki pravilni (nenegativni!

Pozor! Premisli, kaj se dogaja na pregradi!

Za začetni masi 22.4 in 13.5 se število bakterij giblje

1, 5.658, 6.548
2, 2.11952, 2.03052
3, 0.7010568, 0.7099568
4, 0.240317312, 0.239427312
5, 0.08151208608, 0.08160108608
6, 0.0277336892672, 0.0277247892672
7, 0.00942749635085, 0.00942838635085
8, 0.00320554455929, 0.00320545555929
9, 0.00108986557016, 0.00108987447016
10 0.0003035929996319583 0.0003035829997319583

zato bi program izpisal 10. Primer:

 >>>Masa v regiji A: 22.4
 Masa v regiji B: 13.5
 Korakov: 10

Uradna rešitev

# Določimo število tednov, ko se ustrezno zmanjša začetna porazdelitev bakterij
masaA = float(input('Masa v regiji A: '))
masaB = float(input('Masa v regiji B: '))
korak = 0
while masaA + masaB >= 0.001: # skupna teža je še prevelika
    staraA = masaA # potrebovali bomo za izračun B-ja
    masaA = masaA * 0.12  # toliko jih ostane
    masaA = masaA + masaB * 0.22 # toliko jih pa še pride iz B
    masaB = masaB * 0.12
    masaB = masaB + staraA * 0.22
    korak += 1 # en korak več
print('Korakov:', korak)

Vsote

1. podnaloga

Sestavite funkcijo vsotaKvadratov(n), ki izračuna in vrne vsoto $1^2 + 2^2 + 3^2 + \ldots + n^2$. Primer:

>>> vsotaKvadratov(10)
385

Uradna rešitev

def vsotaKvadratov(n):
    '''Vsota kvadratov'''
    s = 0
    for i in range(1, n + 1): # začnemo pri 1 in končamo pri n
        s += i * i
    return s

def vsotaKvadratovMat(n):
    '''No, če ste malo bolj "doma" v matematiki,
       je vaša rešitev seveda '''
    return n * (n + 1) * (2*n + 1) // 6

2. podnaloga

Sestavite funkcijo vsota(n), ki izračuna in vrne vsoto $1\cdot2 + 2\cdot3 + 3\cdot4 + \ldots + n\cdot(n+1)$. Primer:

>>> vsota(10)
440

Uradna rešitev

def vsota(n):
    '''Vsota vrste '''
    s = 0
    for i in range(1, n + 1): # začnemo pri 1 in končamo pri n
        s += i * (i + 1)
    return s

3. podnaloga

Sestavite funkcijo stevilo_clenov(m), ki izračuna, največ koliko členov vsote $1\cdot2 + 2\cdot3 + 3\cdot4 + \ldots + n\cdot(n+1) + \ldots$ lahko seštejemo, da bo dobljena vsota še vedno manjša ali enaka m. Primer:

>>> stevilo_clenov(20)
3

Uradna rešitev

def stevilo_clenov(m):
    '''Koliko členov vrste lahko seštejemo, da ne presežemo meje m'''
    vsotaVrste = 0
    if m < 1: # ne smemo sešteti nobenega
        return 0
    stČlenov = 0
    while vsotaVrste <= m:
        stČlenov += 1
        vsotaVrste += stČlenov * (stČlenov + 1)
    return stČlenov - 1 # z zadnjim smo že presegli mejo

4. podnaloga

Sestavite funkcijo najblizje(a, b, m), ki poišče takšno število k med a in b, pri katerem se z eno od delnih vsot $k\cdot(k+1) + (k+1)\cdot(k+2) + (k+2)\cdot(k+3) + \ldots$ najbolj približamo številu m. Če je takšnih števil več, naj funkcija vrne najmanjšega. Primer:

>>> najblizje(10, 20, 10000)
14

Uradna rešitev

def blizina(k, m):
    '''Kako najbližje se lahko z $k\cdot(k+1) + (k+1)\cdot(k+2) + (k+2)\cdot(k+3) + \ldots$
       približamo meji m '''
    sZgoraj = 0
    while sZgoraj <= m: # nehali bomo, ko bomo presegli mejo
        sZgoraj += k * (k + 1)
        k += 1
    sSpodaj = sZgoraj - (k - 1) * k # če se ustavimo prej, še ne presežemo m
    return min(sZgoraj - m, m - sSpodaj) # ali smo se meji bolj približali od spodaj, ali od zgoraj

def najblizje(a, b, m):
    '''Kje med a in b moramo začeti sešetvati člene vrste, da se najbolj približamo meji m'''
    najmanRaz, najK = blizina(a, m), a # kandidat je a
    for k in range(a + 1, b + 1): # še preostale
        razdalja = blizina(k, m)
        if razdalja < najmanRaz: # našli smo boljšega kandidata
            najmanRaz = razdalja
            najK = k
    return najK

Čaramo s celimi števili

1. podnaloga

Sestavi funkcijo obrniStevilo(n), ki obrne pozitivno celo število. Iz 234 torej naredi število 432. Če je podatek slučajno negativen, vrni 0!

Tisti, ki že poznate funcije in metode za delo z nizi, teh prijemov tukaj ne smete uporabiti. V tej nalogi torej nikjer ne smemo uporabljati nizov!

Uradna rešitev

def obrniStevilo(n):
    ''' Funkcija obrne pozitivno celo število'''
    if n < 0:
        return 0
    obrnjeno = 0
    while n > 0:
        obrnjeno = obrnjeno * 10  + n % 10
        n = n // 10
    return obrnjeno

2. podnaloga

Najprej si oglej naslednjo funkcijo. Ugotovi, kaj počne! Očitno jo je pisal nek programer začetnik, kar lahko zlahka ugotovimo po odsotnosti komentarjev in izbiri imen spremenljivk

   def f(a, b):
       d = a % b # koliko še ostane
       k = 0
       r = '.'
       while k <=  10: 
           d = d * 10
           nd = d // b
           r = r + str(nd) 
           k = k + 1 
           d = d % b 
       return r

Namig: pokliči jo s parametri 7, 3; 2, 4; 15, 7; 1, 3; 2, 3; 3, 3 in poskusi postaviti hipotezo, kaj se dogaja ...

Oborožen z vedenjem o zgornji funkciji sestavi funkcijo kvocient(a, b, n), ki bo vrnila celi del in prvih n decimalk kvocienta a/b v obliki niza. Delaj samo s pozitivnimi celimi števili in na koncu NE zaokrožuj

   >>>kvocient(10, 761, 40)
   0.0131406044678055190538764783180026281208 
   >>>kvocient(100, 3, 14)
   33.33333333333333

Namig: kako delimo "peš"?

Uradna rešitev

def kvocient(a, b, n):
    '''Izračunamo a/b na n decimalk (za dec. piko)'''
    celiDel = a // b
    rezultat = str(celiDel) + '.'
    delimo = a % b # koliko še ostane
    decimalke = 0
    while decimalke < n: # koliko decimalk potrebujemo
        delimo = delimo * 10
        novaDec = delimo // b
        rezultat = rezultat + str(novaDec) # dodamo decimalko
        decimalke = decimalke + 1 # je ena več
        delimo = delimo % b # za naslednji korak
    return rezultat

3. podnaloga

Programer Mihec je sestavil funkcijo vsebuje(a, k), ki naj bi povedala (True/False) če poljubno celo število a vsebuje števko k.

   def vsebuje(stevilo, k):
       ''' ali število stevilo vsebuje števko k '''
       while stevilo > 0:
           if stevilo % 10 == k:
               return True
           stevilo = stevilo // 10
       return False

Preizkusil jo je na storitvi Projekt Tomo, a je kar naprej dobival obvestila o napačnem rezultatu. Preizkusi jo še ti in jo ustrezno popravi!

Uradna rešitev

def vsebuje(stevilo, k):
    ''' ali število stevilo vsebuje števko k '''
    if stevilo == k == 0 :
        return True
    stevilo = abs(stevilo) # zaradi neg. števil!
    while stevilo > 0:
        if stevilo % 10 == k:
            return True
        stevilo = stevilo // 10
    return False

4. podnaloga

Sestavi funkcijo prestejVsebuje(a,b,k), ki prešteje, koliko števil med a in b (meji vključeni) vsebuje števko k. A pozor, lahko je tudi a >= b

Uradna rešitev

def prestejVsebuje(a, b, k):
    ''' prešteje koliko je med a in b števil, ki vsebujejo števko k '''
    mini = min(a, b)
    maksi = max(a, b)
    stevilo = mini
    koliko = 0 # koliko jih zadošča pogoju
    while stevilo <= maksi:
        if vsebuje(stevilo, k):
            koliko += 1
        stevilo = stevilo + 1
    return koliko

5. podnaloga

Sestavi funkcijo prestejPrave(a,b,k), ki prešteje, koliko števil med a in b (0 <= a <= b), meji vključeni, vsebuje števko k ali pa so ona ali pa njihovi obrati deljivi s k.

Uradna rešitev

def vsebuje(stevilo, k):
    ''' ali število stevilo vsebuje števko k '''
    if stevilo == k == 0 :
        return True
    while stevilo > 0:
        if stevilo % 10 == k:
            return True
        stevilo = stevilo // 10
    return False

def obrniStevilo(n):
    ''' Funkcija obrne pozitivno celo število'''
    obrnjeno = 0
    while n > 0:
        obrnjeno = obrnjeno * 10  + n % 10
        n = n // 10
    return obrnjeno

def prestejPrave(a, b, k):
    ''' prešteje koliko je med a in b števil, ki zadoščajo pogoju k '''
    stevilo = a
    koliko = 0 # koliko jih zadošča pogoju
    while stevilo <= b:
        if vsebuje(stevilo, k) or stevilo % k == 0 or obrniStevilo(stevilo) % k == 0:
            koliko += 1
        stevilo = stevilo + 1
    return koliko

Zlati rez

Pravimo, da sta števili $a$ in $b$ v razmerju zlatega reza, kadar je $a : b$ enako $(a + b) : a$, kar je takrat, ko je $\frac{a}{b}$ enako številu $\phi = \frac{1 + \sqrt{5}}{2}$.

Približek števila $\phi$ lahko izračunamo z zaporedjem $\phi_0, \phi_1, \phi_2, \dots$, kjer je $\phi_0 = 1$, naslednji približek $\phi_{n + 1}$ pa izračunamo kot $\phi_{n + 1} = 1 + 1 / \phi_n$.

1. podnaloga

Sestavite funkcijo naslednjiPriblizek(x), ki iz približka x po zgornjem postopku izračuna naslednji približek števila $\phi$.

Uradna rešitev

def naslednjiPriblizek(x):
    '''Naslednji približek za zlati rez'''
    return 1 + 1 / x

2. podnaloga

Sestavite funkcijo priblizek(k), ki izračuna k. približek števila $\phi$. Za začetni približek (ko je k enak $0$) vzamite število $1$.

Uradna rešitev

def priblizek(k):
    '''K-ti približek za zlati rez, če začnemo z 1'''
    x = 1
    poskus = 1 #kateri člen zaporedja
    while poskus <= k:
        x = naslednjiPriblizek(x)
        poskus += 1
    return x

3. podnaloga

Sestavite funkcijo natancniPriblizek(eps), ki izračuna prvi približek števila $\phi$, ki se od prejšnjega približka razlikuje za manj kot pozitivno realno število eps.

Uradna rešitev

def natancniPriblizek(eps):
    '''Približek, ki se od prejšnjega razlikuje za manj kot eps (začnemo z 1)'''
    prejsnji = 1
    naslednji = naslednjiPriblizek(prejsnji)
    while abs(prejsnji - naslednji) >= eps:
        prejsnji = naslednji # zadnji je za naslednji korak prejšnji
        naslednji = naslednjiPriblizek(prejsnji)
    return naslednji

Kvadratni koren

Približke za kvadratni koren števila $n$ lahko izračunamo po naslednjem postopku. Začetni približek $x_0$ je enak $n / 2$. Vsak naslednji približek $x_{k + 1}$ pa izračunamo kot $(x_k + n / x_k) / 2$.

1. podnaloga

Sestavite funkcijo priblizek(n, k), ki po zgornjem postopku izračuna k. približek korena števila n.

Uradna rešitev

def priblizek(n, k):
    '''K-ti približek za kvadratni koren'''
    trenutniPrib = n / 2
    i = 0
    while i < k:
        trenutniPrib = (trenutniPrib + n / trenutniPrib) / 2
        i = i + 1
    return trenutniPrib

2. podnaloga

Sestavite funkcijo koren(n, eps), ki po zgornjem postopku izračuna prvi približek korena števila n, za katerega se kvadrat približka od n razlikuje za manj kot eps. Smislena vrednost za argument eps je npr. $10^{-6}$.

Uradna rešitev

def koren(n, eps):
    '''Približek za koren na eps natančno'''
    priblizek = n / 2
    while abs(priblizek ** 2 - n) > eps:
        priblizek = (priblizek + n / priblizek) / 2
    return priblizek

Zanka while in naključna števila


Približek za število π

S pomočjo naključnih števil bomo določili število π

1. podnaloga

Približek za število π lahko določimo tako, da izberemo n naključnih točk v kvadratu [0,1]×[0,1], ter preštejemo, koliko jih leži v krogu s središčem v (0,0) in polmerom 1. Če je n dovolj velik, mora biti razmerje med številom točk v krogu in številom vseh točk približno π/4, saj z veliko točkami "pokrijemo" kvadrat oz. del kroga, ki imata ploščini 1 oziroma π/4.

Sestavi funkcijo približekPi(stTočk), ki izračuna približek za število π po opisani metodi. Število naključnih točk funkcija dobi kot parameter.

Seveda math.pi nima v vaši funkciji kaj početi!

Uradna rešitev

import random
def približekPi(stTočk):
    '''S simulacijo izbire stTočk določi približek za Pi'''
    kolikoVKrogu = 0
    stPoskusa = 1
    while stPoskusa <=  stTočk: # naredimo toliko "poskusov"
        # naključna točka v kvadratu
        x = random.random()
        y = random.random()
        razdalja = x*x + y*y
        if razdalja <= 1 :
            kolikoVKrogu += 1
        stPoskusa = stPoskusa + 1
    # izračunamo razmerje
    razm = kolikoVKrogu / stTočk
    return razm * 4

Mečemo kocko

1. podnaloga

Sestavi funkcijo dvakratZapored(), ki vrne kolikokrat smo vrgli kocko, da smo dvakrat zapored vrgli 6. če npr. mečemo

    2, 6, 3, 4, 6, 4, 5, 6, 6, ...

smo torej kocko morali vreči 9x, da smo dvakrat zapored vrgli 6.

Uradna rešitev

import random
def dvakratZapored():
    '''Kolikometov je potrebnih, da dvakrat zapored vržemo 6'''
    številoMetov = 0
    prejšniMet = 42 # koliko smo vrgli v prejšnem metu (karkoli razen 6!)
    while True : # kar neskončna zanka, izstopili bomo z return
        met = random.randint(1,6)
        številoMetov += 1
        if (met == 6) and (prejšniMet == 6):
            return številoMetov
        prejšniMet = met

2. podnaloga

Sestavi funkcijo verjetnostDveh6(n), ki na podlagi n klicev prejšnje funkcije dvakratZapored() ugotovi, kolikokrat smo v povprečju v n poskusih morali vreči kocko, da smo dvakrat zapored vrgli 6.

Uradna rešitev

import random
def verjetnostDveh6(stPoskusov):
    skupnoŠtMetov = 0
    poskus = 1
    while poskus <= stPoskusov:
        številoMetov = dvakratZapored() # opravimo eno poskus
        skupnoŠtMetov = skupnoŠtMetov + številoMetov
        poskus = poskus + 1
    # verjetnost
    return skupnoŠtMetov / stPoskusov

Enake decimalke

Ogledali si bomo koliko naključnih števil mora računalnik generirati, da dobi tako s samimi enakimi decimalkami

1. podnaloga

Spodnja funkcija vrne vsoto prvih petih decimalk števila x.

Pri tem je predpostavka, da pri računanju abs(x - int(x)) ne pride do zaokrožitvenih napak!!!!! Predelaj jo v funkcijo z imenom enakeDecimalke(x, k) tako, da bo preverila, ali ima realno število x prvih k decimalk enakih.

def vsota(x):
    '''Vsota prvih 5 decimalk števila x'''
    vs = 0
    x = abs(x - int(x))
    i = 1
    while i <= 5:
        x = x * 10
        decim = int(x)
        vs = vs + decim
        x = x - decim
        i = i + 1
    return vs

Opomba: Načeloma se zadeve ne da narediti čisto prav. Če npr. uradno rešitev preizkusimo na

enakeDecimalke(2.2222,4)

dobimo napačen rezultat! Oglej si zakaj, tako da izračunaš x - int(x) tega števila. K nalogi se bomo vrnili kasneje in si pomagali z nizi!

Uradna rešitev

def enakeDecimalke(x, k):
     ''' ali ima število x prvih k decimalk enakih '''
     s = 0
     x = abs(x - int(x)) # decimalni del
     prvaDecimalka = int(x*10)
     i = 1
     while i <= k:
         x = x * 10
         trenutnaDecimalka = int(x)
         if prvaDecimalka != trenutnaDecimalka :
             return False # napačna decimalka!
         x = x - int(x) # še preostale decimalke
         i = i + 1
     return True # vsi testi so bili uspešni

2. podnaloga

Uporabi funkcijo enakeDecimalke v novi funkciji z imenom kolikoGenerirati(k), ki bo preštela, koliko naključnih realnih števil med 0 in 1 moramo generirati, da bomo dobili decimalno število, ki ima prvih k decimalk enakih. Funkcija naj vrne tudi to število - vrne torej par (koliko, nakŠtevilo)!

Uradna rešitev

import random
def kolikoGenerirati(k):
    koliko = 0 # števec generiranih nak. števil
    jeUstrezno = False
    while not jeUstrezno : #Dokler ne bomo naleteli na ustrezno število
        nakŠtevilo = random.random()
        koliko += 1
        if enakeDecimalke(nakŠtevilo, k) : # je število ustrezno?
            jeUstrezno = True #skočimo iz zanke
    return (koliko, nakŠtevilo)

Pogojni stavek, zanka while ...


Zavarovalnica

1. podnaloga

Zavarovalnica zavarovalno premijo za avtomobilska zavarovanja izračuna takole:

Osnovni znesek je 150 €.

K temu se prišteje znesek, ki je odvisen od prijavljenih zavarovalniških zahtevkov stranke v preteklem letu. Če je bil en zahtevek, se prišteje 30 €; če sta bila dva, se prišteje 50 €; za več kot dva zahtevka pa se prišteje 80 €.

Nadalje se k premiji prišteje fiksna vsota 40 €, če je zavarovanec mladi voznik, tj. če je mlajši od 21 let ali pa ima vozniško dovoljenje manj kot dve leti.

Na koncu se še upošteva, če stranka plačilo poravna z gotovino (5 % popust) ali na obroke (7 % več).

Napišite program, ki glede na podatke o stranki (te preberemo tako, kot kaže zgled) izračuna višino premije (zaokroženo na dve decimalki) in jo izpiše.

Primer:

   Starost voznika: 25
   Koliko let vozniških izkušenj: 5
   Koliko zavarovalnih zahtevkov: 2
   Način plačila (gotovina/na obroke): gotovina
   Premija znaša: 190.0

Uradna rešitev

starost = int(input('Starost voznika: '))
izkusnje = int(input('Koliko let vozniških izkušenj: '))
zahtevki = int(input('Koliko zavarovalnih zahtevkov: '))
placilo = input('Način plačila (gotovina/na obroke): ')


premija = 150 # osnovni znesek

if zahtevki == 1:
    premija += 30
elif zahtevki == 2:
    premija += 50
elif zahtevki > 2:
    premija += 80

if starost < 21 or izkusnje < 2:
    premija += 40

if placilo == "gotovina":
    premija -= 0.05 * premija
else:
    premija += 0.07 * premija
print('Premija znaša:', round(premija,2))

2. podnaloga

Zavarovalni agenti so bili sicer zadovolji s programom, vendar so imeli težave z napačnim vnosom. Zato popravi program tako, da bo:

  • uporabnike spraševal po starosti, dokler ne bo ta med 18 in 100
  • "sitnaril", dokler ne bo vnos plačila res gotovina ali na obroke

Primer (tudi tvoj program mora brati podatke v istem vrstnem redu, torej starost, izkušnje, zahtevki, način plačila):

   Starost voznika: 15
   Starost voznika: 25
   Koliko let vozniških izkušenj: 5
   Koliko zavarovalnih zahtevkov: 2
   Način plačila (gotovina/na obroke): kartica
   Način plačila (gotovina/na obroke): pare na sunce
   Način plačila (gotovina/na obroke): gotovina
   Premija znaša: 190.0

To seveda niso edine kotrole, ki bi bile smislene, a ...

Uradna rešitev

starost = 0
while not (18 <= starost <= 100):
    starost = int(input('Starost voznika: '))
izkusnje = int(input('Koliko let vozniških izkušenj: '))
zahtevki = int(input('Koliko zavarovalnih zahtevkov: '))
plačilo = ''
while plačilo != "gotovina" and plačilo != "na obroke":
    plačilo = input('Način plačila (gotovina/na obroke): ')


premija = 150 # osnovni znesek

if zahtevki == 1:
    premija += 30
elif zahtevki == 2:
    premija += 50
elif zahtevki > 2:
    premija += 80

if starost < 21 or izkusnje < 2:
    premija += 40

if plačilo == "gotovina":
    premija -= 0.05 * premija
else:
    premija += 0.07 * premija
print('Premija znaša:', round(premija,2))

Vsote števk

1. podnaloga

Sestavite funkcijo vsota_stevk(n, k), ki vrne vsoto tistih števk celega števila n, ki so večje ali enake k. Kako bi izračunal vsoto vseh števk?

Uradna rešitev

def vsota_stevk(n, k):
    '''Vrne vsoto števk števila n, ki so večje ali enake k'''
    vsota = 0
    n = abs(n) # za primer negativnih števil
    while n > 0:
        stevka = n % 10 # števke gledamo od zadaj (enice)
        if stevka >= k: # ali gre k vsoti 
            vsota += stevka
        n //= 10 # odrežemo že pregledane enice
    return vsota

2. podnaloga

Sestavite funkcijo vsota_stevk_stevil_med(m, n), ki vrne vsoto števk vseh števil med vključno m in n.

Uradna rešitev

def vsota_stevk_stevil_med(m, n):
    '''Vsota števk vseh števil med m in n'''
    vsota = 0
    stevilo = m 
    while stevilo <= n:
        vsota += vsota_stevk(stevilo, 0) # uporabimo funkcijo prejšnje naloge
        stevilo += 1
    return vsota

3. podnaloga

Sestavite funkcijo najmanjse_stevilo_z_vsoto_stevk(n), ki izračuna točno to, kar piše v njenem imenu.

Uradna rešitev

def najmanjse_stevilo_z_vsoto_stevk(n):
    '''Vrne najmanjše število z vsoto števk n'''
    # matematični premislek pove, da se bno število končalo z največ možnimi 9kami
    # spredaj pa, kar še ostane. In če temu prištejemo 1, dobimo zapis števila (ki mu potem odštejemo 1)
    return (n % 9 + 1) * 10 ** (n // 9) - 1
    # 
    # če pa je zadeva videti preveč "zavita", lahko isto idejo udejanimo
    # tudi takole:
    #
    # koliko9 = n // 9 # koliko 9 potrebujemo
    # stev = str(n % 9) + '9' * koliko9
    # return int(stev)

Restavracija

1. podnaloga

V restavraciji morajo nahraniti veliko lačnih ust, zato vsak dan naročijo nekaj hrane. Ker želijo varčevati, obdržijo nekaj hrane iz zaloge, nekaj pa jo naročijo na novo.

Sestavite funkcijo kolikoHrane1(potrebujemo, zaloga), ki vrne količino hrane, ki jo je potrebno dokupiti. V spremenljivki potrebujemo je zapisana količina potrebne hrane, v spremenljivki zaloga pa količina hrane na zalogi. Če hrane ni potrebno dokupiti, naj funkcija vrne število 0. Predpostavite lahko, da sta oba argumenta nenegativni celi števili.

Primer:

>>> kolikoHrane1(15, 5)
10

Uradna rešitev

def kolikoHrane1(potrebujemo, zaloga):
  '''Koliko hrane je potrebno nakupiti'''
  if potrebujemo > zaloga:
    return potrebujemo - zaloga
  else:
    return 0
  # lahko bi napisali tudi brez else,
  # (seveda je return 0 potem ustrezno zamaknjen v levo!
  # saj return pri if tako ali tako zaključi program!
  # lahko pa rešitev skrčimo le v stavek
  #   return max(potrebujemo - zaloga, 0)

2. podnaloga

Zgornja metoda se ni najbolje obnesla, saj ni prav nič upoštevala, kakšno hrano restavracija potrebuje. Zato so hrano razdelili v tri osnovne kategorije. Imenujmo jih hrana tipa 1, tipa 2 in tipa 3.

Sestavite funkcijo kolikoHrane2(potrebujemo1, potrebujemo2, potrebujemo3, zaloga1, zaloga2, zaloga3), ki sprejme šest nenegativnih celih števil. V potrebujemoX je zapisana količina potrebne hrane tipa $X$, v zalogaX pa zaloga hrane tipa $X$.

Funkcija naj vrne tri cela števila (kot return a, b, c - seveda namesto a, b, c uporabimo smiselne spremenljivke). Število na $i$. mestu naj pove, koliko hrane tipa $i$ je treba dokupiti. Če hrane določenega tipa ni potrebno dokupiti, naj na ustrezno mesto zapiše število 0. Če ni potrebno dokupiti nobene hrane, naj funkcija vrne število 0.

Primer:

>>> kolikoHrane2(10,12,7, 12, 8, 5)
0, 4, 2

Uradna rešitev

def kolikoHrane2(potrebujemo1, potrebujemo2, potrebujemo3,
                 zaloga1, zaloga2, zaloga3):
  '''koliko hrane posameznega tipa je potrebno kupiti'''
  potrebnaHranaTip1 = kolikoHrane1(potrebujemo1, zaloga1)
  # brez funkcije kolikoHrane1 bi pač napisali
  # potrebnaHranaTip1 = max(potrebujemo1 - zaloga1, 0)
  potrebnaHranaTip2 = kolikoHrane1(potrebujemo2, zaloga2)
  potrebnaHranaTip3 = kolikoHrane1(potrebujemo3, zaloga3)
  if max(potrebnaHranaTip1, potrebnaHranaTip2, potrebnaHranaTip3) == 0:
      return 0
  return potrebnaHranaTip1, potrebnaHranaTip2, potrebnaHranaTip3

3. podnaloga

Zgornja metoda je požela velik uspeh, a še vedno je najbolj priljubljene hrane včasih zmanjkalo. Zato so se odločili, da bodo najbolj priljubljeno hrane naročili nekaj več, kot bi bilo nujno potrebno.

Sestavite funkcijo kolikoHrane3, ki se obnaša enako kot funkcija kolikoHrane1, le da:

  • če je na zalogi manj kot polovica potrebne hrane, vrne dvakratnik razlike med zalogo in potrebno količino.
  • če je na zalogi manj kot četrtina potrebne hrane, vrne trikratnik razlike med zalogo in potrebno količino.

Primer:

>>> kolikoHrane3(15, 5)
20

Uradna rešitev

def kolikoHrane3(potrebujemo, zaloga):
  '''Koliko hrane je potrebno kupiti, če kupimo nekaj na zalogo'''
  if zaloga < potrebujemo/4: 
    return (potrebujemo - zaloga) * 3
  elif zaloga < potrebujemo/2: 
    return (potrebujemo - zaloga) * 2
  elif zaloga < potrebujemo:
    return potrebujemo - zaloga
  else:
    return 0

Ugibaj točke

1. podnaloga

Miha se rad igra igro ugibanja števil. V ta namen bi rad napisal računalniški program, ki bi mu izžrebal število, on pa bi ga ugibal. V pomoč mu sestavite funkcijo relacija(racunalnik, miha), ki sprejme dve celi števili. Funkcija naj vrne niz 'premalo', če je število miha manjše od števila racunalnik; niz 'preveč', če je število miha večje od števila racunalnik in niz 'Bravo!', če sta števili enaki. Primer:

>>> relacija(45, 20)
'premalo'

Uradna rešitev

def relacija(racunalnik, miha):
  '''je miha povedal premalo, preveč ali točno'''
  if racunalnik > miha:
    return 'premalo'
  if racunalnik < miha:
    return 'preveč'
  return 'Bravo!'

2. podnaloga

Mihu je ugibanje v eni dimenziji počasi postalo predolgočasno. Zato je začel ugibati števila, ki ležijo na celoštevilski mreži v ravnini. V ta namen sestavite funkcijo ugibaj(racunalnikX, racunalnikY, mihaX, mihaY), ki sprejme seznama dveh celih števil (točk v ravnini).

Vrne naj niz 'Od prave točke si oddaljen vsaj {0}.', če Miha točke ni uganil in niz 'Bravo!', če je Miha točko uganil. Pri tem naj bo namesto {0} izpisana Evklidska razdalja med točkama miha in racunalnik, zaokrožena navzdol na najbližje celo število.

Primer:

>>> ugibaj(15, 15, 16, 16)
'Od prave točke si oddaljen vsaj 1.'

Uradna rešitev

def ugibaj(racunalnikX, racunalnikY, mihaX, mihaY):
  '''Kako daleč od točke smo'''
  razdalja = int(((racunalnikX - mihaX)**2 + (racunalnikY - mihaY)**2)**0.5)
  if razdalja == 0:
    return 'Bravo!'
  else:
    return 'Od prave točke si oddaljen vsaj ' + str(razdalja) + '.'

3. podnaloga

Miha se je tudi igre v dveh dimenzijah hitro naveličal, saj je trajala predolgo. Zato si je poleg oddaljenosti od izžrebane točke zaželel vedeti še, kje leži njegova točka glede na izžrebano število: v prvem, drugem, tretjem ali četrtem kvadrantu.

Pri tem k prvemu kvadrantu štejemo tudi pozitivno $x$-os, k drugemu pozitivno $y$-os, k tretjemu negativno $x$-os in k četrtemu negativno $y$-os.

Sestavite funkcijo ugibaj1(racunalnikX, racunalnikY, mihaX, mihaY), ki naj vrne niz 'Bravo!', če je Miha točko uganil oz. niz 'Tvoja točka je v {0}. kvadrantu.' (kjer na na namesto {0} izpisana številska oznaka kvadranta).

Primer:

>>> ugibaj1(15, 15, 16, 16)
'Tvoja točka je v 1. kvadrantu.'

>>> ugibaj1(15, 15, 14, 14)
'Tvoja točka je v 3. kvadrantu.'

Uradna rešitev

def ugibaj1(racunalnikX, racunalnikY, mihaX, mihaY):
  '''Ugibanje točke z vrnjenim kvadrantom'''
  razdalja = int(((racunalnikX - mihaX)**2 + (racunalnikY - mihaY)**2)**0.5)
  if razdalja == 0:
      odg = 'Bravo!'
  else:
      kvadrant = 0
      if mihaX > racunalnikX and mihaY >= racunalnikY:
         kvadrant = 1
      elif mihaX >= racunalnikX and mihaY < racunalnikY:
         kvadrant = 4
      elif mihaX <= racunalnikX and mihaY > racunalnikY:
         kvadrant = 2
      elif mihaX < racunalnikX and mihaY <= racunalnikY:
         kvadrant = 3
      odg = 'Tvoja točka je v ' + str(kvadrant) + '. kvadrantu.'
  return odg

Kamion

1. podnaloga

Kamion z nosilnostjo 10 ton porabi 30 litrov dizla na prevoženih 100 km. Naročniku, ki je od skladišča oddaljen d km, je treba pripeljati teza ton peska. Kamion ima trenutno v rezervoarju gorivo litrov dizla.

Ali bo kamion uspešno opravil pot? Če je teža tovora večja od nosilnosti, naj program izpiše

Tovor je pretežak. Kamion ne sme na pot!

Če bo na poti zmanjkalo goriva (nikjer ob poti ni bencinske črpalke), naj program izpiše

Po prevoženih x kilometrih bo zmanjkalo goriva.

kjer je namesto x treba izpisati opravljeno razdaljo, ki naj bo zaokrožena navzdol (na celo število). Sicer naj program izpiše

Kamion bo uspešno dostavil tovor naročniku.

Kako bo kamion prišel nazaj do skladišča, naj vas ne skrbi. Predpostavite še, da teža tovora ne vpliva na porabo goriva. Primer:

     Koliko km: 354
     Koliko je teža peska: 7.5
     Koliko goriva je v rezervoarju: 151.4
     Kamion bo uspešno dostavil tovor naročniku.

Uradna rešitev

d = int(input("Koliko km: "))
teza = float(input("Koliko je teža peska: "))
gorivo = float(input("Koliko goriva je v rezervoarju: "))
x = 100 * gorivo / 30
if teza > 10:
    print('Tovor je pretežak. Kamion ne sme na pot!')
elif x < d:
    print('Po prevoženih', int(x), 'kilometrih bo zmanjkalo goriva.')
else:
    print('Kamion bo uspešno dostavil tovor naročniku.')

Fibonaccijevo zaporedje ostankov

Posplošeno Fibonaccijevo zaporedje ostankov je definirano podobno kot običajno Fibonaccijevo zaporedje , le da si prva dva člena lahko izberemo sami, vse vsote pa računamo po modulu $n$. Vsaj eden od prvih dveh členov mora biti različen od $0$ (po modulu $n$), sicer dobimo zaporedje samih ničel. Vsako takšno zaporedje je periodično (ko se enkrat ponovita prva dva člena, se tudi vsi nadaljnji členi začnejo ponavljati).

V nalogi lahko predpostavite, da velja $n > 1$. Pripravljeno imamo funkcijo funkcijo naslednjiČlen(a, b, n), ki pri podanih zaporednih členih a in b izračuna naslednji člen.

   def naslednjiČlen(a, b, n):
       '''Vrne naslednji člen Fibbonacijevega zaporedja ostankov'''
       return (a + b) % n

1. podnaloga

Če računamo nekaj zaporednih členov za začetna člena 0, 1 in za modul 2, dobimo zaporedje 0, 1, 1, 0, 1, 1, 0, 1, 1 ... Zaporedje ima torej periodo 3. Pri podatkih 0, 1, in 5 pa je zaporedje 0, 1, 1, 2, 3, 0, 3, 3, 1, 4, 0, 4, 4, 3, 2, 0, 2, 2, 4, 1, 0, 1, 1, 2, 3, .. perioda torej kar 20!

Funkcija perioda(a, b, n) kot argumente dobi prva dva člena zaporedja in število n ter naj bi vrnila dolžino periode tega zaporedja. Predpostavite, da velja $a < n$ in $b < n$.

   def perioda(a, b, n):
       '''Dolžina periode FZO z začetnima členoma a in b in operacijo po modulu n
       x = a
       y = naslednjiČlen(a, b, n)
       dopPer = 1
       while x != a and y != b:
           x, y = y, naslednjiČlen(x, y, n)
           dolPer = 1
       return dolPer

Vemo, da so v zgornji kodi štiri napakice in sicer v vrsticah 2, 3, 6 in 8. Popravi jih!

Uradna rešitev

def naslednjiČlen(a, b, n):
    '''Vrne naslednji člen Fibbonacijevega zaporedja ostankov'''
    return (a + b) % n

def perioda(a, b, n):
    '''Dolžina periode FZO z začenima členoma a in b in operacijo po modulu n'''
    x = b
    y = naslednjiČlen(a, b, n)
    dolPer = 1
    while not(x == a and y == b): # dokler se člena ne ponovita - morda je to bolj jasno!
        x, y = y, naslednjiČlen(x, y, n)
        dolPer += 1
    return dolPer

2. podnaloga

Oglej si funkcijo

   def fun1(a, b, n, m):
       '''Vrnem nekaj v povezavi s FZO z začetnima členoma a in b in operacijo po modulu n'''
       x = min(a, b)
       q = a
       w = b
       st = 2
       while st <= m:
           y = naslednjiČlen(q, w, n)
           if y < x:
               x = y
           q = w
           w = y
           st += 1
       return x

S pomočjo te funkcije sestavi funkcijo minMax(a, b, n, m), ki vrne najmanjši in največji člen med prvimi m členi FZO z začetnima členoma a in b, če se operacije izvajajo po modulu n.

       >>>minMax(0, 1, 5, 8)
       (0, 3)
       >>>minMax(1, 1, 5, 4)
       (1, 3)

Namig: Poskušaj razumeti, kaj dana funkcija počne. Spremenljivkam daj smiselna imena. Npr. za y je očitno, da gre za člen v FZ. Tudi q in w imate nekaj zveze s tem. Čemu pa služi x?

Uradna rešitev

def minMax(a, b, n, m):
    '''Vrnem najmanjši in največji člen med prvimi m členi FZO z začetnima členoma a in b in operacijo po modulu n'''
    if m == 1:
        return (a, a)
    najmanj = min(a, b)
    največ = max(a, b)
    predzadnji = a
    zadnji = b
    st = 3
    while st <= m: 
        naslednji = naslednjiČlen(predzadnji, zadnji, n)
        if naslednji < najmanj: # našli smo manjšega
            najmanj = naslednji
        if naslednji > največ: # našli smo večjega
            največ = naslednji
        predzadnji, zadnji = zadnji, naslednji
        st += 1
    return (najmanj, največ)

3. podnaloga

Sestavite funkcijo najkrajšaPerioda(a, b, m, n), ki vrne najmanjši tak $i \in \left[m, n\right]$, pri katerem trojka (a, b, i) minimizira dolžino periode iz prve podnaloge. Predpostavite lahko, da sta argumenta m in n naravni števili in velja $m \leq n$. Primer:

>>> najkrajsa_perioda(2, 2, 6, 19)
8

Namig: Iz 1. podnaloge znamo izračunati dolžino periode. Ta podnaloga pa bi rada samo najkrajšo periodo med vsemi FZO z začetnima členoma a in b, če za modul jemljemo n, n+1, n+2, .., m

Uradna rešitev

def najkrajšaPerioda(a, b, m, n):
    '''Določi pri katerem modulu m <= modul <= n ima FZO najkrajšo periodo'''
    minPer = m # kandidat za modul za najkrajšo periodo
    dolMinPer = perioda(a, b, m) # dolžina te minimalne periode
    modul = m + 1
    while modul <= n: # pregledamo še ostale kandidate
        dolPer = perioda(a, b, modul)
        if dolPer < dolMinPer: # smo našli krajšo?
            minPer = modul
            dolMinPer = dolPer
        modul += 1
    return minPer

Števila: prijateljska, nepopolna ...

1. podnaloga

Sestavite funkcijo vsotaDeliteljev(n), ki bo izračunala vsoto vseh pravih deliteljev števila n. Pravi delitelji so vsi delitelji, razen števila samega, torej tudi 1 (hm, kaj pa za 1? - recimo, da je najbolj logično, da je za 1 vsota pravih deliteljev 0).

>>> vsotaDeliteljev(24)
36

Uradna rešitev

def vsotaDeliteljev(n):
    '''Vrne vsoto pravih deliteljev števila n'''
    delitelj = 1
    vsota = 0
    while delitelj < n:
        if n % delitelj == 0:
            vsota += delitelj
        delitelj += 1
    return vsota
## Lahko pa tudi upoštevamo, da delitelji števila vedno nastopajo v parih.
## def vsotaDeliteljev(n):
##    vsota = 1
##    d = 2
##    while d * d < n: # gremo do korena iz n
##        if n % d == 0:
##            vsota += d
##            vsota += n // d  # še drugi element para
##        d += 1
##    if d * d == n: # če je n slučajno popoln kvadrat
##        vsota += d
##    return vsota

2. podnaloga

Števili a in b sta prijateljski, če je vsota pravih deliteljev števila a enaka številu b, vsota pravih deliteljev števila b pa številu a. Sestavite program, ki premere m in n in izpiše vse takšne pare (a, b) prijateljskih števil, kjer je $m \le a < b \le n$. Primer:

>>>
Spodnja meja: 1000
Zgornja meja: 3000
(1184, 1210)
(2620, 2924)

Pari naj bodo izpisani naraščajoče glede na a. Par izpišemo s print((a, b)).

Uradna rešitev

def vsotaDeliteljev(n):
    '''Vrne vsoto pravih deliteljev števila n'''
    delitelj = 1
    vsota = 0
    while delitelj < n:
        if n % delitelj == 0:
            vsota += delitelj
        delitelj += 1
    return vsota

m = int(input('Spodnja meja: '))
n = int(input('Zgornja meja: '))
a = m
while a <= n: # pregledamo vse možne a-je
    b = vsotaDeliteljev(a)
    vsotaB = vsotaDeliteljev(b)
    # ali je par ustrezen (b je že enak vs.del. a)
    if a < b <= n and a == vsotaB:
            print((a, b))
    a += 1

3. podnaloga

Sestavite funkcijo najblizji_prijateljski_par(m, n), ki poišče in vrne tisti par (a, b) prijateljskih števil, da je $m \le a < b \le n$ in je razlika $b-a$ najmanjša. Če je takšnih parov več, naj vrne prvega med njimi. Če takšnega para ni, naj funkcija vrne par (None, None).

>>> najblizji_prijateljski_par(1000, 3000)
(1184, 1210)

Uradna rešitev

def najblizji_prijateljski_par(m, n):
    '''Najbližji prijateljski števili med m in n'''
    ma = mb = None # z ma in mb bomo označili naj par
    a = m
    while a < n:
        b = vsotaDeliteljev(a)
        if a < b <= n and vsotaDeliteljev(b) == a:
            if ma == None or b - a < mb - ma:
                # če smo našli boljši par (ali sploh prvega)
                ma, mb = a, b
        a += 1
    return ma, mb

4. podnaloga

Sestavite funkcijo nepopolnost(n), ki vrne absolutno razliko med številom n in vsoto vseh njegovih pravih deliteljev.

Uradna rešitev

def nepopolnost(n):
    '''Razlika med številom in vsoto njegovih deliteljev'''
    return abs(n - vsotaDeliteljev(n))

5. podnaloga

Število je popolnoma nepopolno, kadar je njegova nepopolnost večja od njega samega.

Sestavite funkcijo popolnomaNepopolno(n), ki vrne prvo popolnoma nepopolno število, večje ali enako n.

Uradna rešitev

def popolnomaNepopolno(n):
    '''Vrne prvo popolnomaNepopolno število, večje ali enako n'''
    while(nepopolnost(n) <= n):
        n += 1
    return n

Seznami - osnove osnov


Seznami števil

1. podnaloga

Premisli, kaj počne spodaj navedena funkcija. Žal je rezultat domače naloge študenta 1. letnika, ki še ne ve, da funkcija brez komentarjev ni preveč ...

   def funk(seznam):
       j = 0            
       i = 0
       d = len(seznam)
       while i < d:    
           if seznam[i] == 1:      
               j += 1   
           i += 2
       return j

Predelaj jo (kar pomeni tudi, da jo opremiš s komentarji, ustrezno popraviš imena spremenljivk ...) v funkcijo prestej(n, seznam), ki v seznamu števil prešteje kolikokrat se v tem seznamu pojavi število n Na primer:

  >>> prestej(5,[2, 5, 12, 3, 5, 3, 12, 8, 12])
  2

Uradna rešitev

def prestej(n, seznam):
    '''kolikokrat v seznamu nastopa n'''
    vseh = 0            # na začetku je vseh 0
    i = 0
    dolSez = len(seznam)
    while i < dolSez:    # potujemo po vseh indeksih seznama
        if seznam[i] == n:      
            vseh += 1   # če je enak, povečamo št. vseh
        i += 1
    return vseh        # vrne koliko je bilo vseh parametrov n

2. podnaloga

Napiši funkcijo prestejSodeLihe(seznam), ki v seznamu števil prešteje kolikokrat se v tem seznamu pojavi sodo število. Rezultat naj vrne v obliki para (soda, liha) Na primer:

  >>> prestej([2, 5, 12, 3, 5, 3, 12, 8, 12])
  (5, 4)

Uradna rešitev

def prestejSodaLiha(n, seznam):
    '''koliko je v seznamu sodih in lihih'''
    sodih = 0            # na začetku je vseh sodih 0
    i = 0
    dolSez = len(seznam)
    while i < dolSez :    # potujemo po vseh indeksih seznama
        if seznam[i] % 2 == 0:      
            sodih += 1   # če je sodo
        i += 1
    return (sodih, dolSez - sodih)  # tako bo hitreje, kot če bi posebej šteli še lihe

3. podnaloga

Funkcija seznamMest(n,seznam)

   def seznamMest(n, seznam):
       '''indeksi, kje se v seznamu pojavi n '''
       i = 1
       while i <= len(seznam):    # potujemo po vseh indeksih seznama
           if seznam[i] == n:          # primerjamo ali je i-ti elt. enak n
               mesta.append([i])         # če pridemo do tega mesta, indeks dodamo k novemu seznamu
           i = i + 1
       return mesta                   # vrne nam seznam indeksov

vrnila seznam vseh mest, na katerih se v seznamu števil pojavi število n. Mesta štejemo od 0 dalje! Na primer:

  >>> seznamMest(12,[2, 5, 12, 3, 5, 3, 12, 8, 12])
  [2, 6, 8]

Žal ne dela prav. Ima sintaktične in semantične napake. Popravi jo!

Uradna rešitev

def seznamMest(n, seznam):
    '''indeksi, kje se v seznamu pojavi n '''
    mesta = []
    i = 0
    while i < len(seznam):    # potujemo po vseh indeksih seznama
        if seznam[i] == n:          # primerjamo ali je i-ti elt. enak n
            mesta.append(i)         # če pridemo do tega mesta, indeks dodamo k novemu seznamu
        i  += 1
    return mesta                   # vrne nam seznam indeksov

4. podnaloga

Funkcija kolikoEnic(seznamCelih) naj vrne seznam, kjer na mestu i povemo, koliko je v seznamu seznamCelih števil, ki imajo kot enice vrednost i. Na primer:

  >>> kolikoEnic([1432, 32155, 12, 351, 12353, 1255, 2313, 12, 8, 112])
  [0, 1, 4, 2, 0, 2, 0, 0, 1, 0]

saj v seznamu števil ni nobenega števila, ki bi imelo na mestu enic 0, je eno število (namreč 351), ki ima na mestu enic 1, 4 števila (1432, 12, 12 in 112), ki imajo na mestu enic 2 ....

Uradna rešitev

def kolikoEnic(seznamCelih):
    '''Preštejemo, koliko števil ima i enic'''
    števciEnic = [0] * 10 # vse možne enice
    i = 0
    while i < len(seznamCelih):    # potujemo po vseh indeksih seznama
        število = abs(seznamCelih[i])
        števciEnic[število % 10] += 1  # povećčamo ustrezni števec
        i += 1

    return števciEnic

Dolžina vektorja

Kot veste, je vektor v n razsežnem prostoru podan kot n-terka števil. Pojem dolžine lahko posplošimo in dobimo normo vektorja.

1. podnaloga

Najprej sestavimo funkcijo, ki izračuna "klasično" dolžino vektorja, torej njegovo Evklidsko normo.

 >>> dolzinaVektorja([1, 1])
 1.41421356237
 >>> dolzinaVektorja([1, 4, 2, -5])
 6.78232998313

Uradna rešitev

def dolzinaVektorja(vektor):
    ''' vrne dolžino vektorja v n-razsežnem prostoru '''
    vsotaKvadratov = 0
    i = 0
    while i < len(vektor):
        x = vektor[i]
        vsotaKvadratov += x**2
        i += 1
    return vsotaKvadratov**0.5

2. podnaloga

Sedaj pa sestavite funkcijo manhattanskaRazdalja(vektor), ki izračuna dolžino vektorja, podanega z geometrijo (ameriških) mestnih taksijev, oziroma njegovo prvo normo.

 >>> manhattanskaRazdalja([1, 1])
 2
 >>> manhattanskaRazdalja([1, 4, 2, -5])
 12

Uradna rešitev

def manhattanskaRazdalja(vektor):
    ''' vrne prvo normo vektorja v n-razsežnem prostoru '''
    vsota = 0
    i = 0
    while i < len(vektor):
        x = vektor[i]
        vsota += abs(x)
        i += 1
    return vsota

3. podnaloga

Zanimiva je tudi uniformna norma ali razdalja Čebiševa (tudi neskončna norma). Sestavi funkcijo neskoncnaNorma(vektor), ki izračuna to normo.

 >>> neskoncnaNorma([1, 1])
 1
 >>> neskoncnaNorma([1, 4, 2, -5])
 5

Uradna rešitev

def neskoncnaNorma(vektor):
    ''' vrne neskončno normo vektorja v n-razsežnem prostoru '''
    naj = abs(vektor[0]) # poiskati moramo navječjo abs. vrednost komponente
    i = 1
    while i < len(vektor):
        x = abs(vektor[i])
        if x > naj:  # našli smo boljšo
            naj = x
        i += 1
    return naj

Stopnice

1. podnaloga

V laboratoriju FMF-P1 jim je uspel tehnološki presežek. Sestavili so robota, ki zlahka hodi po stopnicah. Konkurenca na vsak način poskuša diskreditirati ta dosežek. Zato sestavljajo različne čudne kombinacije stopnic in čakajo, kdaj bo robot pri hoji padel, saj robot lahko prehodi le stopnico, ki je nižja od 20 cm. Ampak spretni študenti praktiki so robota opremili z merilnim sistemom, ki zmeri višino posamezne stopnice in napisali funkcijo (opaziš, da je lepo skrbno komentirana, kot vsa koda, ki jo pišejo v tem laboratoriju).

   def kolikoStopnic(stopnice):
       '''Koliko stopnic lahko prehodi robot'''
       katera = 0 # indeks stopnice
       while katera < len(stopnice):       
           v = stopnice[katera]            # za vsako stopnico vzamemo njeno visino 
           if v > 20:   # ce je visina stopnice na vrsti previsoka
               return katera # koliko stopnic lahko prehodim (ker začnemo z 0, bo OK!)
           katera += 1   # naslednja stopnica
       # prehodili smo vse !
       return len(stopnice)

Na osnovi te kode sestavi funkcijo kakoVisokoPridem(stopnice), ki kot argument prejme seznam višin stopnic stopnice rezultat, ki ga vrne, pa pove, na kakšni višini bo robot po koncu hoje.

Uradna rešitev

def kakoVisokoPridem(stopnice):
    '''kako visoko lahko lahko pride robot'''
    trenutnaVišina = 0             # zacnemo na visini 0
    katera = 0 # indeks stopnice
    while katera < len(stopnice):       
        v = stopnice[katera]            # za vsako stopnico vzamemo njeno visino 
        if v > 20:   # ce je visina stopnice na vrsti previsoka
            return trenutnaVišina # do sem sem prišel ...
        trenutnaVišina += v # stopimo višje!
        katera += 1   # naslednja stopnica
    # prehodili smo vse !
    return trenutnaVišina

2. podnaloga

Na FRI-P1 pa jim je uspelo izdelati napravo, ki zmoti merilni sistem robota tako, da meri višine stopnic, merjene od tal (ne od prejšnje stopnice). Ampak praktiki s FMF se ne dajo. Poleg tega, da robota izpopolnijo, da ima sedaj nastavljivo maksimalno višino koraka (a žal se ta med hojo ne da spreminjati), na osnovi prejšnje naloge napišejo funkcijo kakoVisoko(stopnice, korakRobota), ki kot argument prejme seznam višin stopnic stopnice, merjenih na "FRI način" in kako visoko se lahko robot premakne v enem koraku, rezultat, ki ga vrne, pa spet pove, kako visoko bo robot priplezal.

Primer:

 >>> kakoVisoko([5, 25, 45, 50, 76, 80, 81], 20)
 50

Uradna rešitev

def kakoVisoko(stopnice, korakRobota):
    '''Kako visoko bo pripezal robot'''
    trenutnaVišina = 0             # zacnemo na visini 0
    katera = 0
    while katera < len(stopnice):       # za vsako stopnico vzamemo njeno visino (od tal)
        v = stopnice[katera]            # kako visoko mora robot priti
        if v - trenutnaVišina <= korakRobota:   # ce je visina stopnice na vrsti manjša od korakRobota
            trenutnaVišina = v     # potem robot lahko stopi
        else:
            break                   # sicer ne preverjamo naprej in koncamo zanko
        katera += 1   # naslednja stopnica
 
    return trenutnaVišina

Nizi - osnove osnov


Brez? Brez!

1. podnaloga


Brez (pa ne zgoraj, ampak spredaj in zadaj) Napiši funkcijo brez(nekNiz), ki sestavi nov niz, pri katerem spustimo prvi in zadnji znak niza nekNiz.

Uradna rešitev

def brez(niz):
    '''sestavi nov niz, pri katerem spustimo prvi in zadnji znak niza'''
    novNiz = ''
    ind = 1
    while ind < len(niz) - 1: # spustimo prvega in zadnjega
       novNiz += niz[ind]
       ind += 1 
    return novNiz

2. podnaloga


Brezštevno Napiši funkcijo brezŠtevk(nekNiz), ki sestavi nov niz, pri katerem spustimo vse števke, ki nastopajo v nizu nekNiz. Ali je v spremenljivki nekZnak števka, lahko preverimo z

     if '0' <= nekZnak <= '9': # v nekZnak je števka

Uradna rešitev

def brezŠtevk(niz):
    '''sestavi nov niz, pri katerem spustimo števke'''
    novNiz = ''
    ind = 0
    while ind < len(niz):
        nekZnak = niz[ind]
        if not('0' <= nekZnak <= '9'): # v nekZnak ni števka
            novNiz += nekZnak # dodamo k novemu nizu
        ind += 1 
    return novNiz

3. podnaloga


Brez sredine Napiši funkcijo brezSredine(nekNiz), ki nizu spusti srednji znak. Če pa je niz sode dolžine, naj ohrani vse znake

     >>>brezSredine('matematika')
     matematika
     >>>brezSredine('fizik')
     fiik

Uradna rešitev

def brezSredine(niz):
    '''V nizu spusti srednji (ali srednja dva) znaka'''
    dolNiza = len(niz)
    if  dolNiza % 2 == 0:  # niz je sode dolžine
        return niz
    # niz je lihe dolžine
    novNiz = ''
    ind = 0
    # prepišemo prvo polovico
    while ind < dolNiza // 2:
        nekZnak = niz[ind]
        novNiz += nekZnak # dodamo k novemu nizu
        ind += 1
    # in še drugo
    ind += 1
    while ind < dolNiza:
        nekZnak = niz[ind]
        novNiz += nekZnak # dodamo k novemu nizu
        ind += 1       
    return novNiz

Črkojedka

Nekoč je živela ptica Črkojedka, ki je imela še posebej rada samoglasnike. Še posebej od takrat, ko se je naučila, da v Pythonu lahko preverimo, ali je nek znak samogaslnik s pomočjo operatorja in

      >>>'a' in 'aeiouAEIOU'
      True
      >>>'t' in 'aeiouAEIOU'
      False

Kadar je pojedla samoglasnik, je na tistem mestu pustila *.

1. podnaloga

Sestavi funkcijo crkojedka(niz), ki bo za dani niz vrnila nov niz z * na mestu, kjer je bil samoglasnik.
Na primer:

   >>>crkojedka('beseda')                                                      
   b*s*d*

Uradna rešitev

def crkojedka(niz):
    ''' funkcija v dani besedi samoglasnike zamenja z *'''
    samoglasniki ='aeiouAEIOU'
    novNiz=''
    i = 0
    while i < len(niz):
        znak = niz[i]
        if znak in samoglasniki:
            # pogleda ali je posamezen znak v nizu samoglasnik
            # in če je, potem ga nadomesti z *
            novNiz += '*'
        else: # sicer ga le prepiše
            novNiz += znak
        i += 1
    return novNiz

2. podnaloga

Določene besede so bile bolj kalorične (vsebovale več samoglasnikov), določene manj. ker je Črkojedka na dijeti, ji sestavi funkcijo, ki ji bo pomagala izbirati ustrezno hrano. Zato sestavi funkcijo crkojedkaHujša(niz), ki bo za dani niz vrnila njegovo kalorično vrednost, torej število samoglasnikov
Na primer:

   >>>crkojedkaHujša('črmlj')                                                      
   0
   >>>crkojedkaHujša('Kako debeeeeeeeeeeela sem!')                                                      
   16

Uradna rešitev

def crkojedkaHujša(niz):
    ''' funkcija prešteje samoglasnike *'''
    samoglasniki ='aeiouAEIOU'
    kaloričnaVrednost = 0
    i = 0
    while i < len(niz):
        znak = niz[i]
        if znak in samoglasniki:
            # pogleda ali je posamezen znak v nizu samoglasnik
                kaloričnaVrednost += 1
        i += 1
    return kaloričnaVrednost

3. podnaloga

Po neki precej "živahni" zabavi se je Črkojedka odmajala domov. Zjutraj se je zbudila s hudim ... (hm, verjetno ne mačkom, kaj menite?). In opazila je, da so se sedaj njene prehrambene navade spremenile. Sedaj ni več samoglasnikov nadomeščala z *, ampak z ničemer več. Poleg tega pa se je vrstni red preostalih znakov obrnil! Sestavi funkcijo crkojedkaNePij(niz), ki vrne niz, kakor ga predela Črkojedka z mačk... Na primer:

   >>>crkojedkaNePij('beseda')                                                      
   dsb

Uradna rešitev

def crkojedkaNePij(niz):
    ''' funkcija v dani besedi izloči samoglasnike in obrne vrstni red znakov'''
    samoglasniki ='aeiouAEIOU'
    novNiz=''
    i = 0
    while i < len(niz):
        znak = niz[i]
        if not znak in samoglasniki: # če znak ni samoglasnik
            novNiz = znak + novNiz # ga doda na konec
        i += 1
    return novNiz

Seznami I

Skop vaj, pri katerih uporabljamo sezname


Kamionisti

V Republiki Banana zaradi utrujenosti in pomanjkanja koncentracije kamionisti (pet minut za učenje slovenčine) vse pogosteje povzročajo nesreče. Na vladi so se odločili, da bodo sprejeli zakone, ki bodo uredili problematiko preutrujenih kamionistov. Najprej pa morajo analitiki preučiti njihove navade. V ta namen so pridobili podatke o kamionistih. Za vsakega imajo podan seznam, v katerem so shranjene prevožene razdalje po posameznih dnevih. Število 0 pomeni, da je kamionist tisti dan počival. Na primer seznam

[350, 542.5, 0, 602, 452.5, 590.5, 0, 248]

pomeni, da je kamionist prvi dan prevozil 350 km, drugi dan je prevozil 542.5 km, tretji dan je počival itd.

1. podnaloga

Sestavite funkcijo pocitekInPovprecje(voznja), ki kot argument dobi zgoraj opisani seznam (prevožene razdalje po posameznih dnevih) in vrne par števil. Prvo število naj bo število dni, ko je kamionist počival. Drugo število naj bo na eno decimalko zaokrožena povprečna dnevna prevožena razdalja, pri čemer upoštevamo samo tiste dneve, ko ni počival. Predpostavite lahko, da kamionist vsaj en dan ni počival. Primer:

>>> pocitekInPovprecje([200, 300, 0, 100])
(1, 200.0)

Kamionist je torej enkrat počival. Ob dnevih, ko ni počival, pa je v povprečju prevozil 200 km.

Uradna rešitev

def pocitekInPovprecje(voznja):
    '''Vrne par števil. Prvo pove število dni, ko kamionist počiva in drugo
    povprečno št. kilometrov na dneve vožnje '''
    stPocitkov = 0
    delovnihDni = 0
    skupnoKilometrov = 0
    for prevozenoTaDan in voznja:
        if prevozenoTaDan == 0:
            stPocitkov += 1
        else:
            delovnihDni += 1
        skupnoKilometrov += prevozenoTaDan
    return stPocitkov, round(skupnoKilometrov / delovnihDni, 1) # zaradi predpostavke
                                           # zagotovo ne delimo z 0!

2. podnaloga

Napišite funkcijo primerjaj(prvi, drugi), ki primerja vožnjo dveh kamionistov. Funkcija kot argumenta dobi dva enako dolga seznama prvi in drugi, ki opisujeta vožnjo dveh kamionistov, ki sta se podala na isto pot. Funkcije naj sestavi in vrne nov seznam, v katerem je za vsak dan zapisano, kdo je do tega dne skupaj prevozil večjo razdaljo: 1, če je bil to 1. kamionist; 2, če je bil to 2. kamionist in 0, če sta oba prevozila enako razdaljo. Primer:

>>> primerjaj([200, 300, 100], [200, 200, 300])
[0, 1, 2]
>>> primerjaj([500, 100, 100], [100, 200, 200])
[1, 1, 1]

Uradna rešitev

def primerjaj(prvi, drugi):
    '''Vrne seznam podatkov o tem, kateri kamionist je prišel do določenega dne dlje'''
    prevozenoPrvi = 0    # skupno število prevoženih kilometrov do določenega dne
    prevozenoDrugi = 0
    primerjava = []
    dan = 0
    while dan < len(prvi):  # uporabimo while, ker potrebujemo podatke iz obeh seznamov
        prevozenoPrvi += prvi[dan]
        prevozenoDrugi += drugi[dan]
        # kdo je v tem trenutku že dlje
        if prevozenoPrvi > prevozenoDrugi:
            primerjava.append(1)
        elif prevozenoPrvi < prevozenoDrugi:
            primerjava.append(2)
        else:
            primerjava.append(0)
        dan += 1
    return primerjava

3. podnaloga

Vlada je uzakonila naslednja pravila: Povprečna prevožena razdalja v treh zaporednih dneh ne sme biti več kot 500 km. (Štejemo tudi dneve, ko je kamionist počival.) Kamionist ne sme brez počitka voziti več kot 5 dni v zaporedju.

Ker pa so ugotovili, da so kamionisti prevečkrat svoje zaporedje skrajšali na dva dni in se s tem dejansko izognili pravilu, so uzakonili še: Zadnji dan ne sme voziti več kot 500 km. Povprečje zadnjih dveh dni ne sme biti več kot 500 km.

Sestavite funkcijo poPravilih(voznja), ki vrne True, če se je kamionist držal predpisov, in False, če se jih ni.

>>> poPravilih([50, 200, 300, 20, 100, 60])
False
>>> poPravilih([600, 200, 0, 300, 300, 0, 600, 600, 400])
False
>>> poPravilih([600, 600, 0, 600, 600])
False
>>> poPravilih([600, 600, 0, 200, 600])
False

Uradna rešitev

def poPravilih(voznja):
    '''Ali je vožnja potekala po pravilih'''
    zaporedno = 0
    dan = 0 
    while dan < len(voznja) - 2: # za račun povprečja zaporedja!
        prevozil = voznja[dan]
        if prevozil == 0:   # je počival, začnemo šteti od začetka
            zaporedno = 0
        else:
            zaporedno += 1
        if zaporedno > 5:  # zaporedoma vozi več kot 5 dni
            return False
        povprecje = (voznja[dan] + voznja[dan+1] + voznja[dan+2]) / 3
        if povprecje > 500:
            return False
        dan += 1
    # pregledamo še zadnja dva dni
  
    while dan < len(voznja): # dan je že ustrezno "velik", torej se bo zanka izvedla 2krat
        prevozil = voznja[dan]
        if prevozil == 0:
            zaporedno = 0
        else:
            zaporedno += 1
        if zaporedno > 5:
            return False
        dan += 1
    # še povprečji

    # če imamo sploh še 2 dni   (če imamo le en podatek+, to ne bo res)
    if len(voznja) - dan == 2:
        if (voznja[len(voznja) - 1] + voznja[len(voznja) - 2])/2 > 500:
            return False
    # še kontrola dolžine vožnje zadnji dan
    if voznja[-1] > 500:
        return False
    # uspešno preko vseh kontrol
    return True

4. podnaloga

Podatke za več kamionistov so analitiki združili v en sam seznam. Zgled takega seznama:

vsiSkupaj = [
    [50, 200, 300, 20, 100, 60],
    [600, 600, 0, 600, 600, 0, 500, 500],
    [600, 200, 0, 300, 300, 0, 600, 600, 400]
]

Sestavite funkcijo preveriVse(seznamVozenj), ki dobi kot argument na tak način opisan seznam in vrne seznam logičnih vrednosti, ki za vsakega kamionista pove, če je vozil po predpisih. Primer:

>>> preveriVse(vsiSkupaj)
[False, True, False]

Uradna rešitev

def preveriVse(seznamVozenj):
    '''Preveri vožnje vseh kamionistov'''
    rezultat = []
    for voznjaPosameznika in seznamVozenj: # 
        rezultat.append(poPravilih(voznjaPosameznika))
    return rezultat

5. podnaloga

Ko so vožnje vseh kamionistov že tako lepo združene, so se analitiki spravili računati še to, kakšno je navečje število voženj, ki jih je nekdo opravil, kakšno je največje število dni počitka in kakšno največje in kakšno najmanjše povprečno povprečno število dnevno prevoženih kilometrov (povprečje tako, kot pri prvi nalogi - štejemo le dneve vožnje). Pri tem upoštevamo samo tiste kamioniste, ki so vozili po pravilih! Sestavite funkcijo analitika(seznamVozenj), ki dobi kot argument opisani seznam in vrne nabor 4 vrednosti: največ voženj, največ dni počitka, maksimalno dnevno prevoženih in minimalno dnevno prevoženih km. Predpostavi, da imamo vsaj enega kamionista, ki je vozil po predpisih! Primer (podatki iz prejšnje naloge):

>>> analitika(vsiSkupaj)
(6, 2, 566.7, 566.7)

Uradna rešitev

def analitika(seznamVozenj):
    '''med pravilno vozečimi kamionisti
       poišče nabor 4 vrednosti: največ voženj, največ dni počitka, maksimalno dnevno prevoženih
       in minimalno dnevno prevoženih km.'''
    minPovp = 600 # vsaj en bo vozil po predpisih, torej bo minimalno povprecje zagotovo pod 500!
    maxPovp = 0
    maxPoc = 0 # maksimalno dni počitka
    maxVozenj = 0
    for voznjaPosameznika in seznamVozenj:
        if poPravilih(voznjaPosameznika): # "dobri" voznik, ga upoštevamo
            poc, povp = pocitekInPovprecje(voznjaPosameznika)
            vozenj = len(voznjaPosameznika) - poc
            # ali so katere od teh vrednosti za "naj"
            if poc > maxPoc:
                maxPoc = poc
            # namesto if, uporabimo raje kar min, oz. max
            maxVozenj = max(vozenj, maxVozenj)
            minPovp = min(povp, minPovp)
            maxPovp = max(povp, maxPovp)
    return (maxVozenj, maxPoc, maxPovp, minPovp)

aN ebor

Na Najboljši TV je prava zmešnjava (kar za najnovejšo TV v Sloveniji niti ni tako nenavadno). Le še 5 minut do začetka oddaje Izbiramo najlepšega bika Slovenije. Težav pa cel kup.

1. podnaloga

Prva težava je, da je na vseh seznamih naveden napačni vrstni red imen bikov. Režiser na vsak način poskuša prepričati voditeljski par, da ni nič narobe, le od spodaj navzgor naj bereta razvrstitev. A kljub temu, da sta to daleč najbolj inteligentna TV voditelja v Sloveniji, se bojita, da bo to zanju pretežka naloga. Zato jim moraš hitro napisati funkcijo obrniSeznamBikov(sezBik), ki vrnila nov seznam teh imen v obratnem vrstnem redu. Za seznam

    ['Bukso', 'Hitri', 'Šeko', 'Lisko']

naj vrne seznam

    ['Lisko', 'Šeko', 'Hitri', 'Bukso']

Pazi, ker moraš vrniti nov seznam, uporabe metode reverse ne bo OK!

Uradna rešitev

def obrniSeznamBikov(sezBik):
    ''' vrne nov seznam bikov v obratnem vrstnem redu '''
    novSez = []
    for bik in sezBik:
        novSez = [bik] + novSez # ker dodajamo spredaj, bomo seznam obrnili
    return novSez

2. podnaloga

Seznam bikov je sedaj urejen. A kaj, ko so na seznam prikradle tudi krave (na TV sumijo, da gre za podtalno delovanje stricev iz ozadja, a ...). In zato se bodo pritožili na MCSodišče. A ura je neizprosna, ustrezen seznam potrebujejo takoj. Na srečo je najomiljeni voditelj z vso svojo avtoriteto ugotovil, da je krave zlahka prepoznati, saj se vsa njihova imena končajo z 'a'. Ker želijo ohraniti dokaze, morajo seznam s pomešanimi kravami ohraniti. Zato sestavi funkcijo odstraniKrave(seznam), ki vrnila nov seznam teh imen brez krav. Biki morajo ohraniti svoj vrstni red. Za seznam

    ['Liska', 'Šeko', 'Hitri', 'Buksa']

funkcija vrne

    ['Šeko', 'Hitri']

Uradna rešitev

def odstraniKrave(seznam):
    ''' vrne nov seznam brez krav (imen, ki se končajo z a) '''
    novSez = []
    for žival in seznam:
        if žival[-1] != 'a' : # ime se ne končna z a
            novSez.append(žival)
    return novSez

3. podnaloga

težav pa še ni konec. Vse to prekladanje seznamov je povzročilo, da so sedaj določena imena v seznamu navedena večkrat. Zato napiši funkcijo odstraniOdvečne(seznam), ki vrne nov seznam, a tako, da iz seznama pomeče vsa odvečna imena. Pri enakih imenih ohrani le tistega, ki se je v seznamu pojavil prvi.m Iz

    ['Lisko', 'Šeko', 'Šeko', 'Šeko', 'Hitri', 'Bukso', 'Lisko']

dobimo

    ['Lisko', 'Šeko', 'Hitri', 'Bukso']

Namig: Kaj naredi 'bla' in ['blu', 'ble', 'bla', 'blu']?

Uradna rešitev

def odstraniOdvečne(seznam):
    ''' vrne nov seznam brez odvečnih (podvojenih) imen '''
    novSez = []
    for žival in seznam:
        if žival not in novSez : # ime se še ni pojavilo
            novSez.append(žival)
    return novSez

Čete

Četa je najdaljše možno strnjeno podzaporedje v danem seznamu števil z določeno lastnostjo. Če je zaporedje naraščajoče (vsak naslednji element podzaporedja je večji), govorimo o naraščajočih četah, če je nepadajoče (torej je vsak naslednji element večji ali enak) o nepadajočih četah ...

Celoten seznam je tako sestavljen iz več zaporednih čet. Predpostavili bomo, da bomo vedno upoštevali čete iste vrste. Tako v seznamu [2,5,7,1,45,7,7,15] najdemo kar 4 naraščajoče čete (2,5,7, 1,45, 7 in 7,15), seznam [3,5,8,10,12] pa je sestavljen iz ene same čete.

Po drugi strani pa v seznamu [2,5,7,1,45,7,7,15] najdemo kar 6 padajočih čete (2, 5, 7, 1, 45, 7, 7 in 15) in 5 nenaraščajočih čet (2, 5, 7, 1, 45, 7, 7 in 15)

1. podnaloga

Sestavite funkcijo naraščajoč(seznam), ki preveri, ali elementi seznama seznam tvorijo narascajoče zaporedje.

>>> naraščajoč([])
True
>>> naraščajoč([1, 2, 5, 8, 12, 35])
True
>>> naraščajoč([3, 5, 5])
False
>>> naraščajoč([2, 6, 4, 8, 9, 6])
False

Uradna rešitev

def naraščajoč(seznam):
    '''Ali vsi elementi seznama naraščajo'''
    if seznam == []: return True # na "prazno" res
    for i in range(1, len(seznam)):
        if seznam[i - 1] >= seznam[i]: return False 
    return True # vse kontrole padanja so bile neuspešne, narašča!

2. podnaloga

Sestavi funkcijo številoNepadajočihČet(sez), ki v danem številskem seznamu prešteje, iz koliko nepadajočih čet je sestavljen. Nepadajoča četa je najdaljši možen nepadajoče urejen podseznam, ali še enostavneje, dokler se zaporedni elementi seznama ne zmanjšujejo, so v isti četi.

 >>> številoNepadajočihČet([])
 0
 >>> številoNepadajočihČet([6])
 1
 >>> številoNepadajočihČet([1, 3, 4, 7, 23, 56, 81])
 1
 >>> številoNepadajočihČet([1, 2, 2, 6, 2, 3, 7, 5, 2, 3, 8])
 4

Uradna rešitev

def številoNepadajočihČet(seznam):
    ''' koliko nepadajočih čet ima seznam '''
    if seznam == []:
        return 0
    stCet = 0
    prejšni = seznam[0] # ker NE velja x < x ne bo težav z začetkom!
    for x in seznam: 
        if x < prejšni: # konec čete, začetek nove
            stCet += 1
        prejšni = x
    return stCet + 1 # 1 zaradi zadnje!

3. podnaloga

Sestavi funkcijo številoNaraščajočihČet(sez), ki v danem številskem seznamu prešteje, iz koliko naraščajočih čet je sestavljen. Naraščajoča četa je najdaljši možen naraščajoče urejen podseznam, ali še enostavneje: dokler zaporedni elementi seznama naraščajo, so v isti četi.

 >>> številoNaraščajočihČet([])
 0
 >>> številoNaraščajočihČet([6])
 1
 >>> številoNaraščajočihČet([1, 3, 4, 7, 23, 56, 81])
 1
 >>> številoNaraščajočihČet([1, 2, 2, 6, 2, 3, 7, 5, 2, 3, 8])
 5

Uradna rešitev

def številoNaraščajočihČet(seznam):
    ''' koliko naraščajočih čet ima seznam '''
    if seznam == []:
        return 0
    stCet = 0
    prejšni = seznam[0]
    for x in seznam:
        if x <= prejšni: # konec čete, začetek nove
            stCet += 1
        prejšni = x
    return stCet

4. podnaloga

Sestavite funkcijo dolžinaNajdaljšeNaraščajočeČete(seznam), ki ugotovi, kakšna je dolžina najdaljše čete v seznamu seznam.

>>> dolžinaNajdaljšeNaraščajočeČete([])
0
>>> dolžinaNajdaljšeNaraščajočeČete([2, 5, 7, 10])
4
>>> dolžinaNajdaljšeNaraščajočeČete([1, 3, 6, 3, 8, 8, 10, 12])
3

Uradna rešitev

def dolžinaNajdaljšeNaraščajočeČete(seznam):
    '''Kako dolga je najdaljša naraščajoča četa'''
    if seznam == []: return 0
    naj = 0 # največja dolžina do sedaj
    dolzina = 1 # dolžina trenutne čete (vsebuje prvi element)
    for i in range(1, len(seznam)): # od drugega elementa naprej
        if seznam[i - 1] < seznam[i]:
            dolzina += 1 # še vedno imamo isto četo
        else: # naleteli smo na novo četo
            if dolzina > naj: # je morda ravno zaključena četa daljša?
                naj = dolzina
            dolzina = 1 # začeli smo na novo
    return max(naj, dolzina) #paziti moramo še na dolžino zadnje

5. podnaloga

Sestavite funkcijo naraščajočeČete(seznam), ki vrne seznam vseh naraščajočih čet, ki tvorijo seznam seznam.

>>> naraščajočeČete([1, 3, 6, 3, 8, 8, 10, 12])
[[1, 3, 6], [3, 8], [8, 10, 12]]

Uradna rešitev

def naraščajočeČete(seznam):
    '''Seznam vseh naraščajočih čet v seznamu'''
    if seznam == []: return [] 
    seznamCet = []
    ceta = [seznam[0]] # trenutna četa
    for i in range(1, len(seznam)):
        if seznam[i - 1] < seznam[i]:
            ceta.append(seznam[i]) # podaljšujemo obstoječo četo
        else: # zaključili smo s prejšnjo
            seznamCet.append(ceta)
            ceta = [seznam[i]] # in začeli novo
    seznamCet.append(ceta) # dodamo še zadnjo četo
    return seznamCet

Polžje dirke

Butalci vsako leto priredijo polžje dirke. Pomagajte jim pri organizaciji.

1. podnaloga

Tekmovalnega polža opišemo s tremi parametri: starostjo, težo in velikostjo. Faktor njegove tekmovalne sposobnosti opisuje takšna formula: $faktor = (starost + (\frac{teža}{velikost})^2)^2$, pri čemer velja manj je bolje (manjša kot je vrednost faktorja, boljši je polž).

Med vsemi polži želimo poiskati najboljšega. Napišite funkcijo najboljsi(s), ki sprejme seznam opisov polžev (seznam trojčkov [starost, teža, velikost]) in vrne faktor za najboljšega polža, spet na dve decimalni mesti natančno.

>>> najboljsi([[5, 6, 72], [4, 17, 77], [14, 21, 22], [17, 36, 64], [13, 29, 23]])
16.39

Uradna rešitev

def sposobnost(s, t, v):
    '''Vrne sposobnost polža'''
    return round((s + (t/v)**2)**2,2)

def najboljsi(polzi):
    '''Določi faktor sposobnosti najboljšega polža'''
    polz = polzi[0] # prvi polž
    najboljsiFaktor = sposobnost(polz[0], polz[1], polz[2]) # prvi je najboljši ;-)
    for polz in polzi: # prvega bomo sicer gledsali dvakrat, a kaj zato!
        kakoSposoben = sposobnost(polz[0], polz[1], polz[2])
        if kakoSposoben < najboljsiFaktor: # našli smo boljšega
            najboljsiFaktor = kakoSposoben
    return najboljsiFaktor
        
# Mimogrede: z nekaj znanja /ki ga še nimamo/ bi nalogo lahko rešili
# v eni vrstici ;-)
##def najboljsi(s):
##    return min([sposobnost(x[0], x[1], x[2]) for x in s])

2. podnaloga

Butalski polži se premikajo samo v štirih smereh: sever, jug, vzhod in zahod, kar v koordinatnem sistemu pomeni gor, dol, desno in levo. V vsakem koraku se premaknejo za eno enoto, lahko pa tudi mirujejo. Njihove premike zato lahko opišemo s seznami, ki vsebujejo poljubno zaporedje črk 'S', 'J', 'V', 'Z' in 'M'.

Napišite funkcijo pot(premiki), ki sprejme seznam, ki opisuje premike polža in pove, kako daleč je polž lezel in kako daleč (zračne razdalje) je prilezel. Rezultat naj bo dan v obliki para celo in dec. število, kjer je slednje zaokroženo na dve decimalni mesti.

>>> pot(['M', 'J', 'V', 'S', 'S', 'M'])
(4, 1.41)

Uradna rešitev

def pot(premiki):
    '''Kako daleč je lezel in prilezel polž'''
    x=0
    y=0
    kolikoMirovanj = 0
    for premik in premiki: # izračunamo končno točko, ter število mirovanj
        if premik == 'S':
            y += 1
        elif premik == 'J':
            y -= 1
        elif premik == 'V':
            x += 1
        elif premik == 'Z':
            x -= 1
        else:
            kolikoMirovanj += 1
    kolikoLazenja = len(premiki) - kolikoMirovanj 
    return (kolikoLazenja, round((x**2 + y**2)**0.5, 2))

3. podnaloga

Po končanem tekmovanju so znane poti vseh polžev. Velja, da je zmagovalec tisti polž, ki je prilezel najdlje (seveda pa si lahko prvo mesto deli več zmagovalcev, če so prelezli isto razdaljo). Pri tem seveda doseženo razdaljo računamo zaokroženo, kot pri zg. nalogi! Napišite funkcijo zmagovalci(s), ki sprejme seznam poti vseh polžev ter izračuna, koliko polžev je osvojilo prvo nagrado.

>>> zmagovalci([['S', 'Z', 'S'], ['M', 'Z', 'V'], ['J', 'J', 'Z'], ['J', 'J', 'Z'], ['S', 'S', 'M']])
3

Uradna rešitev

def zmagovalci(poti):
    '''Koliko polžev je zmagalo'''
    najRazdalja = -1 # vse poti bodo daljše
    kolikoZmagovalcev = 0
    for potPolza in poti:
        _, razdalja = pot(potPolza) # lazil nas načeloma ne zanima, zato "čudno" imen - le podčrtaj
        if razdalja > najRazdalja: # našli smo boljšega
            najRazdalja = razdalja
            kolikoZmagovalcev = 1 # začnemo šteti znova
        elif razdalja == najRazdalja:
            kolikoZmagovalcev += 1 # en več z naj razdaljo
    return kolikoZmagovalcev        

#
# z nekaj dodatnega znanja je zapis rešitve krajši
#
##def zmagovalci(s):
##    p = [pot(x) for x in s]
##    mx = max(p)
##    return p.count(mx)

Seznami in nizi


Mirko, Slavko in Blaise Pascal

1. podnaloga

Mirko in Slavko sta postala navdušena nad vsemi oblikami števil, zaporedij in podobno. Potem ko sta se naveličala preštevati, ali število zrn v krogih pri sončnici res ustreza Fibonaccijevemu zaporedju, sta naletela na funkcijo, ki naj bi vrnila seznam, ki vsebuje prvih n vrstic Pascalovega (binomskega) trikotnika (vrstica naj bo spet seznam).

  def trikotnik(n):
      '''Vrne n vrstic Pascalovega trikotnika'''
      celota = [[1]] # bo tole prav?
      zgoraj = []
      j = 0
      while j <= n:   # tu je nekaj narobe
          i = 1
          nova = [1]
          while i < len(zgoraj):
              nova = [zgoraj[i - 1] + zgoraj[i]] # hm, tole ...
              i = i + 1
          zgoraj = nova + [1]
          celota = celota + zgoraj # tudi tukaj
          j = j + 1 
      return celota

Žal (kot običajno) ima nekaj napak. Odpravi ji ... Na srečo je Mirko že intenzivno preiskušal funkcijo in odkril štiri možna mesta napak (glej oznake v kodi). Slavko pravi, da je Mirko pretiraval in da so napake zagotovo tri.

Uradna rešitev

def trikotnik(n):
    '''Vrne n vrstic Pascalovega trikotnika'''
    celota = [[1]] # začetna vrstica
    zgoraj = [] # prejšnja vrstica
    j = 0
    while j < n - 1:   
        i = 1
        nova = [1] # sestavimo novo vrstico
        while i < len(zgoraj): # na osnovi prejšnje sestavimo novo
            nova += [zgoraj[i - 1] + zgoraj[i]] # dodamo vsoto zgornjih členov
            i = i + 1
        zgoraj = nova + [1] # še zaključna enka - in si jo zapomnimo za naslednji korak
        celota = celota + [zgoraj] # dodamo v trikotnik
        j = j + 1 
    return celota

EMŠO

Številka EMŠO je sestavljena iz trinajst števk v obliki DDMMLLL50NNNX, pri čemer je DDMMLLL rojstni datum, 50 je koda registra, NNN je zaporedna številka in X kontrolna številka. Trimestna številka, NNN, je med 000 in 499 za moške ter med 500 in 999 za ženske.

1. podnaloga

Napiši funkcijo je_zenska(emso), ki za številko EMŠO, ki jo podamo kot niz, vrne True, če pripada ženski in False, če moškemu.

Primer:

>>> je_zenska('0505913509174')
True
>>> je_zenska('2109913502875')
False

Uradna rešitev

def je_zenska(emso):
    '''Ali EMSO predstavlja žensko'''
    return emso[9] >= "5"   # od 000 do 499 velja, da na 9. mestu stoji št. med 0 in 4
                            # od 500 do 999 velja, da na 9. mestu stoji št. med 5 in 9

2. podnaloga

Za okroglo mizo sedijo gosti. Nekateri so srečni, drugi ne. Konkretno: srečni so moški, ki sedijo med dvema ženskama in ženske, ki sedijo med dvema moškima. Razpored gostov podamo s seznamom njihovih številk EMŠO.

Napiši funkcijo stevilo_srecnezev(razpored), ki prejme takšen seznam in vrne število srečnih gostov.

Pazi: Ker je miza okrogla, sedi prvi gost poleg zadnjega. Če sedita moški in ženska sama za okroglo mizo, seveda nista srečneža.

Primer:

>>> stevilo_srecnezev(['0903912505707', '0110913506472', '2009956506012','1102946502619', '1902922506199', '2602930503913',
                       '0204940508783','1602960505003', '0207959502025', '0207962509545'])
4
>>> stevilo_srecnezev(['0702948501362', '1505987508785'])
0

Uradna rešitev

def stevilo_srecnezev(razpored):
    '''Koliko moških sedi med dvema ženskama, oz.
       koliko žensk sedi med dvema moškima'''
    srecnih = 0
    n = len(razpored)
    if n < 3: # če nimamo vsaj tri osebe, ni srečnih!
        return 0
    
    for i in range(n):
        pred = razpored[i-1]    # sedez pred i-tim sedezom
        tren = razpored[i]      # i-ti sedez
        nasl = razpored[(i+1) % n]      # sedez za i-tim sedezom
        
        if je_zenska(pred) != je_zenska(tren) != je_zenska(nasl):  # ce je na sredini moski, mora
            srecnih += 1                                  # imeti levo in desno zensko in obratno
            
    return srecnih

Dis... kva že?

Jeremija ves svoj prosti čas (torej ko ne spi, ko ne toži nad bolečinami, skratka kako minuto na dan) preživi kot moderator na nekem manj znanem forumu, ki ima kar lepo število registriranih uporabnikov, vendar večina med njimi piše same neumnosti. Med takimi je še posebno dejaven Tomaž Majer. Če se zdi Jeremiji objava žaljiva, jo nemudoma zbriše. Če objava na forumu ni žaljiva, ampak je le precej neumna, pa naredi naslednje: Vse samoglasnike odstrani iz besedila in jih (v enakem vrstnem redu) doda nazaj na konec. Na primer sporočilo:

Banana je najboljša! Izjemno dobra je tudi v kombinaciji z Nutelo!

postane

Bnn j njbljš! zjmn dbr j td v kmbncj z Ntl!aaaeaoaIeooaeuioiaiiueo

Takó besedilo postane težko berljivo in večina bralcev ga ignorira. Če pa se najde kdo, ki ga vsebina resnično zanima, lahko z nekaj truda razvozla vsebino sporočila.

Opisana transformacija besedila je znana pod imenom disemvoweling.

1. podnaloga

Napišite funkcijo prviSamoglasnik(niz), ki kot argument dobi niz niz in vrne položaj (indeks) prvega samoglasnika v danem nizu. Če v nizu ni samoglasnika, vrni -1

>>> prviSamoglasnik('Krščen matiček!')
4

Uradna rešitev

def prviSamoglasnik(niz):
    '''vrne položaj (indeks) prvega samoglasnika v danem nizu'''
    samoglasniki = "aeiouAEIOU"
    kje = 0
    for znak in niz:
        if znak in samoglasniki: # takoj pri prvem bomo zaključili zadevo
            return kje
        kje += 1
    return -1 # ni ga bilo 

def prviSamoglasnikDieHardPython(niz): #dieHard Pythonovci bi seveda zadevo napisali takole
    samoglasniki = "aeiou"
    return min(niz.index(c) for c in samoglasniki + samoglasniki.upper() if c in niz)

2. podnaloga

Napišite funkcijo disemvowel(sporocilo), ki kot argument prejme niz sporocilo. Funkcija naj sestavi in vrne nov niz, ki ga dobi iz niza sporocilo tako, da vse samoglasnike prestavi na konec niza.

>>> disemvowel('Banana je dobra!')
'Bnn j dbr!aaaeoa'

Uradna rešitev

def disemvowel(sporocilo):
    '''desemvowlanje sporočila'''
    brez = '' # niz brez samoglasnika
    samoglasniki = '' # vsi samoglasniki iz niza
    for c in sporocilo:
        if c in 'aeiouAEIOU': # gre za samoglasnik
            samoglasniki += c
        else: #"običajni" znak
            brez += c
    return brez + samoglasniki #zlepimo v pravem redu

3. podnaloga

Včasih pa želimo sporočilo dešifrirati, saj nas zanima njegova vsebina. Ta naloga je vse prej kot enostavna, saj ne vemo kam točno je treba vriniti izgnane samoglasnike. Sporočilo 'Bnn j dbr!aaaeoa' bi lahko dešifrirali tudi kot 'Banna ja debora!', kot 'Bnana aje dobar!' ipd.

Bob Rock je na mesta v besedilu, kjer predvideva, da bi morali stati samoglasniki, vstavil znake '*' (zvezdica). V sporočilu zvezdica nikoli ne bo imela drugega pomena. Prav tako bo število samoglasnikov enako številu zvezdic in vsi samoglasniki bodo na koncu niza.

Napišite funkcijo razveljaviDisemvowel(niz), ki bo zvezdice v nizu nadomestila s samoglasniki, ki se v nizu niz nahajajo na koncu. Zgled:

>>> razveljaviDisemvowel('B*n*n* j* d*br*!aaaeoa')
'Banana je dobra!'

Uradna rešitev

def razveljaviDisemvowel(niz):
    '''zvezdice v disemvowlanem nizu nadosmesti s samoglasniki s konca niza'''
    stZvezdic = niz.count('*') #da bomo vedeli, kje se začno samoglasniki
    if stZvezdic == 0: # ni kaj delati
        return niz 
    samoglasniki = niz[-stZvezdic:]  # odrežemo ustrezen del niza
    sporocilo = ''
    stevec = 0 # kje v nizu samogalsnikov smo
    for c in niz[:-stZvezdic]: # pregledamo le del niza brez končnih samoglasnikov
        if c == '*': # zvezdico nadomestimo z ustreznim samoglasnikom
            sporocilo += samoglasniki[stevec]
            stevec += 1 # premaknemo se naprej po samoglasnikih
        else:
            sporocilo += c
    return sporocilo

Cvetoče črke

1. podnaloga

Učenci so se v šoli v naravi med prostim časom zabavali z različnimi besednimi igrami. Ena izmed njih je bila ugibanje pomena čudnih besed. Pravilo je bilo, da so si zamišljali besede, ki vsebujejo črkovno zvezo 'pa' in jo nadomestili z zvezo 'cveti'. Tako so nastajale smešne besede kot na primer: cvetiprika, cveticvetigaj, nacvetidalec itd... Sestavi funkcijo cvetoceBesede(niz), ki bo vrnila pravi pomen teh besed.

Pri tem ne smeš uporabiti funkcije replace!

Na primer:

         >>>cvetoceBesede('cveticvetigaj')
         papagaj

Uradna rešitev

def cvetoceBesede(niz):
    '''izloči 'cveti' in nadomesti s 'pa' '''
    pravaBeseda = ''
    i = 0
    while i < len(niz) - 4: 
      if niz[i:i+5] == 'cveti':  # jemljemo po 5 znakov
          pravaBeseda += 'pa' # prvi dve zaporedni črki iz niza 'cveti' zamenja z 'pa', ostale tri pa izbriše
          i += 5
      else:
          pravaBeseda += niz[i]
          i += 1
    # prepišemo še mnorebitni preostanek
    while i < len(niz):
        pravaBeseda += niz[i]
        i += 1
    return pravaBeseda

def cvetoceBesedeV1(niz):
    '''izloči 'cveti' in nadomesti s 'pa' 
       uporaba ukaza del '''
    pravaBeseda=''
    if 'cveti' in niz: #ugotovi, če je v besedi besedna zveza "cveti" 
        sezCvet=list(niz)#niz spremeni v seznam
        i=0
        while i<len(sezCvet)-4: # prvi dve zaporedni črki iz niza 'cveti' zamenja z 'pa', ostale tri pa izbriše, nato pregleda še ostanek seznama
            if sezCvet[i:i+5]==['c', 'v', 'e', 't', 'i']:
                sezCvet[i]='p'
                sezCvet[i+1]='a'
                del sezCvet[i+2:i+5]
            i+=1
            pravaBeseda=''.join(sezCvet) #seznam zopet spremeni v niz in ga vrne
    else:
        return niz
    return pravaBeseda

def cvetoceBesedev2(niz):
    '''izloči 'cveti' in nadomesti s 'pa'
       uporabimo replace
    '''
    pravaBeseda = niz.replace('cveti', 'pa')
    return pravaBeseda

def cvetoceBesedev3(niz):
    '''izloči 'cveti' in nadomesti s 'pa'
       uporabimo split in join
    '''
    pravaBeseda = 'pa'.join(niz.split('cveti'))
    return pravaBeseda

2. podnaloga

V vasi Muhačevo so bili navdušeni pevci in so radi prepevali znano ljudsko: "Priletela muha na zid." Besedilo pesmi je povsem enostavno, saj se v bistvu ves čas ponavlja isto besedilo. Posebnost pesmi je v tem, da se najprej poje tako, da se vsi samoglasniki spremenijo v a, nato v e in tako naprej, dokler ne pridejo na vrsto vsi samoglasniki (a,e,i,o,u). Sestavi funkcijo muha(niz), ki bo vpisano besedo ali besedno zvezo spremenila v seznam besed, v katerih bodo najprej vsi samoglasniki a, nato e in še i, o in u. Beseda je napisana z malimi tiskanimi črkami.

Na primer:

   >>> muha('trobentica')
   ['trabantaca', 'trebentece', 'tribintici', 'trobontoco', 'trubuntucu'].

Uradna rešitev

def muha(niz):
    '''vrne seznam besed, v katerih so spremenjeni vsi samoglasniki
       najprej v a, nato v e in tako naprej'''
    vsiSamoglasniki = 'aeiou' 
    seznamBesed = []
    for samoglasnik in vsiSamoglasniki: # uporabimo vse samoglasnike
        novaBeseda = ''
        for znak in niz:  # pregledamo vse znake v nizu 
            if znak in vsiSamoglasniki: # če gre za poljuben samoglasnik
                novaBeseda += samoglasnik # dodamo trenutno 'aktivnega'
            else:
                novaBeseda += znak # sicer ga le prepišemo
        seznamBesed.append(novaBeseda) # beseda z ustreznim samoglasnikom
    return seznamBesed

3. podnaloga

Učiteljica je dala učencem nalogo naj napišejo tri besede tako, da se bo vsaka črka v vseh treh besedah pojavila natanko enkrat. Vsako novo pojavitev iste črke bo štela za napako. Torej, če se bo črka a pojavila trikrat, bosta to dve napaki. Sestavi funkcijo besede(niz), ki bo preštela napake in vrnila, koliko napak je učenec naredil. Pri tem so besede ločene z vejicami, ki niso del besed, vsi ostali zanki pa so!

Na primer:

      >>>besede('miza,miha,zima')
      7
      >>>besede('mimi,ali,gremo')
      4

Uradna rešitev

def besede(niz):
    '''funkcija prešteje, kolikokrat se posamezne črke pojavijo več kot enkrat'''
    koliko = 0
    črke = '' # katere črke so se že pojavile
    for znak in niz:
        if znak != ',': # vejice ignoriramo
            if znak in črke: # znak se je pojavil že prej
                koliko += 1
            else:
                črke += znak # nov znak
    return koliko

Škatle

Lojzku je dolgčas, zato se igra z velikimi praznimi škatlami, ki se nahajajo v skladišču, v katerem dela. Dimenzije škatel so shranjene v seznamu trojic. Na primer, trojica (50, 100, 100) predstavlja škatlo, ki je visoka 50 cm ter široka in dolga 100 cm.

S trojicami delamo tako kot s seznami, le spremijati ne moremo elementov. Torej tro[0] nam da prvi element iz trojice. Seveda pa lahko trojko tudi "razpakiramo"

   vis, sir, dol = skatla

če je skatla neka trojka.

1. podnaloga

Sestavite funkcijo stolp(skatle), ki vrne višino najvišjega stolpa, ki ga lahko sestavimo iz škatel, ne da bi jih obračali. Pri tem ni treba paziti na stabilnost stolpa. Na primer

  >>>stolp([(50, 100, 100), (60, 30, 50), (40, 40, 40)])
  150

Uradna rešitev

def stolp(skatle):
    '''Višina stolpa iz škatel, če škatle
       zlagamo po višini (prvi dimenziji)'''
    skupnaVišina = 0
    for skatla in skatle: # pregledamo vse škatle
        visina, sirina, dolzina = skatla
        skupnaVišina += visina
    return skupnaVišina

##Dodatno znanje nam da krajši zapis rešitve:
##def stolp(skatle):
##    return sum(v for v, _, _ in skatle)

2. podnaloga

Sestavite funkcijo najvisjiStolp(skatle), ki vrne višino najvišjega stolpa, ki ga lahko sestavimo iz škatel, če jih lahko obračamo. Pri tem še vedno ni treba paziti na stabilnost stolpa. Na primer, najvišji tak stolp iz škatel z dimenzijami (50, 100, 100), (60, 50, 50) in (40, 40, 40) bi imel višino 200 cm, saj bi prvo škatlo prevrnili na bok.

Uradna rešitev

def najvisjiStolp(skatle):
    '''Kako visok je lahko stolp, če lahko škatle obračamo''' 
    višina = 0
    for skatla in skatle:
        višina += max(skatla) # pobrali smo največjo dimenzijo
    return višina

##Dodatno znanje nam da krajši zapis rešitve:
##def najvisjiStolp(skatle):
##    return sum(max(v, s, d) for v, s, d in skatle)

3. podnaloga

Sestavite funkcijo greNotri(skatla1, skatla2), ki vrne True, če škatlo skatla1 lahko obrnemo tako, da gre v škatlo skatla2, torej da so dimenzije prve škatle strogo manjše od dimenzij druge škatle.

Uradna rešitev

def poVrsti(trojka):
    ''' uredimo trojko (a, b, c) po velikosti 
        (t1, t2, t3) so torej premešani a, b, c
        da velja t1 <= t2 <= t3 '''
    a, b, c = trojka
    mal = min(a, b, c)
    vel = max(a, b, c)
    sre = a + b + c - mal - vel
    return (mal, sre, vel)

def greNotri(skatla1, skatla2):
    '''Ali lahko skatlo1 damo v skatlo2'''
    nova1 = poVrsti(skatla1)
    nova2 = poVrsti(skatla2)
    
    return nova1[0] < nova2[0] and \
           nova1[1] < nova2[1] and \
           nova1[2] < nova2[2]

Ukleščeni nizi

1. podnaloga

Sestavite funkcijo prestej(niz), ki prešteje, koliko zašiljenih oklepajev tj. '<' in '>' je v nizu niz. Primer:

>>> prestej('<abc>>')
3
>>> prestej('a>>  x<Y>x  <<b')
6
>>> prestej('Koliko oklepajev > in < je v tem nizu?')
2

Pri tem ne smeš uporabiti vgrajene metode count

Uradna rešitev

def prestej(niz):
    '''Koliko je v nizu zašiljenih oklepajev'''
    koliko = 0
    iskaniZnaki = "><"
    for znak in niz:
        if znak in iskaniZnaki:
            koliko += 1
    return koliko

2. podnaloga

Niz imenujemo ukleščen, če se začne z predklepajem '<', konča z zaklepajem '>', vmes pa ni nobenega znaka za zašiljene oklepaje. Na primer, niz '<Zunaj sije sonce.>' je ukleščen niz.

Sestavite funkcijo uklescen(niz), ki preveri, ali je niz niz ukleščen. Primer:

>>> uklescen('<Zunaj sije sonce.>')
True
>>> uklescen('<   <3    >')
False
>>> uklescen('Zunaj sije sonce.')
False

Uradna rešitev

def uklescen(niz):
    '''Ali je niz ukleščen'''
    if len(niz) < 2: # niz je prekratek!
        return False
    return niz[0] == '<' and niz[-1] == '>' and prestej(niz) == 2

3. podnaloga

Sestavite funkcijo sklop(niz1, niz2), ki prejme dva ukleščena niza in vrne ukleščeni niz, ki predstavlja njun sklop. (Kaj je to sklop, je razvidno iz spodnjega primera.) Primer:

>>> sklop('<123>', '<456>')
'<123456>'
>>> sklop('<muca>', '<copatarica>')
'<mucacopatarica>'

Uradna rešitev

def sklop(niz1, niz2):
    '''Sklopimo dva ukleščena niza'''
    return niz1[:-1] + niz2[1:]

4. podnaloga

Sestavite funkcijo razlomi(niz, s), ki kot argumenta prejme ukleščeni niz niz in seznam nenegativnih celih števil s ter vrne seznam ukleščenih nizov, ki imajo dolžine, naštete v seznamu s, in skupaj tvorijo niz niz. Primer:

>>> razlomi('<Zunaj sije sonce.>', [2, 7, 8])
['<Zu>', '<naj sij>', '<e sonce.>']
>>> razlomi('<muca copatarica>', [4, 0, 11])
['<muca>', '<>', '< copatarica>']

Predpostavite lahko, da bo vsota števil v seznamu s enaka dolžini ukleščenega niza niz (če ne štejemo zašiljenih oklepajev).

Uradna rešitev

def razlomi(niz, s):
    '''Razlomi ukleščen niz na manjše koščke in vrne seznam koščkov'''
    rez = []
    k = 1 # kje začnemo ukleščeni niz
    for dolzina in s:
        rez.append('<' + niz[k:k+dolzina] + '>')
        k += dolzina
    return rez

Beseda je dala besedo

1. podnaloga

Sestavite funkcijo palindrom(niz), ki vrne True kadar je niz palindrom, in False sicer.

Uradna rešitev

def palindrom(niz):
    '''Ali je niz palindrom'''
    return niz == niz[::-1]

2. podnaloga

Sestavite funkcijo palindromHitro(niz), ki vrne True kadar je niz palindrom, in False sicer.

Če niz ni palindrom, naj to ugotovi kar se da hitro (npr. Matija očitno ni palibndrom, saj M ni enak a

Uradna rešitev

def palindromHitro(niz):
    '''Ali je niz palindrom'''
    for ind in range(1, len(niz) // 2 + 1):
        if niz[ind - 1] != niz[-ind]: # neujemajoča se znaka
            return False
    #vsi pari se ujemajo
    return True

3. podnaloga

Pravimo, da je beseda skoraj palindrom, če ji je treba zbrisati natanko eno črko, da bi postala palindrom. Primer je beseda 'kolo', ki ji moramo zbrisati črko 'k', pa postane palindrom 'olo'.

Sestavite funkcijo skorajPalindrom(niz), ki preveri, ali je niz skoraj palindrom. Vse znake (tudi presledke) v besedi obravnavamo enako.

Uradna rešitev

def skorajPalindrom(niz):
    '''ALi je niz skoraj palindrom'''
    # za vsak i poskusimo izpustiti črko na i-tem mestu
    for i in range(len(niz)):
        # če smo dobili palindrom, končamo
        if palindrom(niz[:i] + niz[i + 1:]):
            return True
    # če je zanka prišla do konca, palindroma nismo našli
    return False

4. podnaloga

Sestavite funkcijo vsebuje(niz1, niz2), ki za parametra dobi dva niza ter vrne True, če in samo če je mogoče prvi niz dobiti tako, da v drugi niz na poljubnih mestih vstavljamo dodatne znake. (povedano drugače, če prvi niz vsebuje vse znake iz drugega niza v ustreznem vrstnem redu).

Iz besede 'ROLA' lahko na primer z vrivanjem znakov dobimo besedo 'pRikOLicA'. Med velikimi in malimi tiskanimi črkami strogo ločujemo.

Uradna rešitev

def vsebuje(niz1, niz2):
    '''Ali je niz2 vsebovan v nizu1'''
    # v spremenljivki indeksNiz2 hranimo indeks znaka v malem nizu, ki ga iščemo
    indeksNiz2 = 0
    for znakNiz1 in niz1:
        # če smo našli znak na indeksu indeksNiz2, iščemo naslednjega
        if niz2[indeksNiz2] == znakNiz1:
            indeksNiz2 += 1
        # če smo našli vse znake v malem nizu, končamo
        if indeksNiz2 == len(niz2):
            return True
    # če se zanka konča, vseh znakov nismo našli
    return False

5. podnaloga

Sestavite funkcijo besede(niz), ki sestavi in vrne seznam parov indeksov (začetek, konec), ki določajo, kje v danem nizu se nahajajo posamezne besede. Beseda je maksimalno zaporedje znakov, ki ne vsebuje presledka.

>>> besede('Danes je lep dan, saj ne dežuje.')
[(0, 4), (6, 7), (9, 11), (13, 16), (18, 20), (22, 23), (25, 31)]
>>> besede(' abc  abc  abc ')
[(1, 3), (6, 8), (11, 13)]

Uradna rešitev

def besede(stavek):
    '''Seznam položajev besed'''
    indeksi = []
    # ali smo našli besedo, ali smo pri presledkih
    smo_v_besedi = False
    # zacetek trenutne besede
    zacetek = 0
    for i in range(len(stavek)):
        z = stavek[i]  # ta in prejšnji stavek lahko nadomestimo z enim
        # for i, z in enumerate(stavek):
        
        # če smo v besedi in najdemo presledek, dodamo njene indekse v seznam
        if smo_v_besedi and z == ' ':
            smo_v_besedi = False
            indeksi.append((zacetek, i - 1))
        # če nismo v besedi in najdemo znak, začnemo z besedo
        elif not smo_v_besedi and z != ' ':
            smo_v_besedi = True
            zacetek = i
    # če smo na koncu zanke še vedno v besedi, ta beseda sega do konca
    if smo_v_besedi:
        indeksi.append((zacetek, len(stavek) - 1))
    return indeksi

V parih nad sezname in nize


Vsote podseznamov

1. podnaloga

Vsote peterk

Podan je seznam nenegativnih števil in naloga je poiskati največjo vsoto petih zaporednih števil v tem seznamu. Napiši funkcijo vsota_peterk(sez), ki prejme seznam nenegativnih števil sez in vrne največjo vsoto. Če je v seznamu manj kot 5 štervil, vrni -1.

Primer (največja vsota je pri 3, 8, 9, 0, 4):

 >>> vsota_peterk([9, 1, 2, 3, 8, 9, 0, 4, 3, 7])
 24

Uradna rešitev

def vsota_peterk(sez):
    '''Vrne maksimalno vsoto pet zaporednih elementiov seznama'''
    if len(sez) < 5:
        return -1
    najvecja = 0 # doslej največja vsota
    for zacetek in range(len(sez) - 4):             # preverjamo samo do 5. elementa na koncu
        vsota = sum(sez[zacetek : zacetek + 5])     # sestevamo po 5 elementov naenkrat
        if vsota > najvecja:        # ce je trenutna vsota vecja od najvecje
            najvecja = vsota        # smo nasli novo najvecjo vsoto
    return najvecja

2. podnaloga

Sestavi funkcijo, ki za dani seznam in naravno število k izračuna in vrne seznam vsot vseh strnjenih podseznamov dolžine k.

 >>> vsote([1, 2, 3, 4], 2)
 [3, 5, 7]
 >>> vsote([2, 6, 3, 7, 5, 2, 3], 3)
 [11, 16, 15, 14, 10]
 >>> vsote([3, 5, 1], 1)
 [3, 5, 1]
 >>> vsote([3, 5, 1], 3)
 [9]
 >>> vsote([3, 5, 1], 4)
 []

Uradna rešitev

def vsote(sez, k):
    '''vsote vseh strnjenih podseznamov dolžine k'''
    if k > len(sez): #nimamo nobenega takega podseznama
        return []
    vsota = 0
    #najprej seštejemo prvih k
    for i in range(k):
        vsota += sez[i]
    sezVsot = [vsota]
    #sedaj pa gremo do konca seznama in prištejemo konec in odštejemo začetek
    for i in range(k, len(sez)):
        vsota = vsota - sez[i-k] + sez[i]
        sezVsot.append(vsota)
    return sezVsot

3. podnaloga

Razmisli, kako bi rešil nalogo, kjer moramo poiskati največjo vsoto strnjenega podzaporedja, če je seznam zelo velik (> 100 000) in iščemo največjo vsoto daljših (> 10 000) zaporedij števil. Napiši funkcijo max_vsota_nterk(s, n), kjer je s seznam števil in n število zaporednih števil.

Če bi morali poiskati vsoto desettisočerk, bi moral na vsakem koraku sešteti 10 000 števil, to bi storili skoraj milijonkrat. Vsega skupaj bi seštel deset milijard števil, za kar bi potrebovali kar nekaj časa.

Pri preverjanju na sistemu Tomo - če odgovora ne dobiš v recimo 20 sekundah - s CTRL-Cprekini program, saj tvoja rešitev očitno ni dovolj hitra!

Uradna rešitev

def max_vsota_nterk(s, n):
    '''Računanje naj n-terke v velikih seznamih'''
    najvecja = vsota = sum(s[:n]) # začetni kandidat je vsota prvih n
    for i in range(n, len(s)):          # sestevamo ze sproti
        vsota += s[i] - s[i-n]          # vsakic ko povecamo indeks za 1, se
        if vsota > najvecja:            # prvi element odsteje, naslednji pa doda
            najvecja = vsota
            
    return najvecja

Ujemanja

1. podnaloga

Sestavite funkcijo zacetek(a, b), ki bo za niza a in b preštela, v koliko začetnih znakih se ujemata.

Niza nimata nujno enakega začetka, lahko se delno ujemata na začetku, lahko se en niz v celoti pojavi na začetku drugega, ali pa sta niza celo enaka. Ni potrebno obravnavati vsakega od teh primerov posebej, samo pazite, da bo funkcija delala pravilno za vse primere.

>>> zacetek('matematika', 'fizika')
0
>>> zacetek('matematika', 'matija')
3
>>> zacetek('oder', 'oderuh')
4
>>> zacetek('izpit', 'izpit')
5

Pri rešitvi obvezno uporabite zanko while.

Uradna rešitev

def zacetek(a, b):
    ''' v koliko začetnih znakih se ujemata niza a in b'''
    i = 0
    dolžinaKrajšega = min(len(a), len(b))
    while i < dolžinaKrajšega and a[i] == b[i]:
        i += 1
    return i

2. podnaloga

Sestavite funkcijo zacetekFor(a, b), ki bo za niza a in b preštela, v koliko začetnih znakih se ujemata.

Pri rešitvi ne smete uporabiti zanke while.

Uradna rešitev

def zacetekFor(a, b):
    ''' v koliko začetnih znakih se ujemata niza a in b'''
    najkrajsi = min(len(a),len(b))
    for i in range(najkrajsi): # ustaviti se moramo, ko krajšega niza zmanjka
        if a[i] != b[i]:
            return i # i-ti znak je prvi neujemajoči, ker štejemo od 0 dalje ...
    return najkrajsi # ujemala sta se v vseh

3. podnaloga

Napiši funkcijo st_ujemanj(b1, b2), ki prejme dve besedi in vrne število znakov, v katerih se besedi ujemata.

Primeri:

>>> st_ujemanj("ROKA", "ROKAV")
4
>>> st_ujemanj("CELINKE", "POLOVINKE")
1

Uradna rešitev

def st_ujemanj(b1, b2):
    '''Koliko istoležnih znakov je enakih'''
    ujemanja = 0   
    for i in range(min(len(b1), len(b2))):  # zanka se izvede tolikokrat koliko je dolga najkrajsa beseda b1 oz. b2
        if b1[i] == b2[i]:          # ce sta znaka v obeh besedah enaka, se ujemanje poveca za 1. 
            ujemanja += 1
    return ujemanja

4. podnaloga

Napiši funkcijo ujeme(b1, b2), ki kot vhod prejme dve besedi b1 in b2, ter vrne novo besedo, ki vsebuje tiste znake, v katerih se besedi ujemata (imata na istem mestu isti znak), znaki na ostalih mestih pa so zamenjane s pikami. Če besedi nista enako dolgi, naj bo nova beseda toliko dolga, kolikor je dolga krajša izmed podanih besed.

Primeri:

>>> ujeme("ROKA", "REKE")
'R.K.'
>>> ujeme("ROKA", "ROKAV")
'ROKA'

Uradna rešitev

def ujeme(b1, b2):
    '''Vrne niz, ki označuje, kako se ujkemata niza'''
    rezultat = ""
    for i in range(min(len(b1), len(b2))):
        if b1[i] == b2[i]:      # ce sta znaka enaka
            rezultat += b1[i]   # potem v rezultat dodamo ta znak
        else:
            rezultat += "."     # sicer dodamo piko.
    return rezultat

Žaba

Žaba (Salientia) skače po ravnini. Na začetku in po vsakem skoku zabeležimo projekcijo točke (tj. položaja žabe) na abscisno in ordinatno os, s čemer dobimo seznama px in py. Sestavili bomo sklop funkcij, s katerimi bomo analizirali žabino gibanje.

1. podnaloga

Sestavite funkcijo vPravokotniku(tocka, pravokotnik), ki za dano točko tocka vrne True, če je točka znotraj ali na robu pravokotnika pravokotnik. Točka v ravnini je podana s seznamom $[x, y]$, pravokotnik pa s seznamom $[x1, y1, x2, y2]$, kjer sta $(x1, y1)$ in $(x2, y2)$ koordinati nasprotnih oglišč.

Uradna rešitev

def vPravokotniku(tocka, pravokotnik):
    [x, y] = tocka
    [x1, y1, x2, y2] = pravokotnik
    (x1, x2) = (min(x1, x2), max(x1, x2))
    (y1, y2) = (min(y1, y2), max(y1, y2))
    return x1 <= x and x <= x2 and y1 <= y and y <= y2

2. podnaloga

Sestavite funkcijo pot(px, py), ki iz danih seznamov px in py rekonstruira pot žabe, tj. vrne seznam točk, v katerih je bila žaba. Točko podano kot seznam dolžine 2.

>>> pot([0, 0, 1, 2], [0, 1, 2, 1]))
[[0, 0], [0, 1], [1, 2], [2, 1]]

Predpostavite lahko, da sta seznama px in py enakih dolžin.

Uradna rešitev

def pot(px, py):
    '''Določi točke na žabini poti'''
    kje = 0
    seznam = []
    while kje < len(px):
        seznam.append([px[kje], py[kje]])
        kje += 1
    return seznam      

##dodatno znanje nam da enovrstični zapis:
##
##def pot(px, py):
##    return [[x, y] for x, y in zip(px, py)]

3. podnaloga

Sestavite funkcijo najdaljsiSkok(tocke), ki iz seznama tocke, v katerih so zapisane točke, po katerih je skakala žaba, izračuna dolžino najdaljšega skoka. Če žaba ni naredila nobenega skoka, naj funkcija vrne None.

Uradna rešitev

def d(a, b):
    '''Razdalja med točkama a in b'''
    return ((a[0] - b[0])**2 + (a[1] - b[1])**2)**0.5

def najdaljsiSkok(tocke):
    '''Kako dolg je najdaljši skok'''
    if len(tocke) <= 1:
        return None
    najSkok = -1
    doskok = 1 # indeks, kjer je žaba doskočila
    while doskok < len(tocke):
        skok = d(tocke[doskok], tocke[doskok - 1])
        if skok > najSkok: # je skok daljši?
            najSkok = skok
        doskok += 1
    return najSkok    
    
##dodatno znanje nam da krajši zapis:
##def najdaljsiSkok(tocke):
##    if len(tocke) <= 1:
##        return None
##    else:
##        return max([d(tocke[i], tocke[i - 1]) for i in range(1, len(tocke))])

4. podnaloga

Žaba skače po ravnini in po nekaj skokih skoči v pravokotno blatno mlako. Sestavite funkcijo vMlaki(tocke, mlaka), ki naj vrne prvi podseznam skokov, ki v celoti potekajo po mlaki. Pri tem so skoki podani s seznamom vmesnih točk tocke, pravokotna mlaka pa s četverico mlaka, ki je oblike [x1, y1, x2, y2] kot v prvi podnalogi.

Če takega podseznama ni, naj funkcija vrne None.

Uradna rešitev

def vMlaki(tocke, mlaka):
    '''Vrne prvi seznam skokov, ki potekajo po mlaki'''
    podSeznam = None
    katera = 0
    while katera < len(tocke):
        tocka = tocke[katera]
        if vPravokotniku(tocka, mlaka): # če smo v mlaki 
            if podSeznam == None: # še nimamo podseznama
                podSeznam = [tocka]
            else:
                podSeznam.append(tocka)
        elif podSeznam == None: # če nismo v mlaki in
                                # sploh še nismo skočili v mlako
            katera += 1
            continue # skačemo naprej
        else:
            break # konec veselja, bili smo v mlaki in smo skočili ven
        katera += 1
    return podSeznam

Igra življenja

Igro življenja si je izmislil britanski matematik John H. Conway, gre pa takole: Imamo matriko, katere elementi sta logični vrednosti True in False. Vrednost True pomeni, da je celica živa, vrednost False pa pomeni, da je celica mrtva. Celice (razen robnih) imajo po 8 sosedov: dva horizontalna, dve vertikalna in štiri diagonalne. Čas teče v diskretnih korakih. S trenutnim stanjem sveta je tudi stanje sveta v naslednjem koraku natanko določeno in sicer po naslednjih pravilih:

  • Živa celica, ki ima manj kot 2 živa soseda, umre (zaradi osamljenosti).
  • Živa celica, ki ima 2 ali 3 žive sosede, preživi.
  • Živa celica, ki ima več kot 3 žive sosede, umre (zaradi prenaseljenosti).
  • Mrtva celica, ki ima natanko 3 žive sosede, oživi (reprodukcija).

Primeri matrik, ki predstavljajo stanje sveta:

svet_1 = [
    [False, False, False, False, False, False],
    [False, False, False, True, False, False],
    [False, True, False, False, True, False],
    [False, True, False, False, True, False],
    [False, False, True, False, False, False],
    [False, False, False, False, False, False]
]

svet_2 = [
    [False, False, False, False],
    [False, True, True, False],
    [False, True, True, False],
    [False, False, False, False]
]

Tukaj si lahko ogledate simulacijo

1. podnaloga

Napišite funkcijo zivi(svet, i, j), ki v svetu svet prešteje in vrne število živih sosedov celice v $i$-ti vrstici in $j$-tem stolpcu. Zgled (naj bo svet_1 matrika, kot je definirana zgoraj):

>>> zivi(svet_1, 2, 0)
2

Opomba: Kot je za Python običajno, se stolpci in vrstice začnejo številčiti pri 0.

Uradna rešitev

def zivi(svet, i, j):
    '''Koliko živih sosedov ima celica (i,j) '''
    n, m = len(svet), len(svet[0]) # dimenzije sveta
    stej = 0
    for vrst in range(i-1, i+2):
        for stol in range(j-1, j+2):
            if (vrst == i) and (stol == j): # sama sebi ni sosed
                continue
            if (vrst < 0) or (vrst > n-1) or (stol < 0) or (stol > m-1):
                # smo izven sveta
                continue
            if svet[vrst][stol] : # živ sosed!
                stej += 1 
    return stej

2. podnaloga

Napišite funkcijo igra(svet), ki sestavi in vrne matriko, ki predstavlja novo stanje sveta. Štiri pravila, ki določajo novo stanje sveta, so opisana zgoraj.

Zgled (matrika svet_1 naj bo enaka kot zgoraj):

>>> igra(svet_1)
[[False, False, False, False, False, False],
 [False, False, False, False, False, False],
 [False, False, True, True, True, False],
 [False, True, True, True, False, False],
 [False, False, False, False, False, False],
 [False, False, False, False, False, False]]

Uradna rešitev

def igra(svet):
    '''vrne nov svet po enem koraku'''
    n, m = len(svet), len(svet[0])
    novSvet = []
    for i in range(n): # po vrsticah
        novaVr = []
        for j in range(m): # po stolpcih
            novaVr.append(zivi(svet, i, j) == 3 or # celica ima tri žive sosede
                           (zivi(svet, i, j) == 2 and svet[i][j])) # ali pa 2 in je ona živa
        novSvet.append(novaVr)
    return novSvet
    
## Z nekaj več znanja lahko napišemo le
##     n, m = len(svet), len(svet[0])    
##     return [[zivi(svet, i, j) == 3 or \
##        (zivi(svet, i, j) == 2 and svet[i][j]) for j in range(m)] for i in range(n)]

3. podnaloga

Napišite funkcijo populacija(svet, n), ki naredi n korakov igre življenje in na vsakem koraku prešteje število živih celic. Ta števila naj vrne v obliki seznama, ki ima $n + 1$ elementov – prvo število v seznamu naj bo število živih celic v začetnem svetu. Zgled (matrika svet_1 naj bo enaka kot zgoraj):

>>> populacija(svet_1, 3)
[6, 6, 6, 6]

Funkcijo bomo testirali še na naslednjih svetovih (poleg tistih dveh, ki sta podana zgoraj):

svet_3 = [
    [False, False, False, False, False, False],
    [False, True, True, False, False, False],
    [False, True, True, False, False, False],
    [False, False, False, True, True, False],
    [False, False, False, True, True, False],
    [False, False, False, False, False, False]
]

svet_4 = [
    [True, True, True],
    [True, True, True],
    [True, True, True]
]

Nasvet: Najprej napišite pomožno funkcijo, ki prešteje število živih celic v matriki.

Uradna rešitev

def kolikoŽivih(svet):
    '''Koliko je v svetu živih celic'''
    n, m = len(svet), len(svet[0])
    koliko = 0
    for i in range(n): # po vrsticah
        novaVr = []
        for j in range(m): # po stolpcih
            if svet[i][j] : # živa celica!
                koliko += 1
    return koliko


def populacija(svet, n):
    '''Kako se v n korakih spreminja populacija živih celic'''
    ret = [kolikoŽivih(svet)] # stanje na začetku
    for i in range(n): # koliko korakov
        svet = igra(svet) # nov svet
        ret.append(kolikoŽivih(svet)) # dodamo število živih
    return ret

## Z nekaj več znanja:
##
## def kolikoŽivih(svet):
##    return sum(sum(1 if x else 0 for x in vr) for vr in svet)
##
## def populacija(svet, n):
##    ret = [pop(svet)]
##    for i in range(n):
##        svet = igra(svet)
##        ret.append(kolikoŽivih(svet))
##    return ret

Analiza besedila

Pri tej nalogi bomo analizirali nize, ki predstavljajo pravilno slovensko oblikovane besede in stavke. Pri vseh podnaloge lahko predpostavite, da so vhodni nizi s dobro oblikovani, tj. ne vsebujejo dveh zaporednih presledkov oz. nepotrebnih presledkov ter prelomov vrstice na začetku ali na koncu.

1. podnaloga

Sestavite funkcijo stevilo_besed(s), ki v podanem nizu prešteje število besed, pri čemer lahko predpostavite, da presledki stojijo natanko pred vsako (razen prvo) besedo v nizu. Primer:

>>> stevilo_besed('Višje, hitreje, močneje!')
3

Uradna rešitev

def stevilo_besed(s):
    '''Koliko besed je v nizu s'''
    if s == '':
        return 0
    return s.count(' ') + 1 # +1 zaradi prve besede

2. podnaloga

Sestavite funkcijo samoglasniki(s), ki v podanem nizu s prešteje število samoglasnikov. Zgled:

>>> samoglasniki('pomaranča')
4

Uradna rešitev

def samoglasniki(s):
    '''Koliko je v nizu s samoglasnikov'''
    vsiSamoglasniki = 'aeiouAEIOU'
    stevec = 0
    for c in s:
        if c in vsiSamoglasniki:
            stevec += 1
    return stevec

3. podnaloga

V Pythonu vrstice večvrstičnega niza ločujemo z znakom '\n'. Sestavite funkcijo vrstice(s), ki sprejme večvrstični niz s in vrne seznam, ki vsebuje vse vrstice tega niza (v istem vrstnem redu). Zgled:

>>> vrstice("Danes\n je lep\ndan.\n")
['Danes', ' je lep', 'dan.', '']

Opomba: Python obravnava niz '\n' kot en sam znak.

Uradna rešitev

def vrstice(s):
    '''Vrne seznam vrstic v nizu s'''
    return s.split('\n')

4. podnaloga

Haiku (japonsko 俳句) je japonska pesniška oblika iz treh verzov (vrstic), ki obsega sedemnajst zlogov. Prvi in tretji verz imata po pet zlogov, drugi sedem.

Na kulturnem natečaju TomoHaiku udeleženci oddajajo svoje izdelke na strežnik Tomo. Napišite kontrolno funkcijo haiku(s), ki sprejme niz s, ter vrne True, če niz ustreza pesniški obliki haiku, sicer pa vrne False.

Predpostavite lahko, da število samoglasnikov v neki besedi ustreza številu njenih zlogov, ter da niz s ne vsebuje nepotrebnih začetnih oz. končnih praznih vrstic. Vrstice so ločene z znakom za prelom vrstice '\n'. Primer:

>>> haiku('Skrit v svojem svetu,\ntemna otožnost neba,\ntvoj topli objem.')
True
>>> haiku('Riba,\nraca, rak,\nvinjak je grenak!')
False

Uradna rešitev

def haiku(s):
    '''Preveri, ali je v nizu s zapisan haiku'''
    if s.count('\n') != 2: # premalo vrstic
        return False
    i = s.find('\n') # konec prve vrstice
    j = s.find('\n', i + 1) # konec druge
    # prva in tretja morate imeti 5, srednja pa 7 samoglasnikov
    return samoglasniki(s[:i]) == samoglasniki(s[j:]) == 5 and samoglasniki(s[i:j]) == 7

5. podnaloga

Sestavite funkcijo podcrtaj(s), ki za parameter dobi niz s, v katerem so podnizi, ki bi morali biti izpisani podčrtano, označeni s podčrtajem na začetku in na koncu. Če je v nizu liho mnogo podčrtajev, si mislite, da je še eden na koncu. Funkcija naj vrne dvovrstični niz, kjer je v prvi vrstici originalni niz s toda brez podčrtajev, sledi znak za prelom vrstice, naslednjo vrstico pa sestavlja niz, sestavljen iz presledkov in minusov, pri čemer minusi ležijo pod tistimi deli besedila, ki morajo biti podčrtani. Primer:

>>> podcrtaj("Jaz _sem_ pa cajzelc!")
'Jaz sem pa cajzelc!\n    ---            '

Predpostavite, da v nizu s ni nobenega znaka '\n'.

Uradna rešitev

def podcrtaj(s):
    '''Vrne niz iz dveh vrstic, kjer so ustrezni znaki podčrtani'''
    prvaVrsta = ''
    drugaVrsta = ''
    podcrtujem = False  # ali so trenutni znaki podčrtani
    for znak in s:
        if znak == '_': # preklopimo način podčrtovanja
            podcrtujem = not podcrtujem
        else:
            prvaVrsta += znak # v prvi vrsti so vsi znaki razen podčrtajev
            if podcrtujem: # v drugi pa bodisi presledki, bodisi podčrtaji
                drugaVrsta += '-'
            else:
                drugaVrsta += ' '
    return prvaVrsta + '\n' + drugaVrsta

6. podnaloga

Sestavite funkcijo stevilo_znakov(s), ki v podanem nizu s prešteje število znakov, pri čemer se presledki ne upoštevajo. Zgled:

>>> stevilo_znakov('B     u!')
3

Uradna rešitev

def stevilo_znakov(s):
    '''Število znakov brez presledkov'''
    return len(s) - s.count(' ')

7. podnaloga

Sonet je priljubljena pesniška oblika. Sestavljen je iz štirih kitic, pri čemur med vsakima dvema kiticama avtor izpusti eno prazno vrstico. Prvi dve kitici sta štirivrstični — kvartini, drugi dve pa sta trivrstični — tercini.

V slovenskem sonetu je standardni verz italijanski (laški) ali jambski enajsterec. To pomeni, da v vsaki vrstici nastopa natanko enajst zlogov.

Na kulturnem natečaju TomoSonet udeleženci oddajajo svoje izdelke na strežnik Tomo. Napiši kontrolno funkcijo sonet(s), ki sprejme niz s, ter vrne True, če niz ustreza slovenskemu sonetu, in False sicer. Zgled:

>>> sonet('Bolj slab\nsonet.\n\nZa umret!')
False

Namig: V slovenskem jeziku število samoglasnikov v neki besedi ustreza številu njenih zlogov. (Obstaja nekaj izjem, ki pa jih bomo zanemarili.)

Uradna rešitev

def sonet(s):
    '''Ali je v s zapisan sonet'''
    v = vrstice(s) # seznam vrstic - s pomočjo funkcije od prej!
    if len(v) != 17:
        return False  # Sonet mora imeti 14 + 3 (prazne) = 17 vrstic.
    for i in range(17): # indeksi vseh vrstic
        if i in [4, 9, 13]:
            if v[i] != '':
                return False  # Med kiticami morajo biti prazne vrstice.
        else:
            if samoglasniki(v[i]) != 11:
                return False  # Verz ni jambski enajsterec.
    return True

Lepšanje in šifriranje

1. podnaloga

Klodovik /papiga/ in ne Klodvik, frankofonski kralj/ bi rad zašifriral svoja besedila, da jih nepoklicane osebe ne bodo mogle prebrati. To stori tako, da najprej v besedilu vse male črke spremeni v velike in odstrani vse znaki, ki niso črke. (Klodvik vsa pomembna besedila piše v angleščini. Uporabljali bomo angleško abecedo.) Na primer iz besedila 'Attack at dawn!' dobi besedilo 'ATTACKATDAWN'. Nato ga zapiše cik-cak v treh vrsticah, kot prikazuje primer:

A...C...D...
.T.A.K.T.A.N
..T...A...W.

Sestavite funkcijo cik_cak(s), ki vrne trojico nizov (torej tuple) in sicer prvo, drugo in tretjo vrstico v tem zapisu. Primer:

>>> cik_cak('Attack at dawn!')
('A...C...D...', '.T.A.K.T.A.N', '..T...A...W.')

Uradna rešitev

def cik_cak(niz):
    '''Niz spremenimo v trtvrstični zapis CikCak'''
    perioda = [0, 1, 2, 1] # kako se izmenjujejo nizi, kamor
                           # zapišemo znak: prva, druga, tretja, druga, prva, druga, tretja, druga ...
    niz = niz.upper()
    vrste = ['', '', '']  # vrstice bodo na začetku seznami, da jih lahko spreminjamo!
    stevec = 0 # kateri znak jemljemo 
    for znak in niz:
        if not 'A' <= znak <= 'Z': # če ne gre za znak angleške abecede
            continue
        pos = perioda[stevec % 4] # kam bomo napisali znak
        vrste[pos] += znak
        vrste[(pos+1)%3] += '.'
        vrste[(pos+2)%3] += '.'
        stevec += 1
    return tuple(vrste) # vrniti moramo trojico!

2. podnaloga

Zašifrirano besedilo dobi tako, da najprej prepiše vse znake iz prve vrstice, nato vse znake iz druge vrstice in na koncu še vse znake iz tretje vrstice. V zgornjem primeru bi tako dobil 'ACDTAKTANTAW'. Sestavite funkcijo cik_cak_sifra(s), ki dobi kot argument niz s in vrne zašifrirano besedilo. Primer:

>>> cik_cak_sifra('Attack at dawn!')
'ACDTAKTANTAW'

Uradna rešitev

def cik_cak_sifra(s):
    '''Zašifrirajmo besedilo'''
    prva, druga, tretja = cik_cak(s) # najprej zapišemo cik-cak
    sifra = ''
    for znak in prva + druga + tretja: # gremo preko vseh treh vrstic
        if znak != '.': # spustimo pike
            sifra += znak
    return sifra

3. podnaloga

Klodovik se zelo razjezi, ko dobi elektronsko pošto v takšni obliki:

Kar sva  si obljubljala    že leta,  si   želiva potrditi tudi   pred prijatelji in   celo
žlahto. Vabiva te na

     poročno slovesnost,        ki bo
   10.   maja 2016 ob    15.    uri na gradu Otočec.   Prijetno   druženje bomo 
nadaljevali v    hotelu   Mons.   Tjaša in  Pavle

Nepopisno mu gre na živce, da je med besedami po več presledkov. Še bolj pa ga nervira, ker so nekatere vrstice precej daljše od drugih. Ker je Klodvik vaš dober prijatelj, mu boste pomagali in napisali funkcije, s katerimi bo lahko olepšal besedila.

Najprej napišite funkcijo razrez(s), ki kot argument dobi niz s in vrne seznam besed v tem nizu. Besede so med seboj ločene z enim ali večimi praznimi znaki: ' ' (presledek), '\t' (tabulator) in '\n' (skok v novo vrstico). Pri tej nalogi ločilo obravnavamo kot del besede. Primer:

>>> razrez('   Kakšen\t pastir, \n\ntakšna  čreda. ')
['Kakšen', 'pastir,', 'takšna', 'čreda.']

Uradna rešitev

def razrez(s):
    '''Niz s razreže na podnize, ki jih ločijo "beli" presledki'''
    seznam = []
    beseda = '' # trenutna beseda, ki jo sestavljamo
    for znak in s:
        if znak in ' \n\t':  # znak ni del besede
            if len(beseda) > 0: # če je ta znak zaključil besedo, jo dodamo v seznam
                seznam.append(beseda)
            beseda = '' # in nato bomo začeli sestavljati novo
        else:
            beseda += znak # smo "znotraj" besede
    if len(beseda) > 0: # ne pozabimo na morebitno besedo na koncu!
        seznam.append(beseda)
    return seznam

4. podnaloga

Sedaj, ko že imate funkcijo razrez(s), bo lažje napisati tisto funckijo, ki jo Klodovik zares potrebuje. To je funkcija olepsanoBesedilo(s, sir), ki kot argumenta dobi niz s in naravno število sir. Funkcija vrne olepšano besedilo, kar pomeni naslednje:

  • Funkcija naj odstrani odvečne prazne znake.
  • Vsaka vrstica naj bo kar se le da dolga.
  • Nobena vrstica naj ne vsebuje več kot sir znakov (pri čemer znaka '\n' na koncu vrstice ne štejemo).
  • Besede znotraj iste vrstice naj bodo ločene s po enim presledkom (ne glede na to s katerimi in koliko praznimi znaki so ločene v originalnem besedilu).

Predpostavite, da dolžina nobene besede ni več kot sir in da je niz s neprazen. Primer:

>>> s2 = olepsanoBesedilo('  Jasno in   svetlo \t\tna sveti \t\n\nvečer,  dobre\t\t letine je dost, če pa je\t  oblačno in   temno,        žita ne bo.', 20)
>>> print(s2)
Jasno in svetlo na
sveti večer, dobre
letine je dost, če
pa je oblačno in
temno, žita ne bo.

Uradna rešitev

def olepsanoBesedilo(s, sir):
    '''olepša besedilo do največje širine sir'''
    besedilo = '' #končno besedilo
    besede = razrez(s) # razrežemo na posamezne besede
    vrstica = ''
    for b in besede:
        if len(vrstica) + len(b) > sir: # če je beseda, ki je na vrsti, predolga
            besedilo += vrstica[:-1] + '\n' # odrežemo presledek, ki smo ga dodali za stikanje
            vrstica = '' # začnemo novo vrstico
        vrstica += b + ' '
    if len(vrstica) > 0: # ne pozabimo na zadnjo vrstico!
        besedilo += vrstica[:-1] + '\n'
    return besedilo[:-1] # na koncu je odvečni prehod v novo vrsto

Malica

Srednje šole po Sloveniji si prizadevajo, da bi njihovi dijaki jedli čim bolj zdravo malico. Ravnatelj ene od srednjih šol je vaš dobri prijatelj. Rad bi, da mu napišete program, s katerim bo lahko analiziral jedilnike, saj si želi, da bi bili njegovi dijaki zdravi in srečni.

   primer_jedilnika = [
       'svinjski zrezek v omaki', 'sirov kanelon', 'ocvrt oslič',
       'svinjski zrezek v omaki', 'ocvrt oslič', 'sirov burek',
       'sirov kanelon', 'ocvrt oslič', 'sirov kanelon', 'sirov kanelon'

1. podnaloga

Jedilnik opišemo s seznamom nizov, kot vidite na primeru. Vsak zaporedni element seznama ustreza enemu od zaporednih dni. Jedilnik se periodično ponavlja. Če je dolžina jedilnika $n$, bodo dijaki $(n+1)$-ti dan spet jedli tisto, kar je na prvem mestu na jedilniku. Ravnatelj je opazil, da so nekatere stvari na jedilniku napisane po večkrat. Rad bi, da napišete funkcijo brez_ponovitev(l), ki sestavi nov seznam, tako da se bo vsak element pojavil samo enkrat. Zgled:

>>> brez_ponovitev(primer_jedilnika)
['svinjski zrezek v omaki', 'sirov kanelon', 'ocvrt oslič', 'sirov burek']

Elementi naj se v novem seznamu pojavijo v takem vrstnem redu, kot njihove prve ponovitve v originalnem seznamu l.

Uradna rešitev

def brez_ponovitev(jedilnik):
    '''sestavi jedilnik brez ponovitev'''
    novJedilnik = []
    for jed in jedilnik:
        if jed not in novJedilnik: # če jed še ni v novem jedilniku
            novJedilnik.append(jed)
    return novJedilnik

2. podnaloga

Ravnatelj je sicer ugotovil, da se jedi ponavljajo. Če pa je od dneva, ko je bila neka jed nazadnje na jedilniku, minilo dovolj časa, ni s tem nič narobe. Napišite funkcijo kdaj_prej(l), ki dobi nek jedilnik in vrne enako dolg seznam, kjer je za vsak dan ena številka in sicer koliko dni je minilo od takrat, ko je bila jed nazadnje na jedilniku. Upoštevajte, da je jedilnik periodičen, torej jedilnik ['burek', 'jogurt', 'burek'] je v 5 dneh ['burek', 'jogurt', 'burek', 'burek', 'jogurt'] v 10 pa ['burek', 'jogurt', 'burek', 'burek', 'jogurt', 'burek', 'burek', 'jogurt', 'burek', 'burek'] Primer:

>>> kdaj_prej(['burek', 'jogurt', 'burek'])
[1, 3, 2]
>>> kdaj_prej(primer_jedilnika)
[7, 2, 5, 3, 2, 10, 5, 3, 2, 1]

Uradna rešitev

def kdaj_prej(jedilnik):
    '''Koliko dni je minilo pred enako jednjo'''
    ustrezniIndeksi = []
    n = len(jedilnik)
    for dan in range(n):
        j = dan - 1 # dan prej 
        jedTaDan = jedilnik[dan]
        while jedilnik[j%n] != jedTaDan: #kdaj je bila ta jed prej
            j -= 1 
        ustrezniIndeksi.append(dan - j)
    return ustrezniIndeksi

3. podnaloga

Ravnatelj je definiral pojma raznolikost in kakovost. (Opomba: Ravnatelj je študiral matematiko in jo svoje čase tudi poučeval.) Raznolikost je število različnih jedi na jedilniku. Kakovost pa je povprečje kvadratov vrednosti elementov seznama, ki ga vrne funkcija kdaj_prej(l). Sestavite funkcijo raznolikost_in_kakovost(l), ki vrne par števil in sicer raznolikost in kakovost jedilnika l. Primer:

>>> raznolikost_in_kakovost(['burek', 'burek', 'jogurt'])
(2, 4.666666666666667)
>>> raznolikost_in_kakovost(primer_jedilnika)
(4, 23.0)

Uradna rešitev

def raznolikost_in_kakovost(jedilnik):
    '''Vrne raznolikost in kakovost jedilnika'''
    jedilnikBP = brez_ponovitev(jedilnik) # število različnih jedi bo raznolikost!
    kdajPrej = kdaj_prej(jedilnik) # koliko nazaj je bila posamezna jed
    vsotaKv = 0
    for kolikoNazaj in kdajPrej:
        vsotaKv += kolikoNazaj**2
    povp = vsotaKv / len(kdajPrej)
    return len(jedilnikBP), povp

4. podnaloga

Ravnatelj je v zbornici zbral različne predloge jedilnikov in jih združil v en sam seznam. Sestavite funkcijo naj_jedilnik(ll), ki izmed vseh teh jedilnikov v seznamu ll izbere in vrne najboljšega. Bolši je tisti jedilnik, ki je bolj raznolik. Med jedilniki z enako raznolikostjo je boljši tisti, ki je kakovostnejši. Predpostavite, da imate vsaj en jedilnik in da v podatkih ne bo dveh enako dobrih jedilnikov. Primer:

>>> naj_jedilnik([['burek', 'jogurt'], ['pomaranča', 'pomaranča']])
['burek', 'jogurt']

>>> naj_jedilnik([['burek', 'jogurt'], primer_jedilnika])
['svinjski zrezek v omaki', 'sirov kanelon', 'ocvrt oslič',
'svinjski zrezek v omaki', 'ocvrt oslič', 'sirov burek',
'sirov kanelon', 'ocvrt oslič', 'sirov kanelon', 'sirov kanelon']

Uradna rešitev

def naj_jedilnik(seznamJedilnikov):
    '''Iz seznama jedilnikov poiščemo najboljšega'''
    naj = seznamJedilnikov[0] # kandidat za najboljšega je prvi 
    for jedilnik in seznamJedilnikov[1:]: # pregledamo še ostale
        if raznolikost_in_kakovost(jedilnik) > raznolikost_in_kakovost(naj): # pari se primerjajo "leksikografsko"
            naj = jedilnik
    return naj

FizzBuzz

FizzBuzz je priljubljena otroška igrica, ki se jo lahko igrajo vsi otroci, ki znajo deliti cela števila. Otroci se usedejo v krog in štejejo (začenši z 1), pri čemer številke glasno izgovarjajo. Če je število deljivo s 3, potem morajo (namesto številke) zaklicati ‘Fizz!’. Če je število deljivo s 5, morajo zaklicati ‘Buzz!’. Če pa je število deljivo s 3 in 5 hkrati, morajo zaklicati ‘Fizzbuzz!’.

1. podnaloga

Sestavite funkcijo fizzbuzz(n), ki kot argument dobi naravno število n in vrne seznam števil od 1 do $n$, pri čemer naj:

  • večkratnike števila 3 zamenja z nizom 'Fizz',
  • večkratnike števila 5 zamenja z nizom 'Buzz',
  • večkratnike števila 15 pa zamenja z nizom 'FizzBuzz'.

Primer:

   >>> fizzbuzz(16)
   [1, 2, 'Fizz', 4, 'Buzz', 'Fizz', 7, 8, 'Fizz', 'Buzz', 11, 'Fizz', 13, 14, 'FizzBuzz', 16]

Uradna rešitev

def fb(n):
    '''Kaj naj pove, če je na vrsti n'''
    if n % 15 == 0: # večkratnik 15 moramo preveriti prvega!
        return 'FizzBuzz'
    if n % 3 == 0:
        return 'Fizz'
    if n % 5 == 0:
        return 'Buzz'
    return n

def fizzbuzz(n):
    '''Vrne seznam ustreznih besed za igro Fizz buzz, če števejo do n'''
    seznamBesed = []
    for i in range(1, n+1):
        seznamBesed.append(fb(i))
    return seznamBesed

2. podnaloga

Sestavite še funkcijo poisci_napake(l), ki kot argument prejme seznam l. Ta seznam je FizzBuzz zaporedje, ki lahko vsebuje napake. Funkcija naj vrne seznam vseh indeksov seznama l, kjer se pojavijo napake. Elementi izhodnega seznama naj bodo urejeni naraščajoče. Zgled:

>>> poisci_napake([1, 2, 3, 'Fizz', 'Buzz', 7, 'Fizz', 8])
[2, 3, 5, 6]

Uradna rešitev

def poisci_napake(l):
    '''Poiščemo indekse, kjer je v seznamu l, napaka za igro FizzBuzzz'''
    napake = []
    pravilno = fizzbuzz(len(l))
    for i in range(len(l)):
        if l[i] != pravilno[i]:
            napake.append(i)
    return napake

Datoteke - osnovne


Datoteke

1. podnaloga

Sestavite funkcijo prva_vrstica(ime_datoteke), ki vrne prvo vrstico datoteke z danim imenom.

Uradna rešitev

def prva_vrstica(ime_datoteke):
    '''Vrne prvo vrstico datoteke (brez znaka za prehod v novo vrsto)
       Če je datoteka prazna, kot rezultat vrnemo prazen niz
    '''
    dat = open(ime_datoteke)
    vrst = dat.readline()
    vrst = vrst.rstrip() # odstranimo znak za novo vrsto
    dat.close()
    return vrst

def prva_vrstica_V1(ime_datoteke):
    ''' Funkcija vrne prvo vrstico iz datoteke
        Če je datoteka prazna, kot rezulat vrnemo prazen niz
    '''
    dat = open(ime_datoteke, 'r')
    vrst = dat.readline()
    dat.close()
    return vrst[:-1] # odstranimo znak za novo vrsto

def prva_vrstica_V2(ime_datoteke):
    '''Vrne prvo vrstico datoteke (brez znaka za prehod v novio vrsto)
       Predpostavka: Datoteka ni prazna!
    '''
    # uporabimo nekaj drugačnih prijemov - razmisli o njih!
    with open(ime_datoteke) as f:
        return f.readlines()[0].rstrip()

2. podnaloga

Sestavite funkcijo stevilo_dolgih_vrstic(ime_datoteke, dolz), ki vrne število vseh vrstic v datoteki z danim imenom, ki so dolge vsaj dolz znakov (pri tem je dolz-ti lahko tudi znak "\n" za novo vrstico).

Uradna rešitev

def stevilo_dolgih_vrstic(ime_datoteke, dolz):
    '''vrne število vrstic v datoteki, ki so dolge vsaj dolz znakov
       Zraven štejemo tudi prehod v novoi vrsto'''
    kolikoJihJe = 0
    for vrs in open(ime_datoteke): # gremo po vseh vrsticah datoteke
        if len(vrs) >= dolz :
            kolikoJihJe += 1
    return kolikoJihJe

def stevilo_dolgih_vrstic_V1(ime_datoteke, dolz):
    '''vrne število vseh vrstic v datoteki,
       ki so dolge vsaj dolz znakov '''
    stVrstic = 0
    for vrstica in open(ime_datoteke, 'r'): # prečešemo vse vrstice v datoteki
        # ali so dovolj dolge
        if len(vrstica) >= dolz:
            stVrstic += 1
    return stVrstic

def stevilo_dolgih_vrstic_V2(ime_datoteke, dolz):
    '''vrne število vrstic v datoteki, ki so dolge vsaj dolz znakov
       Zraven štejemo tudi prehod v novo vrsto'''
    # uporabimo nekaj drugačnih prijemov - razmisli o njih!
    with open(ime_datoteke) as f:
        return len([vrs for vrs in f if len(vrs) >= dolz])

3. podnaloga

Sestavite funkcijo najdaljsa_vrstica(ime_datoteke), ki vrne najdaljšo vrstico v datoteki z danim imenom. Če je takih vrstic več, vrni zadnjo!

Uradna rešitev

def najdaljsa_vrstica(ime_datoteke):
    '''  vrne najdaljšo vrstico v datoteki z danim imenom. '''
    najdVrst = ''
    najDolz = 0
    for vrst in open(ime_datoteke):
        dolTrenutne = len(vrst)
        if dolTrenutne >= najDolz: #boljši kandidat! = ker hočemo zadnjo!
            najdVrst = vrst
            najDolz = dolTrenutne
    return najdVrst.rstrip()

def najdaljsa_vrstica_V2(ime_datoteke):
    '''  vrne najdaljšo vrstico v datoteki z danim imenom. '''
    # uporabimo nekaj drugačnih prijemov - razmisli o njih!
    with open(ime_datoteke) as f:
        _, max_vrs = max((len(vrs), vrs) for vrs in f)
        return max_vrs.rstrip()

Delo z vrsticami

1. podnaloga

Sestavi funkcijo prestejVrstice(datoteka), ki prešteje, koliko vrstic je na dani znakovni datoteki. Funkcija naj za parameter dobi ime datoteke.

Uradna rešitev

def prestejVrstice(datoteka):
    ''' koliko vrstic je na dani znakovni datoteki
        prepostavimo, da je datoteka dovolj kratka,
        da lahko vse njene vrstice shranimo v seznam '''
    f = open(datoteka,'r')
    stVrstic = len(f.readlines())
    f.close()
    return stVrstic

def prestejVrstice_V1(datoteka):
    ''' koliko vrstic je na dani znakovni datoteki'''
    # ta rešitev deluje, tudi, če je datoteka zelooooooo dolga
    stVrstic = 0
    for vrst in open(datoteka) : # preko vseh vrstic
        stVrstic += 1
    return stVrstic

2. podnaloga

Sestavi funkcijo vrniKVrstico(datoteka,k), ki vrne k-to vrstico dane znakovne datoteke. Vrednost k in ime datoteke naj funkcija dobi za parameter. V primeru ko datoteka ne vsebuje dovolj vrstic, naj vrne prazen niz.

Uradna rešitev

def vrniKVrstico(datoteka,k):
    ''' vrne k-to vrstico dane datoteke '''
    kolikoŠe = k # koliko vrstic je še potrebno prebrati
    f = open(datoteka,'r')
    while kolikoŠe > 0:
        vrstica = f.readline()
        if vrstica == '': # smo že čez konec datoteke, vrstic je premalo
            f.close() 
            return ''
        kolikoŠe -= 1 # ena manj za prebrati       
    f.close()
    return vrstica


def vrniKVrsticoV2(datoteka,k):
    ''' vrne k-to vrstico dane datoteke '''
    # če imamo dovolj prostora
    f = open(datoteka,'r')
    vrstice = f.readlines()
    f.close()
    if len(vrstice) >= k: # imamo dovolj vrstic
        return vrstice[k - 1]
    return ""

3. podnaloga

Na datoteki je pesem zapisana po kiticah. To pomeni, da so med kiticami prazne vrstice. Sestavi funkcijo odstraniPrazneVrstice(datotekaV, datotekaI), ki bo za dano ime datoteke datotekaV vse kitice združila in jih izpisala na datoteko2.

datotekaV:

Dekle je po vodo šlo
na visoke planine.

Vodo je zajemala,
je ribico zajela.

Ribica jo je prosila:
oj, pusti me živeti.

Dekle b'la je usmiljena,
je ribico spustila.

Ribica je zaplavala,
je dekle poškropila.
DatotekaI:
Dekle je po vodo šlo
na visoke planine.
Vodo je zajemala,
je ribico zajela.
Ribica jo je prosila:
oj, pusti me živeti.
Dekle b'la je usmiljena,
je ribico spustila.
Ribica je zaplavala,
je dekle poškropila.

Uradna rešitev

def odstraniPrazneVrstice(datotekaV, datotekaI):
    '''Izpiše datoteko brez praznih vrstic'''
    f = open(datotekaV,'r')
    izpis = open(datotekaI, 'w')
    while True:
        trenutnaVrstica = f.readline()
        if trenutnaVrstica not in ['\n', '\r\n']: # različni načini (glede na OS) oblike prazne vrstice
            print (trenutnaVrstica,end='',file=izpis)
        if trenutnaVrstica == '': # smo že čez konec datoteke
            f.close()
            izpis.close()
            return 

def odstraniPrazneVrstice_V1(datotekaV, datotekaI):
    '''Izpiše datoteko brez praznih vrstic'''
    izpis = open(datotekaI, 'w')
    for vrstica in open(datoteka):
        if vrstica != '\n' : # ni prazna
            print(vrstica, end='', file=izpis)
    izpis.close()

4. podnaloga

Direktor podjetja je dobil datoteko priporocilo.txt na kateri je pisalo: Spoštovani gospod direktor,

Janeza Novaka, mojega asistenta pri delu, vedno vidite, kako
trdo dela v svoji mali pisarni. Janez dela neodvisno in ne
lenari ali se pogovarja s sodelovci. Nikoli se ne zgodi, da bi
zavrnil kakšnega sodelovca, ki potrebuje pomoc. Do sedaj je vedno
koncal z delom pravocasno. Zelo pogosto si vzeme podaljšan
delovni cas, da konca svoje delo, pri cemer vcasih preskoci
odmor. Janez je takšen delavec, ko nima absolutno nobenega
spodrslajaja pri opravljenih delih, ima visoke dosežke in je širokega
znanja na njegovem podrocju. Moje mnenje je, da ga lahko takoj
uvrstimo med tiste najbolj vzorne delovce, ki jih nikoli ne
odpustimo. Prav tako vam vljudno predlagam, da je moj predlog
o napredovanju tega izjemnega, vzornega in nepogrešljivega delavca
izvršen kakor hitro je mogoce.

Lep pozdrav!

Že se je spravil pisati predlog za napredovanje, ko je po e-pošti prispel dopis:

Direktor!
Ta idiot je stal za menoj, ko sem pisal prejšnje priporocilo.
Prosim znova preberite vsako drugo vrstico tega pisma.

Direktor je sedaj povsem zmeden. Pomagaj mu in sestavi funkcijo izpisiDrugo(datotekaV, datotekaI), ki na datotekoI izpiše vsako drugo vrstico vsebine datoteke datotekaV!

Uradna rešitev

def izpisiDrugo(datotekaV, datotekaI):
    '''izpiše vsako drugo vrstico datoteke '''
    f = open(datotekaV)
    izp = open(datotekaI, 'w')
    vrstica = " " # zelimo zaceti pri prvi vrstici
    while vrstica != '' : # do konca datoteke
        vrstica = f.readline()
        # ta je liha, zato jo izpišemo
        print(vrstica, end= '', file = izp) # ker že vsebuje \n
        # naslednjo preskočimo
        vrstica = f.readline()
    f.close()
    izp.close()

def izpisiDrugoV1(datotekaV, datotekaI):
    '''izpiše vsako drugo vrstico datoteke '''
    stVrstice = 1
    izp = open(datotekaI, 'w')
    for vrstica in open(datoteka):
        if stVrstice % 2 == 1 : # vsako drugo preskočimo, začnemo s prvo
            print(vrstica, end= '', file = izp) # ker že vsebuje \n
        stVrstice += 1
    izp.close()
        
def izpisiDrugoV2(datoteka):
    ''' Če vemo, da je dovolj prostora, bomo uporabili kar readlines '''
    f = open(datoteka)
    vrstice = f.readlines()
    izp = open(datotekaI, 'w')
    # iz seznama vzamemo vsako drugo
    vrstice = vrstice[::2]
    for vrstica in vrstice: # Sedaj pa jih le še izpišemo
        print(vrstica, end= '', file = izp) # ker že vsebuje \n

def izpisiDrugoV3(datoteka):
    ''' In še malo bolj kompaktno '''
    for vrstica in open(datoteka).readlines()[0:-1:2]:
        print(vrstica, end= '', file = izp) # ker že vsebuje \n
    izp.close()

Arheologi

1. podnaloga

Arheologi: Arheologi so v jami blizu Gize našli čuden papirus. Besedilo na njem je bilo videti zelo čudno, a po drugi strani zelo podobno našemu. Dolgo so poskušali vse, da bi ga razvozlali. Na koncu se je oglasil 6 letni Mihec, sin glavnega arheologa: "Oci, zakaj si pa na ta papir pisal v napačno smer?" In res. Šlo je za povsem običajni tekst, le besedilo je bilo zapisano od desne proti levi. Ker pa je tako besedilo malček zoprno brati, pomagaj arheologom in sestavi funkcijo arheologi(datoteka), ki za dano ime datoteke sestavi novo datoteko z ravno obrnjenim imenom in enako koncnico (iz bla.txt torej naredi alb.txt, iz mojaDat.py pa taDajom.py). Ta nova datoteka naj vsebuje ravno obrnjene vrstice prvotne datoteke.

Uradna rešitev

import os.path

def arheologi(datoteka):
    '''sestavimo novo datoteko z ravno obrnjenim imenom in enako koncnico
       Ta nova datoteka vsebuje ravno obrnjene vrstice prvotne datoteke.'''
    #locimo koncnico datotek od imena
    ime, koncnica = os.path.splitext(datoteka)
    #ime datoteke obrnemo
    imeDat = ime[::-1]
    #ustvarimo novo datoteko z obrnjenim imenom
    novaDat = open(imeDat + koncnica,"w")
    for vrstica in open(datoteka,"r"):
        obVrstica = vrstica.rstrip()[::-1] # zraven se še znebimo zadnjega znaka 
        print(obVrstica, file = novaDat)
    novaDat.close()

Datoteke I


Kalorije

Tina za vsak obrok, ki ga poje, zapiše njegovo kalorično vrednost (celo število). Vse te podatke hrani v datoteki: podatke vsakega dne zapiše v svojo vrstico, znotraj vrstice pa jih loči z vejico.

1. podnaloga

Sestavite funkcijo kalorijeNaDan(datoteka), ki kot parameter dobi ime vhodne datoteke in vrne seznam kalorij, ki jih je Tina dnevno zaužila.

Torej, za vsako vrstico v datoteki (ki predstavlja en dan) dodaj v seznam eno število, ki naj bo enako vsoti kalorij za tisti dan.

Uradna rešitev

def skupneKalorije(niz):
    '''sešteje cela števila v nizu, ki so ločena z vejico'''
    tabelaNizov = niz.split(',')
    skupnaVsota = 0
    for elt in tabelaNizov:
        skupnaVsota += int(elt) # ne pozabi pretvoriti niza v število!
    return skupnaVsota

def kalorijeNaDan(datoteka):
    '''Seznam kalorij, zaužitih na posamezni dan'''
    zaužiteKalorije = []
    for vrstica in open(datoteka):
        # uporabimo zgornjo funkcijo, ki "obdela" vrstico in seštejemo
        naTaDan = skupneKalorije(vrstica)
        zaužiteKalorije.append(naTaDan)
    return zaužiteKalorije

2. podnaloga

Sestavite funkcijo vsotaKalorij(vhod, izhod), ki kot argumenta dobi imeni dveh datotek: vhodno (ta vsebuje Tinine zapiske) in izhodno. Na izhodno datoteko naj za vsako vrstico v vhodni datoteki zapiše vsoto kaloričnih vrednosti zaužite hrane tistega dne. Vsako število v izhodni datoteki naj bo v svoji vrstici.

Uradna rešitev

def vsotaKalorij(vhod, izhod):
    '''za vsako vrstico v vhodni datoteki zapiše vsoto na izhodno datoteko'''
    piši = open(izhod, 'w')
    for vrstica in open(vhod):
        vsota = skupneKalorije(vrstica) # izračuna vsoto števil v vrstici
        piši.write(str(vsota) + '\n')
    piši.close()

3. podnaloga

Sestavite funkcijo povprecjeKalorij(vhod, izhod), ki kot argumenta dobi imeni dveh datotek: vhodno in izhodno. Na izhodno datoteko naj za vsako vrstico na vhodni datoteki zapiše zaporedno številko vrstice (vrstice se začno šteti z ena) ter povprečno kalorično vrednost obrokov, ki jih je Tina zaužila tisti dan, na dve decimalni mesti natančno. V zadnjo (dodatno) vrstico pa naj funkcija zapiše dnevno povprečje zaužitih kalorij (prav tako na dve decimalni mesti natančno).

Uradna rešitev

def vrniKalorije(niz):
    '''iz niza, v katerem so števila ločena z vejicami, vrne
       seznam teh celih števil.'''
    tabelaNizov = niz.split(',')
    izhTabela = []
    for elt in tabelaNizov:
        izhTabela.append(int(elt)) # dodamo število, ki ga dobimo iz niza
    return izhTabela
    # lahko bi vse skupaj napisali tudi v eni vrstici
    # return list(map(int, niz.split(',')))

def povprecjeKalorij(vhod,izhod):
    vsotaKalorij = 0
    steviloDni = 0
    piši = open(izhod, "w")
    for vrstica in open(vhod):
        obroki = vrniKalorije(vrstica)
        dnevniVnos = sum(obroki)
        steviloDni += 1
        vsotaKalorij += dnevniVnos
        print("{0} {1:.2f}".format(steviloDni, dnevniVnos / len(obroki)), file=piši)
    # še končni podatek o povprečju
    print("{0:.2f}".format(vsotaKalorij / steviloDni), file=piši)
    piši.close()

Delnice

1. podnaloga

V datoteki imamo zapisane podatke o vrednosti neke delnice. V vsaki vrstici je zapisan podatek v obliki

YYYY-MM-DD,vrednost

kjer je prvi podatek dan, drugi pa vrednost delnice na ta dan. Sestavite funkcijo preberi(ime_datoteke), ki kot parameter sprejme ime datoteke, vrne par nabor dveh seznamov, v prvem naj bodo datumi (kot nizi), v drugem pa vrednosti delnice (kot realna števila).

Uradna rešitev

def preberi(ime_datoteke):
    '''vrne par (nabor) dveh seznamov, v prvem so datumi
       (kot nizi), v drugem pa vrednosti delnice (kot realna števila).
    '''
    datumi = []
    vrednosti = []
    for vrstica in open(ime_datoteke,'r'):
        v = vrstica.strip().split(',') # prvi podatek je datum, drugi vrednost
        datumi.append(v[0]) # datum dodamo nespremenjen
        vrednosti.append(float(v[1]))
    return (datumi, vrednosti)

2. podnaloga

Logaritemski povratek delnice je definiran kot logaritem kvocienta vrednosti delnice za dva zaporedna dneva trgovanja. Zakaj je to uporabno Sestavite funkcijo povratek(imeDat), ki kot parameter sprejme ime datoteke z vrednostmi delnice in vrne seznam logaritemskih povratkov. Če je podana datoteka prazna ali pa vsebuje le en podatek, naj funkcija vrne prazen seznam.

Uradna rešitev

from math import log

def povratek(ime_datoteke):
    '''Vrne seznam logaritemskih popravkov'''
    datumi, vrednosti = preberi(ime_datoteke)
    povratki = []
    for i in range(1,len(vrednosti)): #začnemo pri drugem dnevu
        povratki.append(log(vrednosti[i]/vrednosti[i-1]))
    return povratki

3. podnaloga

Iz logaritemskih povratkov lahko razberemo, ali je vrednost delnice naraščala ali padala. Sestavite funkcijo trend(povratki), ki sprejme seznam logaritemskih povratkov in vrne niz pozitiven trend, če je v seznamu več pozitivnih vrednosti kot negativnih, sicer pa naj vrne negativen trend. Vrednosti 0 štejte k negativnim.

Uradna rešitev

def trend(povratki):
    '''Vrne niz "pozitiven trend", če je v seznamu logaritemskih povratkov več pozitivnih vrednosti kot negativnih,
       sicer pa "negativen trend"'''
    poz = 0
    neg = 0
    for x in povratki:
        if x > 0:
            poz += 1
        else:
            neg += 1
    if poz > neg:
        return 'pozitiven trend'
    else:
        return 'negativen trend'

4. podnaloga

Letna volatilnost delnice (Volatilnost ali nihajnost označuje, koliko je statistično verjetno, da cena delnice v kratkem času močneje zraste ali pade) je definirana kot večkratnik standardnega odklona logaritemskega povratka: $$\sigma^2 = \frac{252}{n}\sum_{i=1}^n (x_i-\mu)^2,$$ kjer je $\mu$ povprečna vrednost logaritemskih povratkov, $x_i$ so posamezni logaritemski povratki, $n$ je število logaritemskih povratkov, 252 pa predstavlja število trgovalnih dni v letu. Sestavite funkcijo volatilnost(ime_datoteke), ki iz datoteke prebere vrednosti delnice in vrne njeno letno volatilnost $\sigma$.

Uradna rešitev

from math import sqrt
def volatilnost(ime):
    ''' iz datoteke prebere vrednosti delnice in vrne njeno letno volatilnost'''
    p = povratek(ime)
    n = len(p)
    mu = sum(p)/n # povprečna vrednost logaritemskih povratkov
    s2 = 0 
    for x in p:
        s2 += (x-mu)**2
    return sqrt(252 * s2/n)

Datoteke s HTML

1. podnaloga

Sestavite funkcijo html2txt(vhod, izhod), ki bo vsebino datoteke z imenom vhod prepisala v datoteko z imenom izhod, pri tem pa odstranila vse značke. Vemo, da je datoteka HTML sestavljena prav!

Značke se začnejo z znakom < in končajo z znakom >. Pozor: Začetek in konec značke nista nujno v isti vrstici. Takrat se vrstica nadaljuje! Prav tako ima lahko značka lastnosti, npr. značka a ima lastnost href

<a href = "kk.htm">

Na primer, če je v datoteki vreme.html zapisano:

<h1>Napoved vremena</h1>
<p>Jutri bo <i><b>lepo</b></i> vreme.
Več o vremenu preberite <a
href="napoved.html">tukaj</a>.</p>

bo po klicu html2txt('vreme.html', 'vreme.txt') v datoteki vreme.txt zapisano (pozor na tretjo vrstico!):

Napoved vremena
Jutri bo lepo vreme.
Več o vremenu preberite tukaj.

Uradna rešitev

def html2txt(vhod, izhod):
    ''' Funkcija vsebino datoteke z imenom vhod
    prepiše v datoteko z imenom izhod, pri tem pa
    odstrani vse značke za HTML. '''
    html = open(vhod)
    txt = open(izhod, 'w')
    znotrajZnačke = False # ali se nahajamo v notranjosti
    for vrstica in html:
        izhodnaVrstica = ''
        for znak in vrstica:
            if znak in '<>': # če smo naleteli na začetek ali na konec značke
                znotrajZnačke = not znotrajZnačke
            else: # gre za nek drug znak
                if not znotrajZnačke : # če nismo v znački, znak dodamo
                    izhodnaVrstica += znak
        txt.write(izhodnaVrstica)
    html.close()
    txt.close()

2. podnaloga

Sestavite funkcijo tabela(vhod, izhod), ki bo podatke iz vhodne datoteke zapisala v obliki HTML tabele v izhodno datoteko.

V vhodni datoteki so podatki shranjeni po vrsticah ter ločeni z vejicami. Na primer, če je v datoteki tabela.txt zapisano:

ena,dva,tri
17,52,49.4,6
abc,xyz

bo po klicu tabela('tabela.txt', 'tabela.html') v datoteki tabela.html:

<table>
  <tr>
    <td>ena</td>
    <td>dva</td>
    <td>tri</td>
  </tr>
  <tr>
    <td>17</td>
    <td>52</td>
    <td>49.4</td>
    <td>6</td>
  </tr>
  <tr>
    <td>abc</td>
    <td>xyz</td>
  </tr>
</table>

Pozor: Pazi na zamik (število presledkov na začetku vrstic) v izhodni datoteki.

Uradna rešitev

def tabela(ime_vhodne, ime_izhodne):
    ''' zapis podatkov iz CSV datoteko v datoteko HTML v obliki tabele'''

    # naučimo se uporabljati stavek with! Tu ni uporabe metode close!
    with open(ime_vhodne) as vhodna:
        with open(ime_izhodne, 'w') as izhodna:
            print('<table>', file=izhodna) # začetek tabele
            for vrstica in vhodna:
                print('  <tr>', file=izhodna) # vska vrstica se začne z glavo
                vrstica = vrstica.strip() # znebimo se uvodnih in končnih 'belih' znakov
                podatki = vrstica.strip().split(',') #ločilni znak med podatki je vejica
                for podatek in podatki: # posamezni stolpci
                    print('    <td>{0}</td>'.format(podatek), file=izhodna)
                print('  </tr>', file=izhodna) # konec vrstice
            print('</table>', file=izhodna) # konec tabele

3. podnaloga

Sestavite funkcijo seznami(vhod, izhod), ki bo podatke iz vhodne datoteke zapisala v izhodno datoteko v obliki neurejenega seznama. V vhodni datoteki se vrstice seznamov začnejo z zvezdico.

Na primer, če je v datoteki seznami.txt zapisano:

V trgovini moram kupiti:
* jajca,
* kruh,
* moko.
Na poti nazaj moram:
* obiskati sosedo.

bo po klicu seznami('seznami.txt', 'seznami.html') v datoteki seznami.html:

V trgovini moram kupiti:
<ul>
  <li>jajca,</li>
  <li>kruh,</li>
  <li>moko.</li>
</ul>
Na poti nazaj moram:
<ul>
  <li>obiskati sosedo.</li>
</ul>

Uradna rešitev

def seznami(ime_vhodne, ime_izhodne):
    '''Seznam na tekstovni datoteki prepišimo kot naštevni seznam v HTML'''
    seznam = False # ali smo znotraj obstoječega seznama 
    vhodna = open(ime_vhodne)
    izhodna = open(ime_izhodne, 'w')
    for vrstica in vhodna:
        if vrstica[0] == '*': # z * so označeni elementi seznama 
            if not seznam: # gre za nov seznam, zato ga ustvarimo!
                print('<ul>', file=izhodna)
                seznam = True
            print('  <li>{0}</li>'.format(vrstica[2:-1]), file=izhodna) # znebimo se uvodne * s presledkom, in \n s konca
        else: #'navadna vrstica' 
            if seznam: # je potrebno zaključiti seznam?
                print('</ul>', file=izhodna)
                seznam = False
            print(vrstica, file=izhodna, end='')
    if seznam: print('</ul>', file=izhodna) # če je zadnji seznam šel do konca datoteke!
    vhodna.close()
    izhodna.close()

4. podnaloga

Sestavite funkcijo gnezdeni_seznami(vhod, izhod), ki bo podatke iz vhodne datoteke zapisala v izhodno datoteko v obliki neurejenega gnezdenega seznama. V vhodni datoteki je vsak element seznama v svoji vrstici, zamik pred elementom pa določa, kako globoko je element gnezden.

Na primer, če je v datoteki seznami.txt zapisano:

živali
  sesalci
    slon
  ptiči
    sinička
rastline
  sobne rastline
    difenbahija

bo po klicu gnezdeni_seznami('seznami.txt', 'seznami.html') v datoteki seznami.html zapisano:

<ul>
  <li>živali
    <ul>
      <li>sesalci
        <ul>
          <li>slon
        </ul>
      <li>ptiči
        <ul>
          <li>sinička
        </ul>
    </ul>
  <li>rastline
    <ul>
      <li>sobne rastline
        <ul>
          <li>difenbahija
        </ul>
    </ul>
</ul>

Značk <li> ne zapirajte.

Uradna rešitev

def gnezdeni_seznami(ime_vhodne, ime_izhodne):
    '''iz "navadnega" gnezdenega seznama naredimo tak seznam v HTML'''
    nivo = 0
    zamik = 2
    # naučimo se uporabljati stavek with! Tu ni uporabe metode close!
    with open(ime_vhodne) as vhodna:
        with open(ime_izhodne, 'w') as izhodna:
            for vrstica in vhodna:
                # izračunaj na katerem nivoju sem
                # poiskati moramo prvi znak, ki ni presledek
                prviZnak = 0
                for i in range(len(vrstica)):
                    if vrstica[i] != ' ':
                        break # našli smo ga
                    prviZnak += 1 # ga še ni
                n = prviZnak // zamik + 1 # račun nivoja
                vrstica = vrstica.strip() #sedaj se lahko znebimo začetnih in končnih 'belih znakov'
                if n > nivo: # je potrebno začetni novo gnezdenje?
                    print(2 * zamik * nivo * ' ' + '<ul>', file=izhodna, sep="")
                    nivo += 1
                while n < nivo: # končamo gnezdenje
                    nivo -= 1
                    print(2 * zamik * nivo * ' ' + '</ul>', file=izhodna, sep="")
                print((2 * zamik * nivo - zamik) * ' ' + '<li>', vrstica, file=izhodna, sep="")
            while nivo > 0: # še končen zaključek gnezdenja
                nivo -= 1
                print(2 * zamik * nivo * ' ' + '</ul>', file=izhodna, sep="")

Golf

Na vsakem zanimivem igrišču za golf so ovire: jezero, pesek, ...

Jezero je krog, podan kot trojica (x,y,r). Pesek je pravokotnik, podan kot četverica(x1,y1,x2,y2). Predpostaviš lahko, da so to po vrsti koordinate levega spodnjega in desnega zgornjega oglišča. Vse koordinate računamo na tri decimalke (round(x, 3))

1. podnaloga

Na datoteki imamo zapisane podatke o posameznih udarcih v obliki polarnih koordinat. V vsaki vrstici sta zapisani celi števili r in f, ločeni s presledkom. Napišite metodo datotekaPolozajev(vhod, izhod), ki naj datoteko prebere in v tvori novo datoteko tako, da je v vsaki vrstici zapisan njen trenutni položaj (v obliki decimalnih števil, zaokroženih na 3 decimalna mesta) in ločenih s presledkom. V ta namen uporabite formatiranje s pomočjo "{0:.3f}".format(x) Začetni položaj naj bo v točki (0,0).

Uradna rešitev

from math import *

def datotekaPolozajev(vhod, izhod):
    '''Iz seznama udarcev, podanih v polarnih koordinatah, na datoteko zapiše
       kartezične koordinate položaja točk'''
    x,y = 0,0
    g = open(izhod, "w") 
    for vrstica in open(vhod):
        polarnekoordinate = vrstica.strip('\n').split(' ')
        r = int(polarnekoordinate[0])
        fi = int(polarnekoordinate[1])
        x += r * cos(fi * pi / 180)
        y += r * sin(fi * pi / 180)
        print("{0:.3f}".format(x), "{0:.3f}".format(y), file=g)
    g.close()

2. podnaloga

Podan je seznam položajev žogic po posameznem udarcu in seznam, katerega vsak element je nabor, ki podaja jezero ali pesek. Napišite metodo seIzogne(pot, ovire), ki pove, ali se pot v celoti izogne oviram. Pazi: jezero je podano z naborom treh, pesek pa z naborom štirih števil.

Najprej napišite metodi jeVJezeru(zogica, jezero) in jeVPesku(zogica, pesek), ki povesta, ali je žogica v jezeru ali v pesku. Žogica je podana kot par (x,y), torej "nima dimenzije".

Uradna rešitev

def jeVJezeru(zogica, jezero):
    '''Ali je zogica znotraj jezera '''
    (x0,y0) = zogica 
    (x,y,r) = jezero # okroglo jezero
    return round((x-x0)**2 + (y-y0)**2,3) <= round(r**2, 3)

def jeVPesku(zogica, pesek):
    '''Ali je zogica znotraj peska '''
    (x0,y0) = zogica
    (xmin,ymin,xmax,ymax) = pesek # prakokotna peščena ovira
    return (xmin <= x0 <= xmax) and (ymin <= y0 <= ymax)

def seIzogne(pot, ovire):
    '''Ali se žogice, katerih položaji so podani v seznamu pot
       izogne vsem oviram, podanih v seznamu ovire'''
    for tocka in pot:
        # za vsak položaj preverimo, če smo naleteli na kakšno oviro
        for ovira in ovire:
            if (len(ovira)==3 and jeVJezeru(tocka, ovira)): # žogica se znajde v jezeru
                return False
            if (len(ovira)==4 and jeVPesku(tocka, ovira)): # žogica se znajde v peščeni oviri
                return False
    return True

3. podnaloga

Napišite metodo kjeJeZogica(datoteka, zacetek, ovire), ki vrne vektor od začenega do končnega položaja ali None, če žogica kdaj vmes pade v oviro. Posamezni udarci so v polarnih koordinatah zapisani na datoteko, začetek pa je podan kot par (x,y).

Uradna rešitev

def kjeJeZogica(datoteka, zacetek, ovire):
    '''Glede na seznam udarcev, podanem na datoteki datoteka,
       seznama ovir (seznam naborov) in začetnega položaja žogice
       ugotovi vektor od začetnega do končnega položaja žogice.
       Če žogica pristane v oviri, vrni None'''
    x,y = zacetek
    pot = []
    pot.append((x,y))
    # sestavimo seznam položajev žogice
    for vrstica in open(datoteka):
        polarnekoordinate = vrstica.strip('\n').split(' ')
        r = int(polarnekoordinate[0])
        fi = int(polarnekoordinate[1])
        x += r * cos(fi * pi / 180)
        y += r * sin(fi * pi / 180)
        pot.append((round(x,3),round(y,3)))
    # na koncu je žogica v (x, y)
    # uporabimo funkcijo prejšnje naloge
    if not seIzogne(pot, ovire):
        return None
    else:
        return (round(x-zacetek[0], 3),round(y-zacetek[0],3))

Množice


Besede

besede, besede ... Kaj sploh je beseda? Osnovni delček komunikacije? Orožje? Zaporedje črk? Za nas bo beseda poljubno zaporedje znakov, torej niz!

1. podnaloga

Sestavi funkcijo podobna(beseda, sezBesed), ki kot argument sprejme besedo in kot rezultat vrne prvo med tistimi besedami iz sezBesed, v kateri se pojavi čim več črk, ki se pojavijo tudi v dani besedi. Teh črk je seveda lahko tudi 0 ;-) !Vsako ujemajočo se črko štejemo le enkrat, tudi če se v obeh besedah pojavi večkrat. Funkcija naj deluje tako, da ne razlikuje med malimi in velikimi črkami. Če take sploh besede ni, vrni None.

  >>> besede = ["ana", "berta", "cilka", "dani", "ema", "fanči", "greta", "hilda"]
  >>> podobna("merjasec", besede)
  'berta'
  >>> podobna("zmaj", besede)
  'ema'
  >>> podobna("Krava", besede)
  'berta'

Ana in krava se ujemata v eni črki, namreč črki a in ne v dveh, pa čeprav imata po dva a-ja (tudi če bi napisali ana). Opomba: vsaka podobnost med Berto in merjascem je zgolj naključna.

Uradna rešitev

def podobna(beseda, sezBesed):
    '''Katera  beseda v sezBesed ima največ skupnih črk z besedo `beseda`'''
    beseda = beseda.lower() 
    bs = set(beseda) # množica vseh črk (različnih seveda ;-) )
    naj_crk = -1
    najBeseda = None # nimamo še najboljše besede
    for enaBeseda in sezBesed:
        skupnih = len(bs & set(enaBeseda.lower())) # velikost preseka mniožic
        if skupnih > naj_crk: # smo našli boljšega kandidata?
            naj_crk = skupnih
            najBeseda = enaBeseda
    return najBeseda

2. podnaloga

Napiši funkcijo najraznolika(bes), ki kot argument prejme neprazen seznam nizov in kot rezultat vrne niz, ki ima največ različnih znakov. Male in velike črke upoštevaj kot iste znake - beseda "MamA" ima samo dva različna znaka. Če obstaja več besed z enakim številom različnih znakov, naj vrne zadnjo (glede na položaj v seznamu) med njimi.

  >>> besede = ["RABarbara", "izpit", "zmagA"]
  >>> najraznolika(besede)
  zmagA

Uradna rešitev

def najraznolika(sezBesed):
     '''Kateri niz v sezBesed ima največ različnih znakov'''
     najZnakov = 0
     for enNiz in sezBesed:
         crk = len(set(enNiz.lower())) # število različnih znakov
         if crk >= najZnakov: # boljši kandidat (= ker hočemo zadnjega!)
              najZnakov = crk
              najNiz = enNiz
     return najNiz

3. podnaloga

Dan je seznam besed. Napiši funkcijo sameRazličneČrke(sezBesed), ki vrne urejen (kot ureja sorted) seznam tistih besed, ki imajo vse črke različne. Pri tem upoštevaj le črke angleške abecede! Male in velike črke razlikujemo! Vsako besedo upoštevaj le enkrat. Morda prav pride

  >>> import string
  >>> string.ascii_letters
  'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'

Zgled uporabe funkcije

  >>> besede = ["Rabarbara", "kralj", "Kok", "čmrlj!!", "11-isto", "zmaga", "kralj", "konec"]
  >>> sameRazličneČrke(besede)
  ['11-isto', 'Kok', 'konec', 'kralj', 'čmrlj!!']

Uradna rešitev

def ohraniČrke(niz):
    '''vrne niz, ki vsebuje le še črke angleške abecede.
    '''
    import string
    vseČrke = set(string.ascii_letters)
    novNiz = ""
    for znak in niz:
        if znak in vseČrke: # črka angleške abesede
            novNiz += znak
    return novNiz

def sameRazličneČrke(sezBesed):
     '''vrne po abecedi urejen seznam tistih besed, ki imajo vse črke različne.'''
     množicaNovih = set()
     for beseda in sezBesed:
         oklBeseda = ohraniČrke(beseda)
         crk = len(set(oklBeseda)) # število različnih znakov
         if crk >= len(oklBeseda): # različnih črkl je toliko kot vseh
              množicaNovih.add(beseda)
     novSeznam = list(množicaNovih) # iz množice v seznam
     return sorted(novSeznam)

4. podnaloga

Program

   import urllib.request
   naslov = "http://splasho.com/upgoer5/phpspellcheck" 
   naslov += "/dictionaries/1000.dicin"
   vir = urllib.request.urlopen(naslov)
   for vr in vir:
       vrstica = vr.decode()
       print(vrstica, end='')

izpiše po abecedi urejenih 1000 najpogostejših angleških besed Napiši funkcijo najraznolikaAngBeseda(), ki vrne po abecedi urejen seznam vseh tistih med temi 1000 najpogostejšimi angleškimi besedami, ki imajo največ različnih črk.

Uradna rešitev

def najraznolikaAngBeseda():
     '''Katere med pogostimi angl. besedami imajo največ različnih znakov'''
     import urllib.request
     naslov = "http://splasho.com/upgoer5/phpspellcheck" 
     naslov += "/dictionaries/1000.dicin"
     vir = urllib.request.urlopen(naslov)
     najZnakov = 0
     for beseda in vir:
         enNiz = beseda.decode()[:-1] # odstranimo \n
         crk = len(set(enNiz.lower())) # število različnih znakov
         if crk == najZnakov: # enakovredni kandidat
              najZnakov = crk
              najNizi.append(enNiz)
         elif crk > najZnakov: # boljši kandidat
              najZnakov = crk
              najNizi = [enNiz]
     return najNizi # urejanje ni potrebno, ker so že besede urejene

5. podnaloga

Napiši funkcijo vse_crke(beseda, črke), ki pove, ali množica crke vsebuje vse črke, ki se pojavijo v podani besedi. Pri tem sme množica vsebovati tudi črke, ki se v besedi ne pojavijo.

  >>> vse_crke("AMFITEATER", set(["A", "M"]))
  False
  >>> vse_crke("AMFITEATER", set(["A", "M", "F", "I", "T", "E"]))
  False
  >>> vse_crke("AMFITEATER", set(["A", "M", "F", "I", "T", "E", "R"]))
  True
  >>> vse_crke("AMFITEATER", set(["A", "M", "F", "I", "T", "E", "R", "X",
  "O", "B", "L"]))
  True

Uradna rešitev

def vse_crke(beseda, črke):
    '''Ali množica črke vsebuje vse črke, ki se pojavijo v besedi'''
    return not (set(beseda) - set(črke))

Vislice

Naloga sposojena od tu in malček predelana.

1. podnaloga

Program

   import urllib.request
   naslov = "http://lokar.fmf.uni-lj.si/JAVNO/samostalniki.txt" 
   vir = urllib.request.urlopen(naslov)
   vse = vir.read().decode()
   print(vse[:297])

izpiše prvih nekaj samostalnikov, ki so na datoteki na ustreznem naslovu Sestavi funkcijo vrniSamostalnik(n), ki vrne n-ti samostalnik iz te datoteke. Če je n npr. 1012 in je na datoteki le 300 samostalnikov, dobimo 112 tega.

  >>> vrniSamostalnik(1)
  DOGODEK

Uradna rešitev

def vrniSamostalnik(n):
    '''vrne n-ti samostalnik z datoteke'''
    import urllib.request
    naslov = "http://lokar.fmf.uni-lj.si/JAVNO/samostalniki.txt" 
    vir = urllib.request.urlopen(naslov)
    vse = vir.read().decode()
    samostalniki = vse.split()
    koliko = len(samostalniki)
    return samostalniki[(n - 1) % koliko]

2. podnaloga

Napiši funkcijo kjeJeZnak(beseda, znak), ki vrne urejen seznam mest v besedi beseda, na katerih nastopa podana črka znak. Funkcija naj ne izpisuje ničesar, le rezultat naj vrača!

    >>> kjeJeZnak("PONUDNIK","N")
    [2, 5]
    >>> kjeJeZnak("RABARBARA", "R")
    [0, 4, 7]
    >>> kjeJeZnak("AMFITEATER", "O")
    []

Uradna rešitev

def kjeJeZnak(beseda, crka):
    '''seznam indeksov, kjer v besedi nastošpa črka'''
    mesta = []
    for i in range(len(beseda)):
        if beseda[i] == crka:
            mesta.append(i)
    return mesta

# krajša rešitev z izpeljanimi seznami - preštudiraj!
def kjeJeZnak(beseda, crka):
    '''seznam indeksov, kjer v besedi nastošpa črka'''
    return [i for i, c in enumerate(beseda) if c==crka]

3. podnaloga

Napiši funkcijo imaVseZnake(beseda, znaki), ki pove, ali množica znaki vsebuje vse črke, ki se pojavijo v besedi beseda. Opomba: množica sme vsebovati tudi črke, ki se v besedi ne pojavijo.

    >>> imaVseZnake("AMFITEATER", {"A", "M"})
    False
    >>> imaVseZnake("AMFITEATER", {"A", "M", "F", "I", "T", "E"})
    False
    >>> imaVseZnake("AMFITEATER", {"A", "M", "F", "I", "T", "E", "R"})
    True
    >>> imaVseZnake("AMFITEATER", {"A", "M", "F", "I", "T", "E", "R", "X", "O", "B", "L"})
    True

Uradna rešitev

def imaVseZnake(beseda, crke):
    '''ali množica znaki vsebuje vse črke, ki se pojavijo v besedi beseda'''
    for c in beseda:
        if not c in crke:
            return False
    return True

# z množicami gre enostavneje
def imaVseZnake(beseda, crke):
    '''ali množica znaki vsebuje vse črke, ki se pojavijo v besedi beseda'''
    return set(beseda) <=  crke

4. podnaloga

Napiši funkcijo pokaziZnake(beseda, znaki), ki kot argument sprejme besedo beseda in množico (set) črk znaki. Funkcijo mora vrniti besedo, v kateri so vse črke, ki ne nastopajo v množici znaki, spremenjene v pike.

    >>> pokaziZnake("PONUDNIK", {"O", "N"})
    '.ON..N..'
    >>> pokaziZnake("PONUDNIK", {"O", "I", "K"})
    '.O....IK'
    >>> pokaziZnake("PONUDNIK", set())
    '........'
    >>> pokaziZnake("PONUDNIK", {"P", "O", "N", "I", "K", "U"})
    'PONU.NIK'

Uradna rešitev

def pokaziZnake(beseda, crke):
    "vrne besedo, v kateri so vse črke, ki ne nastopajo v množici znaki, spremenjene v pike."
    bes = ""
    for c in beseda:
        if c in crke:
            bes += c
        else:
            bes += "."
    return bes

# z izpeljanimi seznami gre tudi v eni vrstici ...
def pokaziZnake(beseda, crke):
    "vrne besedo, v kateri so vse črke, ki ne nastopajo v množici znaki, spremenjene v pike."    
    return "".join([c if c in crke else "." for c in beseda])

5. podnaloga

Napiši igro "vislice". Pri tem uporabljajte funkcije, ki ste jih napisali prej. Igra poteka tako, da računalnik izbere naključno besedo iz datoteke s samostalniki. Nato izpisuje besedo tako, da črke, ki jih igralec še ni uganil, zamenja s piko. Igralec vnese znak (pri čemer pazite na to, da lahko vnaša tudi male tiskane črke - spremenite jih v velike). Če beseda vsebuje vnešeni znak, se ti znaki v besedi "razkrijejo". Če beseda ne vsebuje želenega znaka, pa igralec "izgubi eno življenje". V začetku ima igralec 6 življenj; življenja se izpisujejo za besedo, med besedo in številom življenj mora biti en presledek. Igre je konec, ko igralec bodisi ugane vse črke, ki jih vsebuje beseda, bodisi izgubi vseh šest življenj. V prvem primeru mora računalnik izpisati "Bravo!" v drugem pa "Konec, iskana beseda je RESITEV.", kjer namesto RESITEV izpise besedo, ki smo jo (neuspešno) ugibali. Uspešna igra naj bo videti takole:

    ...... 6
    a
    .A.... 6
    e
    .A.... 5
    i
    .A.... 4
    o
    .A..O. 4
    r
    .A..O. 3
    l
    .A..O. 2
    k
    .A..O. 1
    t
    .A.TO. 1
    s
    .ASTO. 1
    n
    NASTO. 1
    p
    NASTOP 1
    Bravo!

Neuspešna pa bo takšna.

    ....... 6
    a
    ....... 5
    b
    ....... 4
    c
    ....... 3
    d
    D...D.. 3
    e
    D...DE. 3
    f
    D...DE. 2
    g
    D.G.DE. 2
    h
    D.G.DE. 1
    i
    Konec, iskana beseda je DOGODEK.

NALOGA NIMA TESTA

Uradna rešitev

import random
def igra():
    kolikoJihJe = 8175 # smo pokukali ...
    beseda = vrniSamostalnik(random.randint(1, kolikoJihJe))
    izbrane = set()
    zivljenj = 6
    while zivljenj:
        print(pokaziZnake(beseda, izbrane), zivljenj)
        if imaVseZnake(beseda, izbrane):
            print("Bravo!")
            break
        crka = input().upper()
        if not kjeJeZnak(beseda, crka):
            zivljenj -= 1
        izbrane.add(crka)
    if not zivljenj:
        print("Konec, iskana beseda je {}.".format(beseda))

Gimnazija

Janko ne mara matematike, je pa navdušen programer. In ker je v šoli dobil cel kup matematičnih nalog iz množic, jih bo rajši rešil tako, da bo napisal ustrezne programe v Pythonu.

1. podnaloga

Sestavi funkcijo vrniTri(a, b), ki vrne nabor (A, B, C), kjer so A, B in C naslednje množice:

$$A = \{30n; n \in \mathbb{N}, n < a\}$$ $$B = \{n^3; n \in \mathbb{N}, 5n < b\}$$ $$C = \{5n; n \in \mathbb{N}, n^3 < a * b\}$$

Pri tem vemo, da velja $a \in \mathbb{N}$ in $b \in \mathbb{N}$

Uradna rešitev

def vrniTri(a, b):
    '''vrne nabor treh matematično opisanih množic'''
    A = set()
    for n in range(1, a):
        A.add(30*n)
    B = set()
    n = 1
    while 5*n < b:
        B.add(n*n*n)
        n += 1
    C = set()
    n = 1
    while n*n*n < a*b:
        C.add(5*n)
        n += 1
    return (A, B, C)

2. podnaloga

Sestavi funkcijo računi(A, B, C), ki vrne nabor (X, Y, Z), kjer so X, Y in Z naslednje množice:

$$X = A \cap B \cap C$$ $$Y = A \setminus (B \cup C)$$ $$Z = (A \setminus B) \cup C$$

  >>> računi({2, 4, 6, 8, 10}, {1, 2, 3, 4}, {4, 6, 5})
  ({4}, {8, 10}, {4, 5, 6, 8, 10})

Uradna rešitev

def računi(A, B, C):
    '''Računamo z množicvami ...'''
    X = A & B & C
    Y = A - (B | C)
    Z = (A - B) | C
    return (X, Y, Z)

3. podnaloga

Sestavi funkcijo kartezični(A, B), ki vrne nabor (X, Y), kjer sta X in Y množici:

$$X = A \times B$$ $$Y = B \times B$$

  >>> kartezični({1, 2, 3}, {3, 4})
  ({(1,3), (1,4), (2,3), (2,4), (3,3), (3,4)}, {(3,3), (3,4), (4,3), (4,4)})

Uradna rešitev

def kartezični(A, B):
    '''vrne nabor (X, Y), kjer X = A x B in Y = B x B'''
    X = set()
    for el1 in A:
        for el2 in B:
            X.add((el1, el2))
    Y = set()
    for el1 in B:
        for el2 in B:
            Y.add((el1, el2))            
    return (X, Y)

4. podnaloga

Dane so množice $$A = \{x; x \in \mathbb{N} \land x < a\}$$ $$B = \{x; x \in \mathbb{N} \land x > b\}$$ $$C = \{2a + 1; a \in \mathbb{N}\}$$ Sestavi funkcijo vrniD(a, b), ki vrne $$D = (A \cap B) \setminus C\prime$$

  >>> vrniD(20, 13)
  {15,17,19}

Uradna rešitev

def vrniD(a, b):
    '''A = {x, x € N & x < a)
       B = {x, x € N & x > b)
       C = {2a + 1, a € N}
       D = (A /\ B) - C'
    '''
    D = set()
    # v preseku so naravna števila med b in a
    # zaradi - C' ohranimo le liha števila, razen 1! 
    for x in range(b + 1, a):
        if x % 2 == 1:
            D.add(x)
    # znebimo morebitne 1
    D.discard(1) # ne smemo uporabiti remove!
    return D

Mirko si želi dekle

1. podnaloga

Mirko (saj veste kateri - tu je cel film o njem si ogleduje spisek vseh deklet, ki so mu všeč:

Ker sta s Slavkom tudi po vojni ostala "nerazdvojna druga", Mirko ne želi hoditi z nobeno od Slavkovih bivših deklet. Slavko mu je poslal spisek svojih bivših.

Pomagaj Mirku in sestavi funkcijo toBoMojeDekle(spisekKandidatk, spisekSlavkovih) ki dobi dva seznama in vrne po abcedi urejen seznam deklet, med katerimi bo izbiral Mirko. A šarmerja, kot sta, imata izjemno dolga spiska. Zato bo "enostavna" rešitev

   for dekle in spisekKandidatk:
      if dekle not in spisekSlavkovih:
          kandidatke.append(dekle)

prepočasna. Sestavi torej metodo, kjer ne boš uporabil zanke (ne while in ne for, seveda pa tudi ne rekurzije)

   >>> toBoMojeDekle(['Jožica', 'Marjetka', 'Anja', 'Petra', 'Marija'],
                     ['Cilka', 'Petra', 'Pepca', 'Francka', 'Marija'])
   ['Anja', 'Jožica', 'Marjetka']

Uradna rešitev

def toBoMojeDekle(sezKand, sezSlavko):
    '''Vrne seznam možnih deklet za Mirka.
       Dejansko vrne seznam elementov tistih iz sezKand, ki
       niso v sezSlavko '''
    mnoMirko = set(sezKand)
    mnoSlavko = set(sezSlavko)
    katere = mnoMirko - mnoSlavko # razlika množic
    return sorted(katere) # želimo urejen seznam

Slovenske besede

1. podnaloga

Na http://bos.zrc-sazu.si/sbsj.html je 354.205 različnih besed iz gesel zbirke Besede slovenskega jezika. Program

   import urllib.request
   naslov = "http://bos.zrc-sazu.si/sbsj.html" 
   vir = urllib.request.urlopen(naslov)
   vse = vir.read().decode()
   besede = vse.split('\n')[15:]

poskuša iz te datoteke narediti seznam vseh besed. Ampak kot vidimo, smo na začetku odrezali premalo. Prav tako se nismo znebili konca ... Sestavi funkcijo vrniBesedo(n), ki vrne n-ti besedo iz te datoteke. Pri tem naj se n obnaša kot indeks v seznamu!

    >>> vrniBesedo(354204)
    žžžžk
    >>> vrniBesedo(354205)      
    Traceback (most recent call last):
    ...
    IndexError: list index out of range
    >>> vrniBesedo(0)
    a
    >>> vrniBesedo(-1)
    žžžžk

Uradna rešitev

def odst(niz):
    '''Iz niza odstrani <br>\r'''
    return niz[:-5]

def vrniBesedo(n):
    '''Vrne n-to besedo. Rešitev predpostavlja, da se besede začno v 15. vrstici in nehajo 13 vrstic pred koncem.
       Taka struktura je bila konec l. 2015
       Namenoma ni uporabljano iskanje prve (a) oz. zadnje besede ('žžžžk')
    '''
    import urllib.request
    naslov = "http://bos.zrc-sazu.si/sbsj.html" 
    vir = urllib.request.urlopen(naslov)
    vse = vir.read().decode()
    besede = vse.split('\n')[15:-13]
    slečene = list(map(odst, besede))
    return slečene[n]

2. podnaloga

Sestavi funkcijo sameRazlične(zač), ki vrne množico vseh tistih slovenskih besed, ki se začno z nizom zač in jih sestavljajo same različne črke!

   >>> sameRazlične('mate')
   {'mate', 'maternik', 'materski', 'matec', 'matenski',
    'matek', 'maten', 'matevž', 'maternski', 'mater', 'materin'}

Uradna rešitev

def odst(niz):
    '''Iz niza odstrani <br>\r'''
    return niz[:-5]

def vrniBesede():
    '''Vrne vse besede is SSKJ. Rešitev predpostavlja, da se besede začno v 15. vrstici in nehajo 13 vrstic pred koncem.
       Taka struktura je bila konec l. 2015
       Namenoma ni uporabljano iskanje prve (a) oz. zadnje besede ('žžžžk')
    '''
    import urllib.request
    naslov = "http://bos.zrc-sazu.si/sbsj.html" 
    vir = urllib.request.urlopen(naslov)
    vse = vir.read().decode()
    besede = vse.split('\n')[15:-13]
    slečene = list(map(odst, besede))
    return slečene

def enakeČrke(niz):
    '''Ali je niz sestavljen iz samih različnih črk'''
    return len(niz) == len(set(niz))

def sameRazlične(zač):
    '''Vrne množico tistih slovenskih besed, ki so sestavljene i
       z samih enakih črk in se začno z zač'''
    vseBesede = vrniBesede()
    mnBesed = set()
    for beseda in vseBesede:
        if beseda.startswith(zač) and enakeČrke(beseda):
            mnBesed.add(beseda)
    return(mnBesed)

Rekurzija

Začetna vaje iz rekurzije


Babuške

Imejmo poljubno celo število v seznamu v seznamu v seznamu ... v seznamu, poljubno mnogokrat. Takšni strukturi bomo rekli babuška. Primeri babušk so 13, [473], [[[[21]]]], [[7]], itd.

1. podnaloga

Sestavi funkcijo babuska(par), ki za parameter dobi takšno babuško ter vrne število seznamov, v katere je zaprto celo število. Za zgornje primere so rezultati 0, 1, 4 in 2. Predpostavi, da je struktura pravilna, torej res opisana kot zgoraj!

Uradna rešitev

def babuska(mojaB):
    '''Vrne število seznamov, v katere je zaprto celo število '''
    if isinstance(mojaB, int) : # ustavitveni pogoj - opraviti imamo s celim številom
        return 0
    # imamo torej vsaj eno zunanjo lupino
    znotraj = mojaB[0]     # odluščimo
    # in preštejemo koliko jih je znotraj
    koliko = babuska(znotraj)
    return 1 + koliko

2. podnaloga

Sestavi funkcijo kdoSeSkriva(par), ki za parameter dobi takšno babuško ter vrne zaprto celo število. Predpostavi, da je struktura pravilna, torej res opisana kot zgoraj! Pri reševanju boste morali preveriti, kakšnega tipa je podatek. V ta namen v ukazni lupini preizkusite naslednje ukaze:

   isinstance(12, int)
   x = 3.5
   isinstance(x, float)
   isinstance(x, int)
   isinstance(x, (int, float))
   isinstance(3, str)
   isinstance("3", str)
   isinstance([], list)
   s = [2, [3]]
   isinstance(s, list)
   isinstance(s[0], list)
   isinstance(s[0], int)
   isinstance(s[1], int)
   isinstance(s[1], list)
   isinstance(s[1][0], int)

Uradna rešitev

def kdoSeSkriva(mojaB):
    '''Vrne število seznamov, v katere je zaprto celo število '''
    if isinstance(mojaB, int) : # število ni skrito!
        return mojaB
    # imamo torej vsaj eno zunanjo lupino
    znotraj = mojaB[0]     # odluščimo
    # in poglejmo, kaj je znotraj
    kdo = kdoSeSkriva(znotraj)
    return kdo

Dolžina niza

1. podnaloga

Napišite rekurzivno funkcijo dolzina, ki sprejme niz in izračuna njegovo dolžino. Niz je sestavljen samo iz črk in števk. Dolžino izračunajte po takšnem pravilu: vsaka števka k dolžini prispeva svojo vrednost, vsaka črka pa 1 (kot običajno).

Primeri:

>>> dolzina('5a')
6
>>> dolzina('abcde12345')
20

V funkciji ne smete uporabiti zanke while ali zanke for!

Pri ugotavljanju, ali gre za črko ali števko si pomagajte s funkcijama isalpha() in isdigit(). Na primer:

>>> 'a'.isalpha()
True
>>> '5'.isdigit()
True

Uradna rešitev

def dolzina(s):
    '''Izračun drugačne dolžine niza'''
    if s == "": 
        return 0
    # razbijemo na prvi znak in preostanek (rep)
    prviZnak = s[0]
    rep = s[1:]
    dolzinaRepa = dolzina(rep)
    # kako prištejemo prvi znak
    if prviZnak.isalpha():
        return 1 + dolzinaRepa
    elif prviZnak.isdigit():
        return int(prviZnak) + dolzinaRepa

def dolzinaV2(niz):
    '''vrne "dolzino" niza, kjer je dolžina def. nekoliko drugače'''
    if niz == '':
        return 0
    if len(niz) == 1 : # samo en znak v nizu
        if niz.isdigit() : # in to je števka
            return(int(niz))          
        return 1 # ni števka, je "navaden" znak
    # "dolzina" celega niza je "dolzina" niza, ki ga
    # sestavlja le prvi znak in "dolzine" preostanka
    
    return dolzina(niz[0]) + dolzina(niz[1:])

SSCČ

Seznam seznamov celih števil (SSCŠ) je seznam, katerega elementi so bodisi cela števila bodisi seznami seznamov celih števil. Da bo enostavneje, recimo, da je tudi prazen seznam SSCŠ. Nekaj primerov:

      [-1, 2, -3]
      [1, [2], 3, [2, 3, 4]]
      [[[[2]], 1], [2, [[-3], [4]]]]

1. podnaloga

Sestavi funkcijo prestejStevila(sscs), ki prešteje, koliko je v sscs celih števil. Za primere od zgoraj so rezultati:

      3
      6
      5

Uradna rešitev

def prestejStevila(sscs):
    if len(sscs) == 0 : # tudi prazen seznam je SSCS
        return 0 # in nima celih števil
    kolikoStevil = 0
    # pregledamo vse elemente
    for el in sscs :
        # če je element 'običajno število'
        if isinstance(el, int):
            kolikoStevil += 1
        else : # potem mora pa biti sscs in moramo prešteti vsa cela števila v njem
            kolikoVSSCS = prestejStevila(el)
            kolikoStevil += kolikoVSSCS
    return kolikoStevil

2. podnaloga

Sestavi funkcijo prestejNegativnaStevila(sscs), ki prešteje, koliko je v seznamu seznamov negativnih celih števil. Za primere od zgoraj so rezultati:

      2
      0
      1

Uradna rešitev

def prestejNegativnaStevila(sscs):
    if len(sscs) == 0 : # tudi prazen seznam je SSCS
        return 0 # in nima celih števil
    kolikoStevil = 0
    # pregledamo vse elemente
    for el in sscs :
        # če je elemnt 'običajno število'
        if isinstance(el, int):
            if el < 0: # če je negativen, povečamo števec, drugače nič!
                kolikoStevil += 1
        else : # potem mora pa biti sscs in moramo prešteti vsa neg. cela števila v njem
            kolikoVSSCS = prestejNegativnaStevila(el)
            kolikoStevil += kolikoVSSCS
    return kolikoStevil

3. podnaloga

Sestavi funkcijo sestejStevila(sscs), ki sešteje vsa cela števila v seznamu seznamov celih števil. Za primere od zgoraj so rezultati:

      -2
      15
      6

Uradna rešitev

def sestejStevila(sscs):
    if len(sscs) == 0 : # tudi prazen seznam je SSCS
        return 0 # z vsoto 0
    vsotaStevil = 0
    # pregledamo vse elemente
    for el in sscs :
        # če je element 'običajno število'
        if isinstance(el, int):
            vsotaStevil += el # ga dodamo vsoti
        else : # če ni, pa mora biti sscs in njegovo vsoto dodamo skupni
            vsotaTegaEl = sestejStevila(el)
            vsotaStevil += vsotaTegaEl
    return vsotaStevil

4. podnaloga

Sestavi funkcijo jeSSCS(sscs), ki preveri (vrne True oz. False), če je dan seznam res seznam seznamov celih števil.

Uradna rešitev

def jeSSCS(sscs):
    if not isinstance(sscs, list): # če sploh ne gre za seznam
        return False
    if len(sscs) == 0 : # tudi prazen seznam je SSCS
        return True 
    # pregledamo vse elemente
    for el in sscs :
        # če je element ni ne SSCS ne celo število
        if not isinstance(el, int) and  \
           not jeSSCS(el) : # ne SSCE
            return False
    # vsi testi OK       

    return True

5. podnaloga

Sestavi funkcijo splosci(sscs), ki "splošči" seznam seznamov celih števil, torej vrne navaden seznam celih števil, ki so vsebovana v SSCŠ (v istem vrstnem redu). Za primere od zgoraj so rezultati:

      [-1, 2, -3]
      [1, 2, 3, 2, 3, 4]
      [2, 1, 2, -3, 4]

Uradna rešitev

def splosci(sscs):
    if len(sscs) == 0 : # prazen seznam je SSCS
        return [] # in vrnemo enakega
    noviSez = []
    # pregledamo vse elemente
    for el in sscs :
        # če je element 'običajno število'
        if isinstance(el, int):
                noviSez.append(el) # ga dodamo seznamu
        else : # če ni, pa mora biti sscs in dodamo sploščeno različico
            splosceniDel = splosci(el)
            noviSez.extend(splosceniDel)
    return noviSez

Rekurzija II


Vsote

1. podnaloga

Nekega dne je Ančka vsa obupana prosila brata Jureta, naj ji napiše program za domačo nalogo. Ta bi moral rekurzivno izračunati vsoto prvih n naravnih števil. A ker se je Juretu mudilo na vsakodnevni žur, je moral zelo hiteti in je zato v programu naredil nekaj napak. Jih najdeš?

   def vsota( n) :
       if n > 0 :
           return 1
       else :
           return vsota(n-1)

Uradna rešitev

def vsota( n) :
    if n <= 0 :
        return 0
    else :
        return n + vsota(n-1)

2. podnaloga

Jure je napisal kodo, ki s pomočjo rekurzije izračuna vsoto 1 + 1/2 + 1/3 + ... + 1/n. Žal mu je med sprehodom list s kodo padel v sneg in se je nekaj kode izbrisalo. Dopolni jo!

   def vsotaRec(n) :
      '''Izračun 1 + 1/2 + ... + 1 / n
         n je zagotovo naravno število
      '''
      if _______________ : # ustavitveni pogoj
            return ___________
      return ___________ + vsotaRec(_______) # rekurzivni klic

Uradna rešitev

def vsotaRec(n) :
   '''Izračun 1 + 1/2 + ... + 1 / n
      n je zagotovo naravno število
   '''
   if n == 1 : # ustavitveni pogoj
         return 1
   return 1 / n + vsotaRec(n - 1) # rekurzivni klic

Osnove rekurzije

1. podnaloga

Dana je rekurzivna funkcija `izpisObratniR', ki izpiše števila v obratnem vrstnem redu.

   def izpisObratniR(stevec) :
       if stevec <= 0 :
           return ""
       else :
           niz = "" + str(stevec) + ", " + izpisObratniR(stevec - 1)
           return niz

Kot vidiš, se na koncu izpiše odvečna vejica. Potrebujemo metodo, ki bi še vedno uporabljala rekurzijo, a ne bi izpisala dodatne vejice

Uradna rešitev

def izpisObratniR(stevec):
    '''Znebimo se odvečne , '''
    if stevec <= 0:
        return ""
    if stevec == 1:
        return "1"
    niz = "" + str(stevec) + ", " + izpisObratniR(stevec - 1)
    return niz

2. podnaloga

Sedaj na osnovi te metode sestavi izpisR(stevec), ki bo vrnila števila v "pravem" vrstnem redu. Še vedno pa uporabi rekurzijo in pazi, da ne bo odvečnih vejic!

Uradna rešitev

def izpisR(stevec):
    if stevec <= 0 :
        return ""
    if stevec == 1 :
        return "1"
    niz = izpisR(stevec - 1) + ', ' + str(stevec)
    return niz

Število enk

1. podnaloga

Sestavite rekurzivno funkcijo steviloEnk(n), ki prešteje, koliko je enk v dvojiškem zapisu nenegativnega celega števila n.

Primer: Število 52 je v dvojiškem zapisu enako 110100, torej so v njem tri enke. Če se ne spomniš postopka, si oglej ta posnetek

Uradna rešitev

def steviloEnk(n):
    '''Koliko enk je v dvojiškem zapisu števila n'''
    if n < 2: return n
    return n % 2 + steviloEnk(n // 2)

Barvanje ograje

Janezek mora za kazen prebarvati vrtno ograjo. Ta je sestavljena iz deščic širine 1 dm, ki so položene tesno skupaj ena ob drugi. Deščice so različnih višin, saj je ograjo izdelal Janezek sam iz odvečnih kosov, vsaka deščica pa se dotika tal, ki so ravna. Ograja izgleda nekako takole:

             +-+                             
     +-+     | |   +-+                             
     | | +-+-+ |   | |                           
   +-+ | | | | +-+-+ +-+                        
   | | +-+ | | | | | | |                        
   | | | | | | | | | | |                           
---------------------------

Janezek pozna višino vsake deščice, zato lahko ograjo opiše s seznamom, ki za vsako deščico vsebuje po eno število (višina v dm):

[2, 4, 1, 3, 3, 5, 2, 2, 4, 2]

Janezek ima čopič, ki je širok ravno 1 dm. Ograjo bo barval z navpičnimi in vodoravnimi potegi čopiča (nikoli ne vleče čopiča diagonalno). To počne tako, da se ob vsakem trenutku vse ščetine čopiča dotikajo ograje. Ponovno lahko potegne čopič tudi preko že pobarvanega dela ograje.

1. podnaloga

Janezek bi rad prebarval ograjo z minimalnim številom potez, da bi lahko čimprej odšel na obisk k Pepci. Sestavite funkcijo stevilo_potez(ograja), ki dobi seznam z opisom ograje in vrne minimalno število potez. Zgled:

>>> stevilo_potez([2, 2, 1, 2, 1])
3
>>> stevilo_potez([2, 2])
2
>>> stevilo_potez([5])
1

Uradna rešitev

def stevilo_potez(ograja):
    '''Koliko potez je potrebnih za prebarvanje ograje '''
    if len(ograja) == 0: # ni ograje
        return 0
    if len(ograja) == 1: # eno deščico prebarvamo naenkrat 
        return 1
    min_visina = min(ograja) 
    poteze = min_visina  # Vodoravne poteze.
    # sedaj barvamno od leve proti desni
    p = [] # kaj je ostalo še za prebarvati na levi strani 
    for i in range(len(ograja)): # pogledamo vse deščice
        if ograja[i] != min_visina: # deščica je višja, kot najnižja
            p.append(ograja[i] - min_visina) # pobarvati moramo še njen zgornji del
        else: # pobarvati moram vse levo, kar še nismo pobarvali prej
            poteze += stevilo_potez(p)  # Barvanje "podograje".
            p = [] # levo je sedaj pobarvano vse
    poteze += stevilo_potez(p) # ko smo na koncu, pobarvamo še preostale višje dele
    return min(poteze,
               len(ograja))  # Druga možnost so same navpične poteze.

Levenshteinova razdalja

1. podnaloga

Pri črkovalnikih (npr. Besana), optičnem prepoznavanju znakov in še marsikje nam zelo prav pride, če znamo za dva niza ugotoviti, kako podobna sta si, oziroma kakšna je razdalja med njima. Poznamo več različnih razdalj med nizi: Hammingovo, Damerau-Levenshteinovo, razdaljo največjega skupnega podniza ...

V MaFiRa Wikiju piše, da Levenshteinova razdalja (angl. Levenshtein distance ali edit distance) med dvema nizoma predstavlja minimalno število operacij, ki jih potrebujemo za preoblikovanje enega niza v drugega. Operacije so izbris, vstavljanje in zamenjava znakov. Dolžini nizov sta lahko poljubni. Niz $a$ # spremenimo v niz $b$ in pri tem uporabimo

  • brisanje enega znaka iz niza;
  • vstavljanje enega novega znaka v niz;
  • zamenjava obstoječega znaka z novim.

Npr. 'banana' se od 'ananas' razlikuje za 2, ker potrebujemo vsaj dve operaciji, da iz banane naredimo ananas. Npr.

'banana' --(zbrišemo b)-> 'anana' --(vstavimo s)-> 'ananas'

Verjetno se ne boste čudili, da je razdalja med pojmoma 'matematika' in 'fizika' kar 7 ;-)

Sestavite funkcijo levenshteinovaRazdalja(a, b), ki kot argumenta prejme niza a in b. Funkcija naj vrne urejevalniško razdaljo med a in b.

>>> levenshteinovaRazdalja('banana', 'ananas')
2
>>> levenshteinovaRazdalja('potop', 'pokol')
2
>>> levenshteinovaRazdalja('matematika', 'fizika')
7

Nalogo rešite z rekurzijo, torej brez uporabe zanke for oziroma while.

Namig: niza si predstavljajte kot xA in yB. In potem preverite, katera od treh možnost za predelavo bo dala minimalno vrednost ... Pri tem ne pozabite še na možnost, da je x == y

Uradna rešitev

def levenshteinovaRazdalja(a, b):
    ''' Vrne Levenshteinovo razdaljo med nizoma a in b'''
    if a == "": # v prazen niz moramo vstaviti vse znake iz b
        return len(b)
    if b == "": # pobrisati moramo vse znake iz a
        return len(a)
    if a[0] == b[0]: # če bomo znak pustili in spreminjali preostanek
        dodatek = 0 
    else:
        dodatek = 1 # tu bomo znaka zamenjali 
    # lahko znak iz a pobrišemo in preostanek a spremenimo v b
    # ali a spremenimo v cel b razen prvega in na začetek vstavimo prvi znak b-ja
    # ali pa preostanek a spremenimo v preostanek b in upoštevamo še morebitno spremembo prvega znaka
    return min(1 + levenshteinovaRazdalja(a[1:], b), levenshteinovaRazdalja(a, b[1:]) + 1, dodatek + levenshteinovaRazdalja(a[1:], b[1:]))

Permutacije

Oznake idej se nanašajo na prosojnice permutacije v spletni učilnici FMF za predmet Programiranje 1 v š.l. 15/16

Če hočemo narediti permutacijo iz 0 elementov, vrnemo tako kot itertools [[]]

1. podnaloga

Na osnovi ideje 1 smo zgradili osnutek metode permutacijeV1(seznam). Dopolni ga do delujočega!

    def permutacijeV1(seznam):
       '''Vrne seznam seznamov, kjer posamezni seznam predstavlja
           permutacijo elementov iz seznama '''
       ... manjka
       prviDel = seznam[:-1]
       zadnji = seznam[-1]
       permPrej = permutacijeV1(prviDel)
       # vstavi zadnji na vsa možna mesta
       rez = []
       for
          koncnaPer = permPrej z vstavljenim zadnjim
          rez.append(koncnaPer)
       ...
       return sorted(rez)

Uradna rešitev

def permutacijeV1(seznam):
    '''Vrne seznam seznamov, kjer posamezni seznam predstavlj
    permutacijo elementov iz seznama '''
    if seznam == []: # ni elementov, le prazna permutacija
        return [[]]
    if len(seznam) == 1: # en sam element - ena permutacija!
        return [[seznam[0]]]
    prviDel = seznam[:-1]
    zadnji = seznam[-1]
    permPrej = permutacijeV1(prviDel) 
    rez = []
    for posameznaP in permPrej:
        '''Iz te perm. naredimo len(seznam) novih '''
        for i in range(len(seznam)):
            novaP = posameznaP[:]
            novaP.insert(i, zadnji)
            rez.append(novaP)
    return sorted(rez)

2. podnaloga

Na osnovi ideje 2 smo zgradili osnutek metode permutacijeV2(seznam). Dopolni ga do delujočega!

    def permutacijeV2(seznam):
       '''Vrne seznam seznamov, kjer posamezni seznam predstavlja
           permutacijo elementov iz seznama '''
       ... manjka
       vse = []
       for ind, elt in enumerate(seznam):
          # zgradimo vse permutacije iz ostalih
          vsePerm = permutacijeV2(seznam[:ind] + seznam[ind + 1:] )
          # in sedaj permutacijam dodamo na začetek element elt
          ...
       ...
       return sorted(vse)

Uradna rešitev

def permutacijeV2(seznam):
    '''Vrne seznam seznamov, kjer posamezni seznam predstavlja
        permutacijo elementov iz seznama '''
    if seznam == []: # ni elementov, le prazna permutacija
        return [[]] 
    vse = []
    for ind, elt in enumerate(seznam):
       # zgradimo vse permutacije iz ostalih
       vsePerm = permutacijeV2(seznam[:ind] + seznam[ind + 1:] )
       # in sedaj permutacijam dodamo na začetek element elt
       for perm in vsePerm:
           vse.append([elt] + perm)
    return sorted(vse)

3. podnaloga

Na osnovi ideje 3 smo zgradili osnutek metode permutacijeV3(seznam). Dopolni ga do delujočega!

    def permutacijeV3(seznam, doSedaj = []):
       '''Vrne seznam vseh možnih permutacij iz elementov v elementi,
          ki imajo začetek enak permutaciji, ki so predstava doSedaj'''
       # Če smo že na koncu
           return [doSedaj]
       vse = []
       for elt in seznam:
          # če elt še lahko dodamo v doslej sestavljeno permutacijo
               # generiramo vse permutacije z začetkom doSedaj + [elt]
               # dodamo vsem
       return sorted(vse)

Uradna rešitev

def permutacijeV3(seznam, doSedaj = []):
    '''Vrne seznam vseh možnih permutacij iz elementov v elementi,
       ki imajo začetek enak permutaciji, ki so predstava doSedaj'''
    if seznam == []: # ni elementov, le prazna permutacija
        return [[]]
    if len(seznam) == len(doSedaj):# Če smo že na koncu
        return [doSedaj]
    vse = []
    for elt in seznam:
       if not elt in doSedaj: # če elt še lahko dodamo v doslej sestavljeno permutacijo
            permut = permutacijeV3(seznam, doSedaj + [elt]) # generiramo vse permutacije z začetkom doSedaj + [elt]
            vse += permut #dodamo vsem
    return sorted(vse)

4. podnaloga

Na osnovi ideje 4 smo zgradili osnutek metode permutacijeV4(seznam). Dopolni ga do delujočega!

    import itertools
    def permutacijeV4(seznam):
       '''Vrne seznam seznamov, kjer posamezni seznam predstavlja
           permutacijo elementov iz seznama '''
       # uporabimo itertools
       # pazimo, ker ta vrne seznam naborov!
       return sorted(vse)

Uradna rešitev

import itertools
def permutacijeV4(seznam):        
    '''Vrne seznam seznamov, kjer posamezni seznam predstavlja
        permutacijo elementov iz seznama '''
    # uporabimo itertools
    permu = itertools.permutations(seznam)
    vse = list(map(list, permu)) # pazimo, ker ta vrne seznam naborov!
    return sorted(vse)

Rekurzija in OS

Sklop nalog, kjer pišemo funcije, ki naredijo določena opravila z datotekami na nivoju operacijskega sistema. V nalogah bodo potrebne te funkcije:

  • os.listdir(mapa) seznam datotek in map v mapi
  • os.path.isfile(dat) ali je dat »navadna« datoteka
  • os.path.isdir(dat) ali je dat imenik
  • os.path.getsize(dat) velikost datoteke
  • os.path.split(dat) razdeli ime datoteke
  • os.path.getmtime(dat) čas zadnje spremembe datoteke
  • os.rmdir(pot) odstrani mapo

Pregled map in datotek

Za preverjanje nalog v imenik/mapo, kjer imate rešitve, skopirajte to datoteko ZIP in jo razširite (nastala bo mapa PythonOsRek s podmapami testX in EURO2012)

1. podnaloga

Sestavi funkcijo vrniMape_IskDat(potDoMape,imeDatoteke), ki vrne po abecedi urejen seznam vseh map, ki vsebujejo dano datoteko. Pri tem pot začni opisovati šele pri mapi potDoMape

    izpisiMape_IskDat('.\\PythonOSRek\\test1', 'drevo.py')
    ['.\\PythonOSRek\\test1', '.\\PythonOSRek\\test1\\PodM\\TraRa']

če potDoMape ne opisuje datoteke, vrni None

Uradna rešitev

import os

def vrniMape_IskDat(potDoMape,imeDatoteke):
    '''vrne po abecedi urejen seznam vseh map, ki vsebujejo dano datoteko.'''
    if not os.path.isdir(potDoMape): # ni te mape
        return None       
    seznamMap_IskDat = [] #shranimo poti do iskane datoteke
    if imeDatoteke in os.listdir(potDoMape): # pogledamo datoteke "na vrhu"
        seznamMap_IskDat.append(potDoMape)
    for datoteka in os.listdir(potDoMape):
        ime = potDoMape + "\\" + datoteka
        if os.path.isdir(ime): # gre za mapo
            vPodmapi = vrniMape_IskDat(ime,imeDatoteke)
            seznamMap_IskDat.extend(vPodmapi)
    return sorted(seznamMap_IskDat)

2. podnaloga

Sestavi funkcijo najvecDatVMapi(potDoMape), ki v izbrani mapi (in njenih podmapah) poišče mapo, ki vsebuje največ datotek (ne podmap). Funkcija naj vrne ime iskane mape (skupaj s potjo) ter število datotek v tej mapi. Če je takih map več, vrni prvo po abecedi (upoštevaj polno ime, skupaj s potjo!)

Uradna rešitev

import os

def najvecDatVMapi(mapa):
    ''' Funkcija vrne ime iskane mape (skupaj s potjo)
    ter število datotek v tej mapi. Če je takih map več, vrne prvo po abecedi (upošteva polno
    ime, skupaj s potjo!)'''
    najvecjaIme = mapa
    #prestejemo datoteke v tej mapi
    steviloDatotek = 0
    for trenDat in os.listdir(mapa):
        if os.path.isfile(mapa + "\\" + trenDat):
            steviloDatotek +=1
    najvecjaVelikost = steviloDatotek
    #mapo z najvec datotek primerjamo z naj mapami v podmapah
    seznamMap = [] # naredimo seznam podmap
    for trenMapa in os.listdir(mapa):
        if os.path.isdir(mapa + "\\" + trenMapa):
            seznamMap.append(trenMapa)
    #rekruzivno pregledamo podmape
    for mape in seznamMap:
        najvecjaImeImenik, najvecjaVelikostImenik = najvecDatVMapi(mapa + "\\" + mape)
        #če ima trenutna mapa enako prejsna ni potrebno narediti nič, ker so že po abecedi!
        if najvecjaVelikostImenik > najvecjaVelikost:
            najvecjaVelikost = najvecjaVelikostImenik
            najvecjaIme = najvecjaImeImenik
    return najvecjaIme, najvecjaVelikost

3. podnaloga

Sestavi funkcijo prestejDatoteke(potDoMape), ki prešteje, koliko je vseh datotek v izbrani mapi. Pri tem naj šteje tudi datoteke v podmapah, map pa naj ne šteje. Če potDomape ne opisuje mape, vrni None

Uradna rešitev

import os

def prestejDatoteke(potDoMape):
    ''' Vrne stevilo datotek v mapi in njenih podmapah.'''
    if not os.path.isdir(potDoMape): # ni te mape
        return None    
    steviloDatotek = 0 #prestejemo datoteke
    for trenDat in os.listdir(potDoMape):
        if os.path.isfile(potDoMape + "\\" + trenDat):
            steviloDatotek += 1
        else: # gre za mapo
            steviloDatotek += prestejDatoteke(potDoMape + "\\" + trenDat)
    return steviloDatotek

4. podnaloga

Sestavi funkcijo vrniDatPy(potDoMape), ki vrne po abecedi urejen seznam (polnih)imen vseh datotek v izbrani mapi in njenih podmapah, ki imajo končnico .py.

Uradna rešitev

import os

def vrniDatPy(potDoMape):
    '''Izpise imena vseh datotek (skupaj s potjo), ki imajo koncnico .py.'''
    if not os.path.isdir(potDoMape): # ni te mape
        return None 
    seznamMap_IskDat = [] #shranimo poti do iskane datoteke
    for datoteka in os.listdir(potDoMape): # pogledamo vse datoteke in datoteke v imenikih
        ime = potDoMape + "\\" + datoteka
        if os.path.isfile(ime): # gre za navadno datoteko
            končnica = os.path.splitext(datoteka)[1]
            if končnica == '.py':
                seznamMap_IskDat.append(ime)
        elif os.path.isdir(ime): # gre za mapo
            vPodmapi = vrniDatPy(ime)
            seznamMap_IskDat.extend(vPodmapi)
    return sorted(seznamMap_IskDat)

Briši ...

1. podnaloga

Sestavi funkcijo izbrisiPrazneMape(potDoMape), ki v izbrani mapi (in njenih podmapah) pobriše vse prazne datoteke (tiste z velikostjo 0) in vse prazne mape (tiste, ki ne vsebujejo nobenih datotek ali podmap). Funkcija naj vrne par, ki ga sestavljata število pobrisanih datotek in število pobrisanih map, oziroma None, če potDoMape ne opisuje imenika Pozor: ko zbrišemo prazno datoteko, se lahko sprazni tudi mapa.

Pred testiranjem vedno skopirajte to datoteko ZIP (staro mapo PythonOsRek pobrišite!) in jo razširite (nastala bo mapa PythonOsRek s podmapami testX in EURO2012)

Uradna rešitev

import os

def izbrisiPrazneMape(potDoMape):
    '''Izbriši vse prazne datoteke in prazne mape'''
    kolikoDat, kolikoMap = 0, 0
    if not os.path.isdir(potDoMape): # ni mapa
        return None
    vseDat = os.listdir(potDoMape)
    for datoteka in vseDat:
        #izbrisemo samo prazne datoteke
        if os.path.isfile(potDoMape + "\\" + datoteka) and os.path.getsize(potDoMape + "\\" + datoteka) == 0:
            os.remove(potDoMape + "\\" + datoteka)
            kolikoDat += 1
        elif os.path.isdir(potDoMape + "\\" + datoteka): #filtriramo mape
            kolDat, kolMap = izbrisiPrazneMape(potDoMape + "\\" + datoteka)
            kolikoDat += kolDat
            kolikoMap += kolMap
    #če je mapa sedaj prazna, jo izbrisemo
    if len(os.listdir(potDoMape)) == 0:
        os.rmdir(potDoMape)
        kolikoMap += 1
    return kolikoDat, kolikoMap

Izpisovanje ...

Naloga nima testnega programa! V Tomu so le zato, da so "na kupu"

Vsaka oddaja bo označena kot pravilna!

1. podnaloga

Izpiši mapo

Sestavi funkcijo izpisiDat_izbMapa(potDoMape), ki izpiše vse datoteke v izbrani mapi in njenih podmapah. Funkcija naj izpisuje samo imena datotek, brez poti. Uporabi izpis z zamikanjem, da bo razvidno, kateri mapi pripada katera datoteka. Vedno najprej izpiši datoteke, potem mape

    a.txt
    tra.py
    bla
      dat.txt
    ble
      blu
        c.dat
      xxx
      yyy
        e.dat

Uradna rešitev

2. podnaloga

Sestavi funkcijo izpisiDatPy(potDoMape), ki izpiše imena vseh datotek v izbrani mapi in njenih podmapah, ki imajo končnico .py. Izpisuje naj polna imena (skupaj s potjo).

Uradna rešitev


Iskanje

Naloge nimajo testnega programa! V Tomu so le zato, da so "na kupu". Vsaka oddaja bo označena kot pravilna!

Če uporabite datotečni strukturo iz datoteke ZIP, kot je opisana pri nalogi Pregled map in datotek, naj se vaše funkcije obnašajo kot kažejo zgledi!

1. podnaloga

Sestavi funkcijo izpisiDatVelikost(potDoMape, velikost), ki vrne seznam polnih imen datotek v dani mapi in njenih podmapah, katerih velikost je večja ali enaka dani vrednosti.

Uradna rešitev

2. podnaloga

Sestavi funkcijo izpisiMape_IskDat(potDoMape,imeDatoteke), ki izpiše vse mape, ki vsebujejo dano datoteko.

     >>> izpisiMape_IskDat('.\\PythonOSrek', 'drevo.py')
     .\PythonOSrek\test1\drevo.py
     .\PythonOSrek\test1\podM\TraRa\drevo.py
     .\PythonOSrek\test1a\drevo.py
     .\PythonOSrek\test1a\podM\TraRa\drevo.py
     >>>

Uradna rešitev

3. podnaloga

Sestavi funkcijo najvecDatVMapi(potDoMape), ki v izbrani mapi (in njenih podmapah) poišče mapo, ki vsebuje največ datotek (štejemo samo datoteke v mapi, ne tudi v podmapah, saj je drugače naloga nesmiselna). Funkcija naj vrne ime iskane mape (skupaj s potjo) ter število datotek v tej mapi.

  >>> najvecDatVMapi('.\\PythonOSrek')
  ('.\\PythonOSrek\\test1', 7)
  >>>

Uradna rešitev

4. podnaloga

Poišči najnovejšo datoteko

Sestavi funkcijo poisciSezZadSprmDat(potDoMape), ki v izbrani mapi (in njenih podmapah) poišče datoteke, ki so bile zadnje spremenjene. Funkcija naj vrne seznam imen teh datotek (skupaj s potjo).

    >>> poisciSezZadSprmDat('.\\PythonOSrek')
    ['.\\PythonOSrek\\EURO2012\\euro database.py']
    >>>

Uradna rešitev

5. podnaloga

Poišči najnovejšo datoteko

Sestavi funkcijo poisciZadSprmDat(potDoMape), ki v izbrani mapi (in njenih podmapah) poišče datoteko, ki so bile zadnja spremenjena. Funkcija naj vrne po abecedi najmanjše ime izmed teh datotek (skupaj s potjo).

    >>> poisciZadSprmDat('.\\PythonOSrek')
    '.\\PythonOSrek\\EURO2012\\euro database.py'
    >>>

Uradna rešitev

6. podnaloga

Poišči največjo datoteko

Sestavi funkcijo najvecjaDat(potDoMape), ki v izbrani mapi (in njenih podmapah) poišče največjo datoteko. Funkcija naj vrne par, ki ga sestavljata po abecedi zandje ime take datoteke (skupaj s potjo) in velikost

   >>> najvecjaDat('.\\PythonOSrek')
   ('.\\PythonOSrek\\test1\\Tips.PNG', 94535)
   >>>

Uradna rešitev

Rekurzija - ponovitev


SSCČ se vrača

Že pri prejšnjih nalogah smo spoznali SSČS. Ponovimo še enkrat - seznam seznamov celih števil (SSCŠ) je seznam, katerega elementi so bodisi cela števila bodisi seznami seznamov celih števil. Da bo enostavneje, recimo, da je tudi prazen seznam SSCŠ. Nekaj primerov:

      [-1, 2, -3]
      [1, [2], 3, [2, 3, 4]]
      [[[[0]], 1], [2, [[-3], [4]]]]

1. podnaloga

Sestavi funkcijo ločiStevila(sscs), ki vrne trojico (poz, nic, neg), ki pove koliko je v sscs pozitivnih, ničelnih in koliko negativnih celih števil. Za primere od zgoraj so rezultati:

      (1, 0, 2)
      (6, 0, 0)
      (3, 1, 1)

Predpostavi, da je parameter zagotovo SSCS,

Uradna rešitev

def ločiStevila(sscs):
    '''Razdeli podatke v SSCS na pozitivne, ničelne in negativne'''
    if len(sscs) == 0 : # tudi prazen seznam je SSCS
        return (0, 0, 0) # in nima celih števil
    kolikoPozStevil = 0
    kolikoNegStevil = 0
    kolikoNic = 0
    # pregledamo vse elemente
    for el in sscs :
        # če je element 'običajno število'
        if isinstance(el, int):
            if el > 0:
                kolikoPozStevil += 1
            elif el < 0:
                kolikoNegStevil += 1
            else:
                kolikoNic += 1
        else : # potem mora pa biti sscs in moramo prešteti vsa cela števila v njem
            trojka = ločiStevila(el)
            kolikoPozStevil, kolikoNic, kolikoNegStevil = kolikoPozStevil + trojka[0], \
                                                          kolikoNic  + trojka[1], kolikoNegStevil + trojka[2]
    return kolikoPozStevil, kolikoNic, kolikoNegStevil

2. podnaloga

Sestavi funkcijo jeSSCS(sscs), ki preveri (vrne True oz. False), če je dan seznam res seznam seznamov celih števil.

POZOR: Python pravi, da je isinstance(True, int) enako True, pa tudi isinstance(False, int) enako True, zato se "znebi" napačnih logičnih vrednosti z isinstance(True, bool)

Uradna rešitev

def jeSSCS(sscs):
    '''Ali je sscs res seznam seznamov celih števil'''
    if not isinstance(sscs, list): # če sploh ne gre za seznam
        return False
    if len(sscs) == 0 : # tudi prazen seznam je SSCS
        return True 
    # pregledamo vse elemente
    for el in sscs :
        # če je element logična vrednost
        if isinstance(el, bool):
            return False
        # če element ni ne SSCS ne celo število
        if not isinstance(el, int) and  \
           not jeSSCS(el) : # ne SSCE
            return False
    # vsi testi OK       

    return True

3. podnaloga

Sestavi funkcijo kolikoNapacnih(sscs), ki prešteje, koliko je v seznamu seznamov celih števil napačnih podatkov, torej elementov, ki niso ne cela števila, ne SSCŠ. Če argument ni seznam, vrni None

    >>>kolikoNapacnih([1, 'bla', 2])
    1
    >>>kolikoNapacnih([1, [['bla', 'blu'], False], 2])
    1
    >>>kolikoNapacnih([[1.8], [2, 3, [4]], [False, 4, [True]]])
    2
    >>>kolikoNapacnih(12)
    None

Uradna rešitev

def kolikoNapacnih(sscs):
    '''Koliko elementov je v sscs napačnih'''
    if not isinstance(sscs, list):
        return None
    kolikoNarobe = 0
    # pregledamo vse elemente
    for el in sscs:
        if (not isinstance(el, int)) and (not jeSSCS(el)) \
                or isinstance(el, bool):
                kolikoNarobe += 1
    return kolikoNarobe

Permutacije

Oznake idej se nanašajo na prosojnice permutacije v spletni učilnici FMF za predmet Programiranje 1 v š.l. 15/16

Če hočemo narediti permutacijo iz 0 elementov, vrnemo tako kot itertools [[]]

1. podnaloga

Na osnovi ideje 1 smo zgradili osnutek metode permutacijeV1(seznam). Dopolni ga do delujočega!

    def permutacijeV1(seznam):
       '''Vrne seznam seznamov, kjer posamezni seznam predstavlja
           permutacijo elementov iz seznama '''
       ... manjka
       prviDel = seznam[:-1]
       zadnji = seznam[-1]
       permPrej = permutacijeV1(prviDel)
       # vstavi zadnji na vsa možna mesta
       rez = []
       for
          koncnaPer = permPrej z vstavljenim zadnjim
          rez.append(koncnaPer)
       ...
       return sorted(rez)

Uradna rešitev

def permutacijeV1(seznam):
    '''Vrne seznam seznamov, kjer posamezni seznam predstavlj
    permutacijo elementov iz seznama '''
    if seznam == []: # ni elementov, le prazna permutacija
        return [[]]
    if len(seznam) == 1: # en sam element - ena permutacija!
        return [[seznam[0]]]
    prviDel = seznam[:-1]
    zadnji = seznam[-1]
    permPrej = permutacijeV1(prviDel) 
    rez = []
    for posameznaP in permPrej:
        '''Iz te perm. naredimo len(seznam) novih '''
        for i in range(len(seznam)):
            novaP = posameznaP[:]
            novaP.insert(i, zadnji)
            rez.append(novaP)
    return sorted(rez)

2. podnaloga

Na osnovi ideje 2 smo zgradili osnutek metode permutacijeV2(seznam). Dopolni ga do delujočega!

    def permutacijeV2(seznam):
       '''Vrne seznam seznamov, kjer posamezni seznam predstavlja
           permutacijo elementov iz seznama '''
       ... manjka
       vse = []
       for ind, elt in enumerate(seznam):
          # zgradimo vse permutacije iz ostalih
          vsePerm = permutacijeV2(seznam[:ind] + seznam[ind + 1:] )
          # in sedaj permutacijam dodamo na začetek element elt
          ...
       ...
       return sorted(vse)

Uradna rešitev

def permutacijeV2(seznam):
    '''Vrne seznam seznamov, kjer posamezni seznam predstavlja
        permutacijo elementov iz seznama '''
    if seznam == []: # ni elementov, le prazna permutacija
        return [[]] 
    vse = []
    for ind, elt in enumerate(seznam):
       # zgradimo vse permutacije iz ostalih
       vsePerm = permutacijeV2(seznam[:ind] + seznam[ind + 1:] )
       # in sedaj permutacijam dodamo na začetek element elt
       for perm in vsePerm:
           vse.append([elt] + perm)
    return sorted(vse)

3. podnaloga

Na osnovi ideje 3 smo zgradili osnutek metode permutacijeV3(seznam). Dopolni ga do delujočega!

    def permutacijeV3(seznam, doSedaj = []):
       '''Vrne seznam vseh možnih permutacij iz elementov v elementi,
          ki imajo začetek enak permutaciji, ki so predstava doSedaj'''
       # Če smo že na koncu
           return [doSedaj]
       vse = []
       for elt in seznam:
          # če elt še lahko dodamo v doslej sestavljeno permutacijo
               # generiramo vse permutacije z začetkom doSedaj + [elt]
               # dodamo vsem
       return sorted(vse)

Uradna rešitev

def permutacijeV3(seznam, doSedaj = []):
    '''Vrne seznam vseh možnih permutacij iz elementov v elementi,
       ki imajo začetek enak permutaciji, ki so predstava doSedaj'''
    if seznam == []: # ni elementov, le prazna permutacija
        return [[]]
    if len(seznam) == len(doSedaj):# Če smo že na koncu
        return [doSedaj]
    vse = []
    for elt in seznam:
       if not elt in doSedaj: # če elt še lahko dodamo v doslej sestavljeno permutacijo
            permut = permutacijeV3(seznam, doSedaj + [elt]) # generiramo vse permutacije z začetkom doSedaj + [elt]
            vse += permut #dodamo vsem
    return sorted(vse)

4. podnaloga

Na osnovi ideje 4 smo zgradili osnutek metode permutacijeV4(seznam). Dopolni ga do delujočega!

    import itertools
    def permutacijeV4(seznam):
       '''Vrne seznam seznamov, kjer posamezni seznam predstavlja
           permutacijo elementov iz seznama '''
       # uporabimo itertools
       # pazimo, ker ta vrne seznam naborov!
       return sorted(vse)

Uradna rešitev

import itertools
def permutacijeV4(seznam):        
    '''Vrne seznam seznamov, kjer posamezni seznam predstavlja
        permutacijo elementov iz seznama '''
    # uporabimo itertools
    permu = itertools.permutations(seznam)
    vse = list(map(list, permu)) # pazimo, ker ta vrne seznam naborov!
    return sorted(vse)

Briši ...

1. podnaloga

Sestavi funkcijo izbrisiPrazneMape(potDoMape), ki v izbrani mapi (in njenih podmapah) pobriše vse prazne datoteke (tiste z velikostjo 0) in vse prazne mape (tiste, ki ne vsebujejo nobenih datotek ali podmap). Funkcija naj vrne par, ki ga sestavljata število pobrisanih datotek in število pobrisanih map, oziroma None, če potDoMape ne opisuje imenika Pozor: ko zbrišemo prazno datoteko, se lahko sprazni tudi mapa.

Pred testiranjem vedno skopirajte to datoteko ZIP (staro mapo PythonOsRek pobrišite!) in jo razširite (nastala bo mapa PythonOsRek s podmapami testX in EURO2012)

Uradna rešitev

import os

def izbrisiPrazneMape(potDoMape):
    '''Izbriši vse prazne datoteke in prazne mape'''
    kolikoDat, kolikoMap = 0, 0
    if not os.path.isdir(potDoMape): # ni mapa
        return None
    vseDat = os.listdir(potDoMape)
    for datoteka in vseDat:
        #izbrisemo samo prazne datoteke
        if os.path.isfile(potDoMape + "\\" + datoteka) and os.path.getsize(potDoMape + "\\" + datoteka) == 0:
            os.remove(potDoMape + "\\" + datoteka)
            kolikoDat += 1
        elif os.path.isdir(potDoMape + "\\" + datoteka): #filtriramo mape
            kolDat, kolMap = izbrisiPrazneMape(potDoMape + "\\" + datoteka)
            kolikoDat += kolDat
            kolikoMap += kolMap
    #če je mapa sedaj prazna, jo izbrisemo
    if len(os.listdir(potDoMape)) == 0:
        os.rmdir(potDoMape)
        kolikoMap += 1
    return kolikoDat, kolikoMap

Collatzovo zaporedje

Collatzovo zaporedje tvorimo na sledeč način. Začnemo z nekim naravnim številom $n$, ki ga nato delimo z $2$, če je sodo, ali pa pomnožimo s $3$ in prištejemo $1$, če je liho. Postopek ponavljamo, dokler ne pridemo do števila $1$ (v tem primeru stvar ni več zanimiva, saj se začno ponavljati števila $1, 4, 2, 1, 4, 2, 1, \ldots$). Primer zaporedja, ki se začne z $6$ je tako $6, 3, 10, 5, 16, 8, 4, 2, 1$. Collatzova domneva, ki trdi, da za poljubno naravno število njegovo Collatzovo zaporedje sčasoma doseže $1$, je še vedno nerešena.

Vse naloge, razen prve, rešite z rekurzijo!

1. podnaloga

Sestavite funkcijo naslednji_clen(n), ki izračuna člen, ki v Collatzovemu zaporedju sledi številu n.

Uradna rešitev

def naslednji_clen(n):
    '''člen, ki v Collatzovemu zaporedju sledi številu `n`'''
    if n % 2 == 0:
        return n // 2
    else:
        return 3 * n + 1

2. podnaloga

Sestavite funkcijo dolzina_zaporedja(n), ki izračuna dolžino Collatzovega zaporedja, ki se začne s številom n.

Uradna rešitev

def dolzina_zaporedja(n):
    '''dolžina Collatzovega zaporedja, ki se začne s številom `n`.'''
    if n == 1:
        return 1
    else:
        return 1 + dolzina_zaporedja(naslednji_clen(n))

3. podnaloga

Sestavite funkcijo najvecji_clen(n), ki izračuna največji člen v Collatzovem zaporedju, ki se začne s številom n.

Uradna rešitev

def najvecji_clen(n):
    '''največji člen v Collatzovem zaporedju, ki se začne s številom `n`.'''
    if n == 1:
        return 1
    else:
        return max(n, najvecji_clen(naslednji_clen(n)))

4. podnaloga

Sestavite funkcijo najdaljse_zaporedje(m, n), ki vrne dolžino najdaljšega zaporedja med vsemi tistimi Collatzovimi zaporedji, ki se začnejo s števili med (vključno) m in n.

Uradna rešitev

def najdaljse_zaporedje(m, n):
    '''Dolžina najdaljšega zaporedja med vsemi tistimi Collatzovimi zaporedji,
       ki se začnejo s števili med (vključno) m in n.'''
    if m == n:
        return dolzina_zaporedja(m)
    else:
        return max(dolzina_zaporedja(m), najdaljse_zaporedje(m + 1, n))

Slovarji


Teža tovora

Dan je slovar, ki hrani podatke o tem, kakšen tovor smo naložili na tovornjak in kolikšna je bila masa posameznih kosov tovora. Na primer, slovar

  {'televizor' : 17, 'zaboj piva' : 12, 'zofa' : 35}

pove, da smo na tovornjak naložili 17kg težak televizor, 12kg težak zaboj piva in 35kg težko zofo.

1. podnaloga

Sestavi funkcijo tezaTovora(tovor), ki sprejme tak slovar in vrne skupno težo tovora (v našem primeru 17 + 12 + 35 = 64 kg).

Uradna rešitev

def tezaTovora(tovor):
    ''' koliko je skupna teža tovora, podanega v slovarju tovor '''
    skupaj = 0
    for teza in tovor.values():
        skupaj += teza
    return skupaj

2. podnaloga

Sestavi funkcijo lahkoPelje(tovor, nosilnost), ki ugotovi, ali tovornjak z nosilnostjo nosilnost lahko pelje tovor, dan s slovarjem tovor

Uradna rešitev

def lahkoPelje(tovor, nosilnost):
    ''' ali tovornjak z nosilnostjo nosilnost lahko pelje tovor, dan s slovarjem tovor '''
    return tezaTovora(tovor) <= nosilnost

def lahkoPelje_V2(tovor, nosilnost):
    ''' koliko je skupna teža tovora, podanega v slovarju tovor 
        Predpostavimo, da funkcije tezaTovora nimamo '''
    # izračunajmo težo tovora
    skupaj = 0
    for teza in tovor.values():
        skupaj += teza
    # in preverimo, če gre
    return skupaj <= nosilnost

3. podnaloga

Sestavi funkcijo enakaTovora(tovor1, tovor2), ki ugotovi, če sta tovora, podana v slovarjih tovor1 in tovor2 enaka! Tovora sta enaka, če vsebujeta iste predmete z isto težo

Uradna rešitev

def enakaTovora(tovor1, tovor2):
    ''' Ali sta tovora, podana v slovarjih tovor1 in tovor2 enaka'''
    # ali imata slovarja enako ključev (enako dolžino)
    if len(tovor1) != len(tovor2):
        return False
    # sedaj je dovolj, če preverimo, če so vsi ključi enega slovarja v drugem
    # in dajo isto vrednost
    for predmet, teza in tovor1.items():
        # je predmet tudi v drugem slovarju
        if not predmet in tovor2:
            return False
        # sta teži v obeh slovarjih enaki
        if teza != tovor2[predmet]:
            return False
    # vsi testi so OK
    return True

def enakaTovora(tovor1, tovor2):
    ''' Ali sta tovora, podana v slovarjih tovor1 in tovor2 enaka'''
    # lahko pa enostavno upoštevamo, da v objektu, ki ga vrne metoda items()
    # vrstni red ni pomemben, torej bosta slovarja enaka, če
    # bosta imela enaka items()
    return tovor1.items() == tovor2.items()

4. podnaloga

Sestavi funkcijo smiselenTovor(tovor, minTeža, maxTeža), ki "prečisti" slovar z danimi težami tako, da vrne slovar, v katerem so le tisti predmeti, katerih teža je med minTeža in maxTeža.

Uradna rešitev

def smiselenTovor(tovor, minTeža, maxTeža):
    ''' vrne slovar, v katerem so le tisti predmeti,
        katerih teža je med minTeža in maxTeža '''
    noviTovor = {}
    # pregledamo celoten tovor in v nov slovar dodamo predmete
    # z ustrezno težo
    for predmet, teža in tovor.items():
        if minTeža <= teža <= maxTeža:
            noviTovor[predmet] = teža
    return noviTovor

5. podnaloga

Sestavi funkcijo najdaljšiOpis(tovor), ki vrne po abecedi urejen seznam tistih predmetov, ki imajo najdaljši opis. Za slovar iz zgleda bi torej dobili ['zaboj piva']

Uradna rešitev

def najdaljšiOpis(tovor):
    ''' vrne po abecedi urejen seznam tistih predmetov,
        ki imajo najdaljši opis '''
    # v seznam opisov dodamo '-'  
    # Če bodo vsi predmeti imeli le enoznakovni opis, se bomo tega
    # na koncu po potrebi znebili
    seznamOpisov = ['-'] # enega potrebujemo, da bomo vedno primerjali s prvim iz
                         # seznama kandidatov!
    for predmet in tovor:
        # je opis daljši?
        if len(predmet) > len(seznamOpisov[0]) : # seznamOpisov[0] vedno obstaja!
            seznamOpisov = [predmet]
        elif len(predmet) == len(seznamOpisov[0]) : # dodamo
            seznamOpisov.append(predmet)
    # je slučajno najdaljši opis dolg le en znak
    if len(seznamOpisov[0]) == 1:
        # "-" se moramo znebiti
        seznamOpisov = seznamOpisov[1:]
    return sorted(seznamOpisov)

6. podnaloga

Sestavi funkcijo najlažjiPredmet(tovor), ki vrne po abecedi urejen seznam tistih predmetov, ki so najlažji. Za slovar iz zgleda bi torej spet dobili ['zaboj piva'], za prazen slovar pa seveda vrnemo prazen seznam

Uradna rešitev

def najlažjiPredmet(tovor):
    ''' vrne po abecedi urejen seznam tistih predmetov,
        ki so najlažji '''
    if not tovor: # če je slovar prazen, vrnemo prazen seznam
        return []
    # kandidat za najlažjega naj bo za 1 večja teža kot je prvi iz slovarja
    for predmet, teža in tovor.items():
        najlažji = teža + 1
        seznamNajlažjih = ['karkoli'] # tega na koncu tako ali tako ne bo!
        break # po prvem prehodu takoj zapustimo zanko
    # sedaj pa pregledujemo 'zares'
    for predmet, teža in tovor.items():     
        # je predmet lažji?
        if teža < najlažji : # nov kandidat za najlažjega
            seznamNajlažjih = [predmet]
            najlažji = teža
        elif teža == najlažji : # dodamo
            seznamNajlažjih.append(predmet)
    return sorted(seznamNajlažjih)

Šifriranje

Substitucijska šifra je enostaven način šifriranja, pri katerem vsako črko iz dane abecede zamenjamo z neko drugo črko. Tako šifro predstavimo s slovarjem, ki ima za ključe vse črke iz abecede, pripadajoče vrednosti pa so črke, s katerimi jih zašifriramo.

Tako slovar {'A': 'B', 'C': 'C', 'B': 'D', 'D': 'A'} pomenil, da se A zašifrira v B, B v D, D v A, C pa se ne spremeni.

1. podnaloga

Sestavi funkcijo sifriraj(sifra, beseda), ki vrne besedo, zašifrirano z dano šifro. Predpostavite lahko, da vse črke v besedi nastopajo v šifri.

Uradna rešitev

def sifriraj(sifra, beseda):
    '''S slovarjem sifra zasifriramo besedo. Vrnemo niz, ki
       predstavlja ustrezno zašifrirano besedo.'''
    sezZasifCrk = []
    for crka in beseda: # zašifriramo posamezno črko
        sezZasifCrk.append(sifra[crka])
    return ''.join(sezZasifCrk) # vrnemo kot niz

2. podnaloga

Sestavi funkcijo jeSifra(slovar), ki ugotovi, ali slovar predstavlja šifro, torej ali je bijekcija črk na neki abecedi.

Uradna rešitev

def jeSifra(slovar):
    '''Ali slovar predstavlja šifro'''
    # Pogledamo, če je množica ključev slovarja enaka množici vrednosti.
    # Ker so ključi v slovarju enolični, je velikost množice ključev enaka
    # kar številu črk v abecedi. Če sta torej množici ključev in vrednosti
    # enaki, je slovar surjektiven in s tem tudi injektiven.
    return set(slovar.keys()) == set(slovar.values())

3. podnaloga

Sestavi funkcijo inverz(sifra), ki vrne inverz dane šifre, če ta obstaja. V nasprotnem primeru funkcija vrne None.

Uradna rešitev

def inverz(sifra):
    '''Vrni slovar, ki predstavlja obratno šifro, oziroma None, če ne gre'''
    if not jeSifra(sifra):
        return None
    inv = {}
    for k, v in sifra.items():
        inv[v]=k
    return inv

4. podnaloga

Sestavi funkcijo odsifriraj(sifra, beseda), ki sprejme šifro in zašifrirano besedilo, vrne pa odšifrirano besedilo. Če se besedila ne da odšifrirati, naj funkcija vrne None.

Uradna rešitev

def odsifriraj(sifra, beseda):
    '''Vrnemo odšifrirano besedo, ki je bila šifrirana s šifro v slovarju sifra'''
    inv = inverz(sifra)
    if inv: # če inverz ne obstraja, dobimo None, kar se tolmači kot False
        return sifriraj(inv, beseda)
    else:
        return None # če inverza ni, ne moremo dešifrirati!

Blagajna

Zaradi napovedane prepovedi pisanja blagajniških računov na roko bodo v lokalni prodajalni prisiljeni preiti na računalniško podprto blagajno. Bazo prodajnih artiklov so pripravili v obliki slovarja, kjer je ključ ime artikla, vrednost pa par, ki vsebuje maloprodajno ceno in stopnjo davka (v procentih), na primer takole:

artikli = {
    'ponev': (32.74, 22),
    'knjiga': (18.60, 9.5),
    'gugalnik': (153.22, 22),
    'igrača': (12.18, 0),
    'likalnik': (43.15, 9.5)
}

Tudi vsak izdan račun predstavimo v obliki slovarja. Ključ je ime kupljenega artikla, vrednost pa par, ki vsebuje količino (celo število) in popust (v procentih). Primeri:

racun1 = {'igrača': (2, 0), 'ponev': (1, 20), 'knjiga': (5, 10)}
racun2 = {'likalnik': (1, 5), 'igrača': (1, 20)}
racun3 = {'knjiga': (1, 0), 'igrača': (1, 0), 'ponev': (2, 20)}

1. podnaloga

Sestavite funkcijo davcna_osnova(mpc, ddv), ki bo za dano maloprodajno ceno mpc in stopnjo davka ddv izračunala davčno osnovo, to je znesek, na katerega zaračunamo davek, da dobimo maloprodajno ceno. Izračunano davčno osnovo zaokrožite na dve decimalni mesti. Zgled:

>>> davcna_osnova(244.13, 22)
200.11
>>> davcna_osnova(1683.76, 9.5)
1537.68

Uradna rešitev

def davcna_osnova(mpc, ddv):
    '''Davčna osnova glede na maloprodajno ceno in davek'''
    return round(mpc / (1 + ddv / 100), 2)

2. podnaloga

Sestavite funkcijo znesek_racuna(artikli, racun), ki izračuna, koliko mora kupec plačati za kupljeno blago. Končni znesek zaokrožite na dve decimalni mesti. Zgled:

>>> znesek_racuna(artikli, racun1)
134.25

Predpostavite, da so na računu le artikli, ki so v s slovarjem podani bazi artikli

Uradna rešitev

def znesek_racuna(artikli, racun):
    '''Kaksen je znesek racuna za kupljene artikle. Racun je podan kot slovar, artikli pa je slovar s podatki o vseh artiklih'''
    vsota = 0
    for artikel in racun:
        cena = artikli[artikel][0]
        kolicina = racun[artikel][0]
        popust = racun[artikel][1]
        znesek = kolicina * cena * (1 - popust / 100)
        vsota += znesek
    return round(vsota, 2)

3. podnaloga

Sestavite funkcijo davcni_obracun(artikli, racun), ki sestavi specifikacijo davka za dani račun. Specifikacijo davka predstavimo s slovarjem, kjer so ključi davčne stopnje, vrednosti pa vsote davčnih osnov za kupljene artikle s takšno davčno stopnjo. Uporabite funkcijo davcna_osnova iz prve podnaloge! Končni zneski naj bodo zaokroženi na dve decimalni mesti. Zgled:

>>> davcni_obracun(artikli, racun1)
{0: 24.36, 9.5: 76.44, 22: 21.47}

Uradna rešitev

def davcni_obracun(artikli, racun):
    '''Vrne slovar s specifikacijo davka za dani račun'''
    obracun = {}
    for artikel in racun:
        cena = artikli[artikel][0]
        kolicina = racun[artikel][0]
        popust = racun[artikel][1]
        znesek = kolicina * cena * (1 - popust / 100)
        ddv = artikli[artikel][1]
        osnova = davcna_osnova(znesek, ddv)
        obracun[ddv] = obracun.get(ddv, 0) + osnova
    return obracun

4. podnaloga

Ob koncu delovnega dneva nas zanima, koliko katerega artikla smo prodali, ločeno po različnih popustih, ki smo jih priznavali. Takšen dnevni obračun predstavimo s slovarjem, kjer so ključi pari oblike (artikel, popust), vrednosti pa ustrezne prodane količine tega artikla. Sestavite funkcijo dnevni_obracun(artikli, racuni), ki bo za dani seznam računov sestavila takšen dnevni obračun. Zgled:

>>> dnevni_obracun(artikli, [racun1, racun2, racun3])
{('ponev', 20): 3, ('likalnik', 5): 1, ('knjiga', 0): 1, ('igrača', 0): 3, ('knjiga', 10): 5, ('igrača', 20): 1}

Uradna rešitev

def dnevni_obracun(artikli, racuni):
    '''Vrne slovar z dnevnim obračunom'''
    obracun = {}
    for racun in racuni:
        for artikel in racun:
            kolicina = racun[artikel][0]
            popust = racun[artikel][1]
            kljuc = (artikel, popust)
            # pazimo na ključ, ki še ni v slovarju
            obracun[kljuc] = obracun.get(kljuc, 0) + kolicina
    return obracun

Davki

Pri nas trenutno obstajajo tri davčne stopnje: višja, nižja in oproščena. Davčne stopnje so podane v slovarju, kjer so ključi vedno taki, kot kaže zgled davcne_stopnje = {'V': 22, 'N': 9.5, 'O': 0}

Lastnik popularne trgovinice (ki ima celo lastno pekarno), vas je najel, da mu napišete program za sestavljanje računov.

Nakupi so shranjeni v slovarju košarice, katerega ključi so imena kupcev, vrednosti pa slovarji predmetov in količin. Zgled:

kosarice = {'Janez': {'banana': 5, 'jogurt': 7},
            'Mojca': {'francoska štrucka': 7}}

1. podnaloga

V tej trgovinici so francoske štručke zelo popularne. Vsak kupec vedno kupi vsaj dve, tudi če tega nima zapisanega na nakupovalnem listku. Sestavite funkcijo dodaj_strucke(kosarice), ki sestavi in vrne nov slovar, v katerem ima vsak kupec poleg vsega, kar že ima, še vsaj po dve francoski štručki. Zgled:

>>> kosarice = {'Janez': {'banana': 5, 'jogurt': 7}, 'Mojca': {'francoska štručka': 7}}
>>> dodaj_strucke(kosarice)
{'Janez': {'jogurt': 7, 'francoska štručka': 2, 'banana': 5},
 'Mojca': {'francoska štručka': 7}}

Uradna rešitev

def dodaj_strucke(kosarice):
    '''Vrne slovar z dodanimi francoskimi stručkami'''
    s_struckami = {} 
    for kupec, nakup_prej in kosarice.items(): # za vsakega kupca vzamemo njegov nakupovalni listek
        s_struckami[kupec] = dict(nakup_prej) # dodamo kupca v nov slovar
        nakup = s_struckami[kupec] # nakupni listek tega kupca
        nakup['francoska štručka'] = nakup.get('francoska štručka', 2) # ohranimo fr. stručke, ali pa dodamo 2
    return s_struckami # nakupovalni listki vseh kupcev

2. podnaloga

Šef trgovine je pripravil cenik vseh izdelkov, ki jih trgovinica prodaja. Vsak izdelek ima par podatkov in sicer bruto ceno in davčno stopnjo. Napišite funkcijo neto_in_davek(cenik, davcne_stopnje), ki sestavi in vrne nov slovar, v katerem bo par podatkov in sicer: neto cena izdelka in davek, ki ga mora trgovinica plačati držav, če so trenutno veljavne podane davčne stopnje. Pri tem vse cene in davke (x) zaokroži z round(x,5). Zgled:

>>> cenik = {'voda': (0.25, 'N'), 'sok': (1.20, 'V'), 'znamka A': (0.23, 'O')}
>>> neto_in_davek(cenik, {'V': 20, 'N': 8.5, 'O': 0})
{'sok': (1.0, 0.2), 'voda': (0.23041, 0.01959), 'znamka A': (0.23, 0.0)}

Uradna rešitev

def neto_in_davek(cenik, davcne_stopnje):
    '''neto cena in davki'''
    neto_davek = {}
    for izdelek in cenik: 
        bruto, stopnja = cenik[izdelek]
        neto = round(bruto / (1 + davcne_stopnje[stopnja] / 100), 5)
        neto_davek[izdelek] = (neto, round(bruto - neto,5))
    return neto_davek

3. podnaloga

Sestavite funkcijo cene_z_ddv(kosarice, cenik, davcneStopnje), ki dobi slovar nakupov in cenik (v originalni obliki), ter sestavi nov slovar nakupov, ki za vsak nakup vsebuje peterico: (kolicina, neto_na_enoto, neto_skupaj, davcna_stopnja, davek_skupaj) (Ne pozabite upoštevati, da vsak kupi vsaj dve štručki.)

Pri tem vse cene in davke (x) zaokroži z round(x,5).
Zgled:

>>> cenik = {'voda': (0.25, 'N'), 'sok': (1.25, 'V'), 'znamka A': (0.23, 'O'),
             'francoska štručka': (1, 'N')}
>>> kosarice = {'Janez': {'voda': 5, 'sok': 7},
                'Mojca': {'francoska štručka': 7}}
>>> cene_z_ddv(kosarice, cenik)
{'Janez': {'sok': (7, 1.0, 7.0, 'V', 1.75), 'voda': (5, 0.22875, 1.14375, 'N', 0.10625),
           'francoska štručka': (2, 0.915, 1.83, 'N', 0.17)},
 'Mojca': {'francoska štručka': (7, 0.915, 6.405, 'N', 0.595)}}

Uradna rešitev

def cene_z_ddv(kosarice, cenik, davcneStopnje):
    ''' '''
    nakupi_ddv = dict()
    ddv_cenik = neto_in_davek(cenik, davcneStopnje) # izvemo neto cene in zneske davkov
    kosarice_s_struckami = dodaj_strucke(kosarice) # po potrebi dodamo štručke
    for kupec, nakup in kosarice_s_struckami.items():
        nakupi_ddv[kupec] = dict() # slovar za tega kupca
        for artikel, kolicina in nakup.items():
            neto_na_enoto = round(ddv_cenik[artikel][0], 5)
            neto_skupaj = round(neto_na_enoto * kolicina, 5)
            ddv_skupaj = round(ddv_cenik[artikel][1] * kolicina, 5)
            nakupi_ddv[kupec][artikel] = (kolicina, neto_na_enoto, neto_skupaj, cenik[artikel][1], ddv_skupaj)
    return nakupi_ddv

Slovarji II


Zbiranje osebnih podatkov

V nekem podjetju zbirajo podatke o osebah, ki brskajo po medmrežju. Podatki so shranjeni v slovarjih (en slovar za vsako osebo). Ključ v takem slovarju je lastnost, vrednosti pa podatek o tej lastnosti za določeno osebo. Primeri takšnih slovarjev:

oseba1 = {'ime': 'Božidar', 'telefon': '031918211',
          'obiskane spletne strani': ['facebook.com', 'google.com']}
oseba2 = {'naslov': 'Dunajska 105', 'številka noge': 42,
          'prijatelji': ['Marko', 'Ana']}

1. podnaloga

Sestavite funkcijo podatek(oseba, lastnost), ki vrne podatek o lastnosti lastnost, ki ga imamo v slovarju oseba. Funkcija naj vrne None, če se ta podatek v slovarju ne nahaja. Primer:

>>> podatek({'ime': 'Božidar', 'naslov': 'Dunajska 105'}, 'naslov')
'Dunajska 105'
>>> podatek({'ime': 'Božidar', 'naslov': 'Dunajska 105'}, 'prijatelji')
None

Pri tem ne smeš uporabiti get!

Uradna rešitev

def podatek(oseba, lastnost):
    '''Vrne podatek o lastnosti lastnost oz. None'''
    if lastnost not in oseba:
        return None
    return oseba[lastnost]

2. podnaloga

Sestavite funkcijo podatekGet(oseba, lastnost), ki vrne podatek o lastnosti lastnost, ki ga imamo v slovarju oseba. Funkcija naj vrne None, če se ta podatek v slovarju ne nahaja. Primer:

>>> podatekGet({'ime': 'Božidar', 'naslov': 'Dunajska 105'}, 'naslov')
'Dunajska 105'
>>> podatekGet({'ime': 'Božidar', 'naslov': 'Dunajska 105'}, 'prijatelji')
None

Pri tem si nujno pomagajte z metodo get

Uradna rešitev

def podatekGet(oseba, lastnost):
    '''Vrne podatek o lastnosti lastnost oz. None'''
    return oseba.get(lastnost, None)

3. podnaloga

Za osebi oseba1 in oseba2 pravimo, da se ujemata v lastnosti lastnost, če za obe osebi poznamo podatka o tej lastnosti in sta podatka enaka. Če pa za obe osebi poznamo podatka in sta podatka različna, pa pravimo, da se osebi razlikujeta v lastnosti lastnost. Na primer, osebi

oseba1 = {'ime': 'Janez', 'priimek': 'Novak'}
oseba2 = {'ime': 'Jože', 'priimek': 'Novak', 'starost': 20}

se ujemata v lastnosti 'priimek' in razlikujeta v lastnosti 'ime'. (V lastnosti 'starost' se niti ne ujemata niti ne razlikujeta.)

Sestavite funkcijo ujemanje(oseba1, oseba2), ki pove, v koliko lastnostih se osebi oseba1 in oseba2 ujemata in v koliko lastnostih se razlikujeta. Rezultat naj funkcija vrne kot seznam z dvema elementoma. Primer:

>>> ujemanje({'ime': 'Janez', 'priimek': 'Novak'},
             {'ime': 'Jože', 'priimek': 'Novak', 'starost': 20})
[1, 1]

Uradna rešitev

def ujemanje(oseba1, oseba2):
    '''V koliko lastnostih se osebi ujemata in razlikujeta'''
    ujema = 0
    razlikuje = 0
    for lastnost in oseba1: # preverimo vse lastnosti prve osebe
        if lastnost in oseba2: # če gre za skupno lastnost
            if oseba1[lastnost] == oseba2[lastnost]:
                ujema += 1
            else:
                razlikuje += 1
    return [ujema, razlikuje]

4. podnaloga

Dva različna slovarja oseba1 in oseba2 lahko predstavljata isto osebo. To se zgodi, če se slovarja ne razlikujeta v več kot 1 lastnosti in se ujemata vsaj v 3 lastnostih ali če sta v obeh slovarjih lastnosti ime in priimek enaki. Na primer, slovarja

oseba1 = {'ime': 'Janez', 'priimek': 'Novak', 'telefon': '031123234',
          'starost': 90}
oseba2 = {'ime': 'Janez', 'priimek': 'Novak', 'davčna': '43424140'}

predstavljata isto osebo, prav tako slovarja

oseba1 = {'ime': 'Janez', 'priimek': 'Novak', 'telefon': '031123234',
          'starost': 90}
oseba2 = {'ime': 'Luka', 'priimek': 'Novak', 'starost': 90,
          'davčna': '43424140', 'telefon': '031123234'}

Sestavite funkcijo ista(oseba1, oseba2), ki preveri, ali slovarja predstavljata isto osebo. Na primer, za zgornja primera mora funkcija vrniti True.

Uradna rešitev

def ista(oseba1, oseba2):
    '''Ali gre ta isto osebo'''
    ujemata, razlikujeta = ujemanje(oseba1, oseba2) # uporaba funkcije prejšnje naloge
    je_ista = ujemata >= 3 and razlikujeta <= 1
    if je_ista :
        return True # dovolj je že ujemanje v lastnostih!
    # če ni ujemanja v lastnostih, preverimo ime in priimek
    if 'ime' in oseba1 and 'ime' in oseba2 and 'priimek' in oseba1 and 'priimek' in oseba2:
        je_ista = (oseba1['ime'] == oseba2['ime'] and oseba1['priimek'] == oseba2['priimek'])
    return je_ista

5. podnaloga

V seznamu slovarjev, ki predstavljajo osebe, se lahko zgodi, da več slovarjev predstavlja isto osebo (isto v smislu prejšnje podnaloge). V takem primeru pravimo, da so ti slovarji podvojeni.

Sestavite funkcijo podvojeni(s), ki vrne seznam vseh podvojenih slovarjev iz seznama s. Slovarji naj bodo razvrščeni v istem vrstnem redu kot v seznamu s. Primer:

>>> podvojeni([{'ime': 'Jan', 'priimek': 'Dan', 'naslov': 'Jadranska 21'},
               {'ime': 'Jan', 'priimek': 'Dan', 'naslov': 'Jadranska 19'},
               {'ime': 'Žan', 'priimek': 'Dan', 'naslov': 'Jadranska 21'},
               {'ime': 'Žan', 'priimek': 'Noč', 'naslov': 'Jamova 25'}])
[{'ime': 'Jan', 'priimek': 'Dan', 'naslov': 'Jadranska 21'},
 {'ime': 'Jan', 'priimek': 'Dan', 'naslov': 'Jadranska 19'}]

Uradna rešitev

def jeOsebaPodvojena(osb, s):
    '''Ali se oseba osb pojavi v seznamu s vsaj dvakrat'''
    kolikoIstih = 0
    for oseba2 in s: # pogledamo vse osebe slovarja s
        if ista(osb, oseba2): # gre za isto osebo po kriterijih prej
            kolikoIstih += 1
    return kolikoIstih >= 2

def podvojeni(s):
    '''Seznam vseh podvojenih oseb'''
    seznam = []
    for oseba in s: # vsako osebo 
        if jeOsebaPodvojena(oseba, s): # prevrimo, če je v seznamu oseba vsaj 2x
            seznam.append(oseba)
    return seznam

Kuhamo in pečemo

Sestavine, ki jih potrebujemo za nek recept, opišemo s slovarjem, v katerem so ključi sestavine, vrednosti pa količine, ki jih potrebujemo.

1. podnaloga

Sestavite funkcijo pomnozi(recept, faktor), ki sestavi in vrne nov recept. Ta naj vsebuje iste sestavine kot recept recept, le da so vse količine v njem pomnožene z danim faktorjem.

>>> pomnozi({'jajca': 4, 'moka': 500}, 2)
{'jajca': 8, 'moka': 1000}

Uradna rešitev

def pomnozi(recept, faktor):
    '''Vrne nov recept, spremenjen za faktor'''
    novR = dict()
    for sestavina, kolicina in recept.items():
        novR[sestavina] = kolicina * faktor
    return novR

def pomnoziV2(recept, faktor):
    '''Vrne nov recept, spremenjen za faktor'''
    # uporabimo izpeljane slovarje (dict comprehension)
    return {sestavina: kolicina * faktor for sestavina, kolicina in recept.items()}

2. podnaloga

Sestavite funkcijo imamoSestavine(recept, shramba), ki preveri, ali imamo v shrambi dovolj sestavin za dani recept. Sestavine, ki jih imamo v shrambi, so predstavljene s slovarjem na enak način kot sestavine v receptu.

Uradna rešitev

def imamoSestavine(recept, shramba):
    '''imamo v shrambi dovolj sestavin za dani recept'''
    for sestavina, kolicina in recept.items():
        if shramba.get(sestavina, -1) < kolicina: # uporaba get, da ne dobimo izjeme pri neobstoječih sestavinah
            return False # že če ena manjka, ne gre
    return True # vsega imamo dovolj!

3. podnaloga

Sestavite funkcijo potrebnoKupiti(recept, shramba), ki vrne slovar sestavin s pripadajočimi količinami, ki jih moramo še dokupiti, da bomo lahko skuhali jed po danem receptu.

>>> potrebnoKupiti({'jajca': 3, 'moka': 500}, {'moka': 1000, 'jajca': 6, 'sladkor': 1000})
{}
>>> potrebnoKupiti({'jajca': 3, 'moka': 500}, {'moka': 1000, 'sladkor': 1000})
{'jajca': 3}
>>> potrebnoKupiti({'jajca': 3, 'moka': 500}, {'moka': 100})
{'jajca': 2, 'moka': 400}

Uradna rešitev

def potrebnoKupiti(recept, shramba):
    '''vrne slovar sestavin s pripadajočimi količinami, ki jih moramo še dokupiti, da bomo
      lahko skuhali jed po danem receptu.'''
    kupiti = dict()
    for sestavina, kolicina in recept.items():
        razlika = kolicina - shramba.get(sestavina, 0) # če sestavine še nimamo, je enako, kot če jo imamo 0!
        if razlika > 0:
            kupiti[sestavina] = razlika
    return kupiti

Antipalindromi

1. podnaloga

Niz je antipalindrom, kadar ima znake na vseh mestih različne od svojega obrata. Tako je beseda mama antipalindrom, saj se noben znak ne ujema z znakom na istem mestu v besedi amam, beseda anketa pa ni antipalindrom, saj ima prvem in zadnjem mestu enako črko kot njen obrat atekna.

Sestavite funkcijo jeAntipalindrom(niz), ki vrne True, če je niz antipalindrom, in False če ni.

Uradna rešitev

def jeAntipalindrom(niz):
    '''Ali je niz antipalnidrom'''
    dolNiz = len(niz)
    if dolNiz % 2 == 1:  # vsi lihi nizi zagotovo niso antipalidromi!
        return False 
    for ind in range(dolNiz // 2): # preverimo prvih pol znakov (pazi 
        if niz[ind] == niz[dolNiz - 1 - ind] : # primerjamo znake začetnega in končnega dela
            return False       
    return True # vsi Testi so bili uspešni


def jeAntipalindromV2(niz):
    '''Ali je niz antipalnidrom'''
    # uporaba naprednejših metod
    # V dokumentaciji si oglej all in zip
    return all(z1 != z2 for z1, z2 in zip(niz, niz[::-1]))

2. podnaloga

Sestavite funkcijo vsebovaniAntipalindromi(niz), ki vrne množico vseh strnjenih podnizov niza niz, ki so antipalindromi.

Uradna rešitev

def vsebovaniAntipalindromi(niz):
    '''Vrne množico podnizov niza niz, ki so antipalindromi'''
    antipalindromi = set()
    # naredimo vse možne podnize
    for i in range(len(niz)):
        for j in range(i + 1, len(niz)+1):
            podniz = niz[i:j]
            # uporabimo funkcijo gornje naloge
            if jeAntipalindrom(podniz): # je anitiPalindrom, ga dodamo
                antipalindromi.add(podniz)
    return antipalindromi

Hatebook

Za razliko od običajnih družabnih omrežij, deluje nedružabno omrežje Hatebook tako, da si v omrežje vsak dodaja svoje sovražnike. Omrežje predstavimo s slovarjem, pri čemer so ključi osebe, vrednosti pa množice oseb, ki jih te osebe sovražijo.

1. podnaloga

Sestavite funkcijo seSovrazita(omrezje, oseba1, oseba2), ki vrne True, kadar osebi sovražita druga drugo, in False sicer.

Uradna rešitev

def seSovrazita(omrezje, oseba1, oseba2):
    '''Ali se osebi sovražita'''
    # oba ključa morata biti v seznamu vrednosti za drug ključ!
    return oseba1 in omrezje[oseba2] and oseba2 in omrezje[oseba1]

2. podnaloga

Sestavite funkcijo kdoSovrazi(omrezje, oseba), ki vrne množico oseb, ki v danem omrežju sovražijo dano osebo.

Uradna rešitev

def kdoSovrazi(omrezje, oseba):
    '''vrne množico oseb, ki v danem omrežju sovražijo dano osebo '''
    kdoSovraži = set()
    for sovrag, sovrazniki in omrezje.items():
        if oseba in sovrazniki:
            kdoSovraži.add(sovrag)
    return kdoSovraži

def kdoSovraziV2(omrezje, oseba):
    '''vrne množico oseb, ki v danem omrežju sovražijo dano osebo '''
    # uporaba izpeljanih množic (set comprehension)
    return {sovrag for sovrag, sovrazniki in omrezje.items()
            if oseba in sovrazniki}

3. podnaloga

Sestavite funkcijo nesrecniki(omrezje), ki vrne množico oseb, ki sovražijo same sebe.

Uradna rešitev

def nesrecniki(omrezje):
    '''Vrne množico oseb, ki sovražijo sebe'''
    mnNesrečnikov = set()
    for oseba, sovrazniki in omrezje.items():
        if oseba in sovrazniki:
            mnNesrečnikov.add(oseba)
    return mnNesrečnikov

def nesrecnikiV2(omrezje):
    '''Vrne množico oseb, ki sovražijo sebe'''
    # uporaba izpeljanih množic
    return {oseba for oseba, sovrazniki in omrezje.items()
            if oseba in sovrazniki}

4. podnaloga

Sestavite funkcijo najboljZadrti(omrezje), ki vrne množico vseh oseb, ki v danem omrežju sovražijo največ oseb.

Uradna rešitev

def najboljZadrti(omrezje):
    '''vrne množico vseh oseb, ki v danem omrežju sovražijo največ oseb'''
    najvec = 0
    for sovrazniki in omrezje.values():
        najvec = max(len(sovrazniki), najvec) # večjega od trenutnega kandidata in naj doslej
    # in še izpeljanih množic
    mnZadrtih = set()
    for oseba, sovrazniki in omrezje.items():
        if len(sovrazniki) == najvec:
            mnZadrtih.add(oseba)
    return mnZadrtih

def najboljZadrtiV2(omrezje):
    '''vrne množico vseh oseb, ki v danem omrežju sovražijo največ oseb'''
    #uporaba izpeljaniega ponovnika
    najvec = max(len(sovrazniki) for sovrazniki in omrezje.values())
    # in še izpeljanih množic
    return {oseba for oseba, sovrazniki in omrezje.items()
            if len(sovrazniki) == najvec}

Unija slovarjev

1. podnaloga

Sestavite funkcijo unijaSlovarjev(seznam_slovarjev), ki bo iz danega seznama slovarjev sestavila nov slovar, ki bo predstavljal unijo posameznih slovarjev. Vrednosti ključev v uniji slovarjev naj bodo seznami vrednosti, ki pripadajo enakim ključem iz vhodnih slovarjev. Vrednosti naj bodo v seznamih zapisane v enakem vrstnem redu, kot nastopajo v seznamu slovarjev. Zgled:

>>> unija_slovarjev([{1: 2, 5: 0}, {2: 3, 5: 6, 7: 3}, {2: 3, 8: 1, 5: 4}])
{1: [2], 5: [0, 6, 4], 2: [3, 3], 7:[3], 8:[1]}

Uradna rešitev

def unijaSlovarjev(seznamSlovarjev):
    '''Vrnemo slovar, ki je unija slovarjev'''
    novSlovar = dict()
    for slovar in seznamSlovarjev: # obdelamo vse slovarje
        for kljuc in slovar:
            if kljuc in novSlovar: # če se je ta ključ že pojavil
                novSlovar[kljuc].append(slovar[kljuc]) # dodamo njegovo vrednost
            else:
                novSlovar[kljuc] = [slovar[kljuc]] # sicer pa je to nov ključ
    return novSlovar

Električne naprave

Patrik je za vse električne naprave, ki jih ima doma, ugotovil ime proizvajalca in podatke shranil v slovar. Zgled takega slovarja:

naprave = {
    'hladilnik': 'Gorenje', 'pralni stroj': 'Whirlpool',
    'kavni mlinček': 'Bosch', 'mikrovalovna pečica': 'Gorenje',
    'parni čistilnik': 'Philips', 'laserski tiskalnik': 'Lexmark',
    'sušilnik las': 'Philips'
}

Njegov sosed Darko pa je iz čistega dolgčasa sestavil slovar, v katerem so ključi naprave, vrednosti pa kategorije, kamor te naprave sodijo. Zgled:

kategorije = {
    'hladilnik': 'bela tehnika', 'parni čistilnik': 'sesalniki',
    'grelnik vode': 'kuhinjski aparati', 'pralni stroj': 'bela tehnika',
    'sušilnik las': 'nega las', 'mikrovalovna pečica': 'kuhinjski aparati', 
    'laserski odstranjevalec dlačic': 'brivniki in depilatorji'
}

1. podnaloga

Patrik in Darko bi rada združila podatke in za vsako blagovno znamko, ki se pojavi v Patrikovem slovarju, ugotovila, katere kategorije naprav "pokriva" glede na Darkov slovar. Napišite funkcijo blagovne_znamke(naprave, kategorije), ki vrne slovar, kjer so ključi imena proizvajalcev, njihove pripadajoče vrednosti pa so množice kategorij, s katerimi se proizvajalec ukvarja.

Naj bosta slovarja naprave in kategorije enaka kot zgoraj. Zgled:

>>> blagovne_znamke(naprave, kategorije)
{'Lexmark': set(), 'Philips': {'sesalniki', 'nega las'}, 'Whirlpool': {'bela tehnika'},
 'Gorenje': {'bela tehnika', 'kuhinjski aparati'}, 'Bosch': set()}

Bodite pozorni na to, da se lahko v Patrikovem slovarju pojavijo tudi naprave, ki jih Darko nima v svojem slovarju, in obratno.

Uradna rešitev

def blagovne_znamke(naprave, kategorije):
    '''vrne slovar, kjer so ključi imena proizvajalcev, njihove pripadajoče vrednosti
       pa so množice kategorij, s katerimi se proizvajalec ukvarja.'''
    blagZnamke = dict()
    for naprava, znamka in naprave.items():
        if znamka not in blagZnamke:
            blagZnamke[znamka] = set() # nova znamka, začnemo s prazno množico
        if naprava in kategorije:
            blagZnamke[znamka].add(kategorije[naprava]) # dodamo vse ustrezne kategorije
    return blagZnamke

Ključ z največjo vrednostjo

Dan je slovar, ki slika ključe xi (ključi so nizi) v celoštevilske vrednosti vi, recimo

   {'foo':1, 'bar':7, 'baz':100, 'qux':20, 'quux':30}

1. podnaloga

Sestavi funkcijo ključZNajVrednostjo(sl), ki sprejme tak slovar in vrne tisti ključ xi, ki ima največjo pripadajočo vrednost vi. Če bi na primer poklicali funkcijo s prejšnjim slovarjem, bi dobili rezultat 'baz'. Če je slovar prazen, vrni None. Predpostavi, da je le en ključ z največjo vrednostjo.

Uradna rešitev

def ključZNajVrednostjo(sl):
    ''' vrne tisti ključ `xi`, ki ima največjo pripadajočo vrednost `vi`.'''
    if not sl:  # če je prazen
        return None
    # za kandidata vzamemo vrednost prvega iz slovarja
    for kl, vr in sl.items():
        najVr = vr
        najKljuč = 'Žal bom izginil'
        break  # rabimo le prvega
    # sedaj pa zares
    for kl, vr in sl.items():
        if vr >= najVr:  # našli smo boljšega
            najVr = vr
            najKljuč = kl
    return najKljuč

2. podnaloga

Sedaj pa spustimo omejitev, da obstaja le en ključ z največjo vrednostjo.

Sestavi funkcijo seznamKljučevZNajVrednostjo(sl), ki vrne po abecedi urejen seznam vseh ključev z največjo vrednostjo. Če je slovar prazen, vrni prazen seznam.

Uradna rešitev

def seznamKljučevZNajVrednostjo(sl):
    ''' vrne po abecedi urejen seznam tisti ključev `xi`, ki imajo
        največjo pripadajočo vrednost `vi`.'''
    if not sl:  # če je prazen
        return []
    # za kandidata vzamemo vrednost prvega iz slovarja - 1
    for kl, vr in sl.items():
        najVr = vr - 1
        najKljuč = []
        break  # rabimo le prvega
    # sedaj pa zares
    for kl, vr in sl.items():
        if vr > najVr:  # našli smo boljšega
            najVr = vr
            najKljuč = [kl]
        elif vr == najVr:  # našli smo enako dobrega
            najKljuč.append(kl)
    return sorted(najKljuč)

Ljubezen nam je vsem v pogubo I

prosto po Tavčarju ;-) ali po Galetu

Socialno omrežje zaljubljenosti podamo s slovarjem, ki ime osebe preslika v seznam vseh, v katere je oseba zaljubljena (ena oseba je lahko zaljubljena v več oseb). Na primer, slovar

s = {'Ana' : ['Bine','Cene'],
     'Bine' : [],
     'Cene' : ['Bine', 'Cene'],
     'Davorka' : ['Davorka'],
     'Eva' : ['Bine']}

nam pove, da je Ana zaljubljena v Bineta in Cenete, Bine ni zaljubljen, Cene ljubi Bineta in samega sebe, Davorka samo sebe in Eva Bineta.

1. podnaloga

Sestavite funkcijo narcisoidi(d), ki sprejme slovar zaljubljenih in vrne po abecedi urejen seznam tistih, ki ljubijo same sebe.

Uradna rešitev

def narcisoidi(d):
    '''Vrne po abecedi urejen seznam narcisoidov'''
    sezNar = []
    for oseba in d:
        if oseba in d[oseba]: # ta bo pravi!
            sezNar.append(oseba)
    return sorted(sezNar)

def narcisoidiV2(d):
    '''Vrne po abecedi urejen seznam narcisoidov'''
    # uporabimo izpeljane sezname (list comprehension)
    return sorted([oseba for oseba in d if oseba in d[oseba]])

2. podnaloga

Sestavite funkcijo ljubljeni(d), ki sprejme slovar zaljubljenih in vrne po abecedi urejen seznam tistih, ki so ljubljeni.

Uradna rešitev

def ljubljeniV2(d):
    '''Vrne po abecedi urejen _seznam_ tistih, ki so ljubljeni'''
    # naredimo s seznamom
    seznamOseb =  list()
    for oseba in d: # za vse osebe
        for ljubljen in d[oseba]: # dodamo njihove ljubljene (če je potrebno)
            if ljubljen not in seznamOseb: # če še niso
                seznamOseb.append(ljubljen)
    return sorted(seznamOseb)

def ljubljeni(d):
    '''Vrne po abecedi urejen _seznam_ tistih, ki so ljubljeni'''
    mnLjubljenih =  set()
    for oseba in d: # za vse osebe
        mnLjubljenih.update(d[oseba]) # dodamo vse ljubljene te osebe
    return sorted(mnLjubljenih) # sorted vrne urejen seznam - argument je lahko poljubni iterator

3. podnaloga

Sestavite funkcijo pari(d), ki sprejme slovar zaljubljenih in vrne seznam vseh parov, ki so srečno ljubljeni. Vsak par naj se pojavi samo enkrat in sicer tako, da je sta zaljubljenca našteta po abecedi. Na primer, če sta Ana in Bine zaljubljena, dodamo par ('Ana','Bine').

Uradna rešitev

def pari(d):
    '''Po abecedi urejen par srečno ljubljenih - torej takioh, ki ljubijo osebo, ki ljubi njih'''
    seznamParov = []
    for oseba in d:
        # za vsako osebo, ki jo ljubi, pogledamo, če ta ljubi njega
        for ljubljeni in d[oseba]:
            if oseba in d[ljubljeni] :
                # ga bomo dodali
                urejenPar = tuple(sorted([oseba,ljubljeni]))
                # a le , če že ni notri
                if urejenPar not in seznamParov:  # namig - namesto seznama bi lahko uporabili množico in bi ta stavek if odpadel
                    seznamParov.append(urejenPar)
    return sorted(seznamParov)

4. podnaloga

Sestavite funkcijo ustrezljivi(oseba, d), ki sprejme ime osebe ter slovar zaljubljenih, vrne pa po abecedi urejen seznam vseh ljudi, ki so do dane osebe še posebej ustrežljivi. Posebej ustrežljivi so seveda za to, ker so bodisi zaljubljeni v dano osebo, bodisi so zaljubljeni v osebo, ki je posebej ustrežljiva do nje, in tako naprej.

Na primer, če imamo slovar

s = {'Ana' : ['Bine', 'Cene'],
     'Bine' : ['Ana'],
     'Cene' : ['Bine'],
     'Davorka' : ['Davorka'],
     'Eva' : ['Bine']}

so do Ceneta posebej ustrežljivi Ana (ki je zaljubljena vanj), Bine (ki je zaljubljen v Ano), Eva (ki je zaljubljena v Bineta), ter seveda Cene, ki je zaljubljen v Evo.

Uradna rešitev

def ustrezljiviV2(oseba, d):
    '''vrne po abecedi urejen _seznam_ vseh ljudi, ki so do dane osebe še posebej ustrežljivi'''
    # seznam, v katerega nabiramo ustrežljive osebe
    ustrezljivi = []
    # najprej dodamo tiste, ki ljubijo prvo osebo
    dodani = [o for o in d if oseba in d[o]]
    # dokler smo koga dodali, dodajamo ustrežljive
    while dodani:
        for nekdo in dodani:
            if nekdo not in ustrezljivi: # če ga še ni noter
                ustrezljivi.append(nekdo)
        # sedaj pa dodajamo tiste, ki ljubijo nazadnje dodane osebe
        dodani = [o for o in d for dodan in dodani
                  if dodan in d[o] and o not in ustrezljivi]
    return sorted(ustrezljivi)

def ustrezljivi(oseba, d):
    '''vrne po abecedi urejen _seznam_ vseh ljudi, ki so do dane osebe še posebej ustrežljivi'''
    # seznam, v katerega nabiramo ustrežljive osebe
    ustrezljivi = []
    # najprej dodamo tiste, ki ljubijo prvo osebo
    dodani = []
    for nekdo in d:
        if oseba in d[nekdo]:
            dodani.append(nekdo)
    # dokler smo koga dodali, dodajamo ustrežljive
    while dodani:
        for nekdo in dodani:
            if nekdo not in ustrezljivi: # če ga še ni noter
                ustrezljivi.append(nekdo)
        # sedaj pa dodajamo tiste, ki ljubijo nazadnje dodane osebe
        noviDodani = []
        for o in d:
            for nekdo in dodani:
                if nekdo in d[o] and o not in ustrezljivi:
                    noviDodani.append(o)
        dodani = noviDodani[:]
    return sorted(ustrezljivi)

Rimski imperij vrača udarec

Pri tej nalogi boste napisali funkcijo, ki bo za dano število vrnila niz z ustrezno rimsko številko. Nato boste napisali še funkcijo, ki bo rimsko številko spremenila nazaj v število.

Preden začnete, si na oglejte članek o rimskih številkah na Wikipediji: Rimske številke.

1. podnaloga

Napišite funkcijo vRimsko(stevilo), ki kot argument dobi celo število med 1 in 3999 (vključno z 1 in 3999). Funkcija naj sestavi in vrne niz, ki vsebuje rimsko številko, ki predstavlja število stevilo. Zgled:

>>> vRimsko(2013)
'MMXIII'

Uradna rešitev

def vRimsko(stevilo):
    '''Iz 'običajnega' zapisa števila med 1 in 3999 naredimo rimski zapis števila
    '''
    tabelaRimskih = [ ['', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX'],
                      ['', 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC'],
                      ['', 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM'],
                      ['', 'M', 'MM', 'MMM']]
    rimsko = ''
    for i in range(4):
        rimsko = tabelaRimskih[i][stevilo%10] + rimsko
        stevilo //= 10
    return rimsko

2. podnaloga

Napišite še funkcijo vArabsko(niz), ki bo dobila niz niz, ki predstavlja rimsko številko. Funkcija naj naredi ravno obratno kot funkcija vRimsko, tj. vrne naj ustrezno število, ki ga predstavlja dana rimska številka. Če niz ne predstavlja veljavnege rimske številke, naj funkcija vrne None. Zgled:

>>> vArabsko('MMXIII')
2013

Nasvet: Najprej sestavi slovar, ki kot ključe vsebuje rimske številke, kot vrednosti pa ustrezna števila. Potem le uporabi ta slovar.

Uradna rešitev

def vArabsko(niz):
    '''Iz rimskega zapisa naredimo arabski zapis števila
       Če ne gre, vrnemo None.
    '''
    tabelaRimskih = [ ['', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX'],
                  ['', 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC'],
                  ['', 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM'],
                  ['', 'M', 'MM', 'MMM']]
    slovarRimskih = {vRimsko(i):i for i in range(1, 4000)}

    if niz not in slovarRimskih:
        return None
    return slovarRimskih[niz]

Rodovniki

Ukvarjali se bomo z rodovniki (Celjskih grofov in drugih). Rodovnik imamo podan kot slovar, kjer je ključ ime "glave rodbine" vrednost pa seznam imen otrok. Recimo:

rodovnik =
 {'Ulrik I.': ['Viljem'], 'Margareta': [], 'Herman I.': ['Herman II.', 'Hans'],
  'Elizabeta II.': [], 'Viljem': ['Ana Poljska'], 'Elizabeta I.': [],
  'Ana Poljska': [], 'Herman III.': ['Margareta'], 'Ana Ortenburška': [],
  'Barbara': [], 'Herman IV.': [], 'Katarina': [], 'Friderik III.': [],
  'Herman II.': ['Ludvik', 'Friderik II.', 'Herman III.', 'Elizabeta I.', 'Barbara'],
  'Ulrik II.': ['Herman IV.', 'Jurij', 'Elizabeta II.'], 'Hans': [], 'Ludvik': [],
  'Friderik I.': ['Ulrik I.', 'Katarina', 'Herman I.', 'Ana Ortenburška'],
  'Friderik II.': ['Friderik III.', 'Ulrik II.'], 'Jurij': []}
 rodovnik['Friderik II.']

nam torej vrne

  ['Friderik III.', 'Ulrik II.']

1. podnaloga

Število otrok

Sestavi funkcijo kolikoOtrok(ime, rodovnik), ki za dano ime in rodovnik vrne število otrok te osebe, oz None, če osebe ni v rodovniku.

Uradna rešitev

def kolikoOtrok(ime, rodovnik) :
    ''' koliko otrok ima oseba z imenom ime '''
    if ime not in rodovnik:
        return None
    return len(rodovnik[ime])

2. podnaloga

Število potomcev

Sestavi funkcijo kolikoPotomcev(ime, rodovnik), ki za dano ime in rodovnik vrne število potomcev te osebe. Če osebe ni v rodovniku, vrni None

Uradna rešitev

def kolikoPotomcev(ime, rodovnik) :
    ''' koliko potomcev ima oseba z imenom ime '''
    if ime not in rodovnik: # če je slučajno ni
        return None
    otroci = rodovnik[ime]
    kolikoJihJe = len(otroci) # potomci so otroci
    for oseba in otroci: # in vsi potomci otrok
        kolikoJihJe += kolikoPotomcev(oseba, rodovnik)
    return kolikoJihJe

3. podnaloga

Je v rodbini?

Sestavi funkcijo jeTaVRodbini(ime, glavaRodbine, rodovnik), ki ugotovi, ali je oseba z imenom ime v rodbini osebe glavaRodbine.

Uradna rešitev

def jeTaVRodbini(ime, glavaRodbine, rodovnik) :
    ''' Ali je oseba z imenom ime v rodbini osebe glavaRodbine '''
    if ime == glavaRodbine: # če je to že kar "šef"
        return True
    # je morda med otroci
    otroci = rodovnik[glavaRodbine]
    if ime in otroci :  # pravzaprav je ta stavek if odveč. Zakaj? Premisli!
        return True
    # je morda v rodbini otrok?
    for oseba in otroci :
        if jeTaVRodbini(ime, oseba, rodovnik) :
            return True
    # ni bil nikjer, torej ...
    return False

4. podnaloga

Kdo se podpisuje najdlje časa?

Sestavi funkcijo najdaljsiPodpis(glavaRodbine, rodovnik), ki ugotovi, kdo v rodbini osebe glavaRodbine ima najdaljše ime za podpis (torej kompletno ime).

Uradna rešitev

def najdaljsiPodpis(glavaRodbine, rodovnik) :
    ''' kdo v rodbini osebe glavaRodbine ima najdaljše ime. '''
    najIme = glavaRodbine  # morda je to kar glava rodbine
    # je morda v rodbini otrok?
    otroci = rodovnik[glavaRodbine]
    for oseba in otroci :
        najMedOsebo = najdaljsiPodpis(oseba, rodovnik)
        if len(najMedOsebo) > len(najIme) :
            najIme = najMedOsebo
    return najIme

5. podnaloga

Kdo ima najkrajše ime?

Sestavi funkcijo najkrajseIme(glavaRodbine, rodovnik), ki ugotovi, kdo v rodbini osebe glavaRodbine ima najkrajše ime. (šteje samo krstno ime, brez "Ortenburga" in "Celja" ter brez številk)?

Uradna rešitev

def najkrajseIme(glavaRodbine, rodovnik) :
    ''' kdo v rodbini osebe glavaRodbine ima najkrajse ime. '''
    # morda je to kar glava rodbine
    najIme = glavaRodbine.split()[0]  # izrabimo dejstvo, da je "pravo" ime takoj na začetku ...
    # je morda v rodbini otrok?
    otroci = rodovnik[glavaRodbine]
    for oseba in otroci :
        najMedOsebo = najkrajseIme(oseba, rodovnik)
        if len(najMedOsebo) < len(najIme) :
            najIme = najMedOsebo
    return najIme

6. podnaloga

Globina rodbine

"Globino" rodbine definiramo tako: če nekdo nima otrok, je globina njegove rodbine 1. Če ima otroka, ta pa nima vnukov (ali celo več otrok, ti pa nimajo vnukov), je globina rodbine 2. Če nekdo ima vnuke, vendar nobenega pravnuka, je globina njegove rodbine 3.

Sestavi funkcijo globina(glavaRodbine, rodovnik), ki vrne globino rodbine osebe glavaRodbine v rodovniku rodovnik

Uradna rešitev

def globina(glavaRodbine, rodovnik):
    '''Kakšna je globina rodbine osebe glavaRodbine'''
    # če nima otrok, je na globini 1
    if rodovnik[glavaRodbine] == []:
        return 1
    # ima vsaj enega otroka
    # potrebujemo globine vseh otrok te glaveRodbine
    glOtrok = []
    for otrok in rodovnik[glavaRodbine]:
        # koliko je globina za tega otroka
        glOtroka = globina(otrok, rodovnik)
        glOtrok.append(glOtroka)
    # zanima nas le največja
    najGlOtrok = max(glOtrok)
    # globina glave rodbine je za 1 večja od tega maksimuma
    return 1 + najGlOtrok

def globinaV2(glavaRodbine, rodovnik):
    '''Kakšna je globina rodbine osebe glavaRodbine
       brez uporabe funkcije max '''
    # če nima otrok, je na globini 1
    if rodovnik[glavaRodbine] == []:
        return 1
    # ima vsaj enega otroka
    # potrebujemo maksimalno globino od vseh otrok te glaveRodbine
    najGlOtrok = 0 # ker so vse globine poz. števila, je začetni kandidat lahko 0
    for otrok in rodovnik[glavaRodbine]:
        # koliko je globina za tega otroka
        glOtroka = globinaV2(otrok, rodovnik)
        if glOtroka > najGlOtrok: # našli smo boljšega kandidata
            najGlOtrok = glOtroka
    # globina glave rodbine je za 1 večja od tega maksimuma
    return 1 + najGlOtrok

def globinaV3(oseba, rodovnik):
    otroci = rodovnik[oseba]
    if otroci: # če imamo otroke, je naša globina za 1 večja od naj globine naših otrok
        return 1 + max(globinaV3(otrok, rodovnik) for otrok in otroci)
    else: # drugače smo na globini 1
        return 1

Slovarji III


Kraji in reči

V nalogi se bomo igrali s slovarji, ki opisujejo, kje se nahaja kakšen kraj. Slovar je lahko recimo, tak

svet = {
    "Evropa": {
        "Slovenija": {
            "Gorenjska": {
                "Kranj": {},
                "Radovljica": {},
                "Zali log": {},
            },
            "Štajerska": {
                "Maribor": {},
                "Celje": {}
            },
            "Osrednja": {
                "Ljubljana": {
                    "Vič": {
                        "FMF": {
                            "2.02": {
                                "tretja vrsta desno": {
                                    "peti stol z desne": {
                                        "Benjamin": {}
                                    }
                                }
                            }
                        }
                    },
                    "Šiška": {}
                }
            }
        },
        "Nemčija": {
            "Bavarska": {
                "Munchen": {}
            },
            "Berlin": {}
        },
    },
    "Amerika": {
        "ZDA": {
            "Teksas": {
                "Houston": {},
                "Austin": {}
            },
            "Kalifornija": {
                "San Francisco": {}
            },
            "Anchorage": {}
        },
        "Kanada": {}
    },
    "Azija": {
        "Osaka": {}
    }
   }

Ključi slovarja so torej nizi, vsebine pa so slovarji, ki so lahko tudi prazni. Namig: Oglejte si kako preveriti, ali je neka stuktura prazna,

1. podnaloga

Napiši funkcijo vrniVse(kraji), ki kot argument dobi slovar, kakršen je gornji, in vrne po abecedi urejen seznam vseh krajev in reči v njem.

Uradna rešitev

def vrniVse(kraji):
    '''po abesedi urejen seznam vseh krajev in reči v slovarju '''
    if not kraji : # če je slovar prazen
        return []
    seznamVseh = []
    for el in kraji:
        # dodamo ključ
        seznamVseh.append(el)
        # in vse elemente slovarja, ki je vrednost
        seznamVseh += vrniVse(kraji[el])
    return sorted(seznamVseh)

2. podnaloga

Napiši funkcijo prestej(kraji), ki vrne število vseh krajev in reči, ki se pojavijo v podanem slovarju.

Uradna rešitev

def prestej(kraji):
    '''koliko je stvari v slovarju kraji'''
    # v praznem slovarju ni stvari
    if len(kraji) == 0:
        return 0
    # pregledamo vse elemente slovarja in prištejemo 1 za ključ
    # in ustrezno število stvari v pripdajočem podslovarju
    koliko = 0
    for slovar in kraji.values():
        koliko += 1 # za ključ!
        kolikoVPodsl = prestej(slovar)
        koliko += kolikoVPodsl
    return koliko

# načeloma je prvi stavek if odveč, zato lahko napišemo
def prestejV2(kraji):
    '''koliko je stvari v slovarju kraji'''
    # pregledamo vse elemente slovarja in prištejemo 1 za ključ
    # in ustrezno število stvari v pripdajočem podslovarju
    koliko = 0
    for slovar in kraji.values():
        koliko += 1 # za ključ!
        kolikoVPodsl = prestejV2(slovar)
        koliko += kolikoVPodsl
    return koliko

3. podnaloga

Denimo, da imamo slovar (kot sta kraji in svet), ki kot vrednosti spet vsebuje slovarje. Sestavi funkcijo najSlovar(slovar), ki vrne tisti slovar, ki vsebuje največ ključev.

Uradna rešitev

def najSlovar(slovar) :
    ''' V slovarju slovarjev vrnemo slovar, ki vsebuje največ ključev '''
    if not slovar : # če je slovar prazen
        return slovar
    #kandidat za največjega je kar ta slovar
    kanNajSlovar = slovar
    # morda se v kakšni vrednosti skriva večji slovar
    for kljuc in slovar:
        # pogledamo najSlovar v ustrezni vrednosti
        vrednost = slovar[kljuc]
        kandidat = najSlovar(vrednost)
        if len(kandidat) > len(kanNajSlovar) : # našli smo boljšega
            kanNajSlovar = kandidat
    return kanNajSlovar

4. podnaloga

Sestavi funkcijo jeVSlovarju(slovar, nekaj), ki ugotovi, ali je v slovarju slovar tudi niz nekaj (bodisi kot ključ, bodisi kot vrednost)

Uradna rešitev

def jeVSlovarju(slovar, nekaj) :
    ''' Ali je v slovarju slovar tudi niz nekaj (bodisi kot ključ, bodis kot vrednost)'''
    if not slovar : # če je slovar prazen
        return False
    # pregledamo vse ključe
    for kljuc in slovar:
        # pogledamo če je morda to ta ključ
        if kljuc == nekaj:
            return True
        # je morda v vrednosti, ki je slovar
        if jeVSlovarju(slovar[kljuc], nekaj):
            return True
    # nekaj ni bil ne ključ, en v kateri od vrenosti, torej je ni!
    return False

5. podnaloga

Sestavi funkcijo vrniVrednost(slovar, nekaj), ki vrne tisto vrednost iz slovarja slovarjev slovar, ki pripada ključu nekaj Če pa v slovarju slovarjev nekaj ni ključ, naj vrne None

Uradna rešitev

def vrniVrednost(slovar, nekaj) :
    ''' Ali je v slovarju slovar tudi niz nekaj (bodisi kot ključ, bodis kot vrednost)'''
    if not slovar : # če je slovar prazen
        return None
    # pregledamo vse ključe
    for kljuc in slovar:
        # pogledamo če je morda to ta ključ
        if kljuc == nekaj:
            return slovar[kljuc]
        # je morda v vrednosti, ki je slovar
        kaj = vrniVrednost(slovar[kljuc], nekaj)
        if kaj != None : # torej je bilo
           return kaj
    # nekaj nismo našli v nobenem slovarju, torej je ni!
    return None

Veliki šef in njegovi podrejeni

V nekem uspešnem podjetju ima skoraj vsak zaposleni svojega šefa. Seveda imajo tudi šefi svoje šefe in ti spet svoje šefe itn. Dobili smo podatke o hierarhiji v podjetju in ugotovili, da velja naslednje:

  • Vsaka oseba ima kvečjemu enega šefa.
  • En šef ima lahko pod seboj več zaposlenih.
  • Vsem, ki so nad nami (naš šef, šef našega šefa itd.), pravimo nadrejeni.
  • Vsem, ki so pod nami (sami smo njihov šef, ali pa smo šef njihovega šefa itd.), pravimo podrejeni.
  • Nihče ni sam svoj šef in nihče ni samemu sebi podrejen.

Napisali bomo nekaj funkcij, s katerimi bomo preučili razmere v podjetju.

1. podnaloga

Podatke smo dobili v obliki seznama parov oblike (uslužbenec, šef). Vsi uslužbenci so predstavljeni z nizi (ki so običajno njihova imena oz. priimki). Zgled:

[('Mojca', 'Tilen'), ('Andrej', 'Tilen'), ('Tilen', 'Zoran')]

Komentar: Tilen ima pod seboj dva podrejena (Andreja in Mojco), njegovi šef pa je Zoran. Zoran nima šefa, vsi ostali pa so njegovi podrejeni.

Napišite funkcijo slovarSefov(seznam), ki bo iz zgoraj opisanega seznama zgradila slovar šefov. Ključi v seznamu naj bodo uslužbenci, vrednosti pa njihovi šefi. Zgled:

>>> slovarSefov([('Mojca', 'Tilen'), ('Andrej', 'Tilen'), ('Tilen', 'Zoran')])
{'Andrej': 'Tilen', 'Mojca': 'Tilen', 'Tilen': 'Zoran'}

Uradna rešitev

def slovarSefov(seznam):
    '''Glede na zgornji opis vrne slovar šefov'''
    slovar = {}
    for usluzbenec, sef in seznam:
        slovar[usluzbenec] = sef
    return slovar

2. podnaloga

Napišite funkcijo neposrednoPodrejeni(seznam), ki bo iz zgoraj opisanega seznama sestavila slovar neposredno podrejenih. Vrednost pri vsakem ključu naj bo množica tistih uslužbencev, ki imajo le-ta ključ za svojega šefa. Zgled:

>>> neposrednoPodrejeni([('Mojca', 'Tilen'), ('Andrej', 'Tilen'), ('Tilen', 'Zoran')])
{'Zoran': {'Tilen'}, 'Tilen': {'Andrej', 'Mojca'}}

Zoranu je torej neposredno podrejen le Tilen, Tilnu pa sta podrejena tako Andrej kot Mojca.

Uradna rešitev

def neposrednoPodrejeni(seznam):
    '''Glede na zgornji opis vrne slovar šef : množica neposredno podrejenih'''
    podrejeni = {}
    for usluzbenec, sef in seznam: # sprehodimo se po parih iz seznama
        if sef not in podrejeni: # če smo našli novega šefa
            podrejeni[sef] = set() # je potrebno narediti nov vnos v slovar
        podrejeni[sef].add(usluzbenec)
    return podrejeni

3. podnaloga

Sestavite funkcijo verigaNadrejenih(usluzbenec, slovar), ki kot prvi argument dobi niz, ki predstavlja nekega uslužbenca, kot drugi argument pa dobi slovar šefov (v obliki kot ga vrne funkcija slovarSefov). Funkcija naj sestavi seznam, ki po vrsti vsebuje: šefa osebe usluzbenec, šefa od njegovega šefa itn. (dokler končno ne pridemo do osebe, ki nima šefa). Zgled:

>>> verigaNadrejenih('Mojca', {'Andrej': 'Tilen', 'Mojca': 'Tilen', 'Tilen': 'Zoran'})
['Tilen', 'Zoran']

Mojci je nadrejen Tilen, kateremu je nadrejen Zoran, torej sta Mojčina šefa Tilen in Zoran.

Uradna rešitev

def verigaNadrejenih(usluzbenec, slovar):
    '''sestavi seznam, ki predstavlja verigo nadreejenih'''
    # če oseba nima šefa, je njegova veriga prazna
    # oseba nima šefa, če se ne pojavi kot ključ
    if usluzbenec not in slovar.keys():
        return []
    # uslužbenec ima šefa!
    sef = slovar[usluzbenec]
    seznamNadrejenih = [sef]
    # določimo verigo nadrejenih od šefa
    verNadrSef = verigaNadrejenih(sef, slovar)
    # združimo seznama
    seznamNadrejenih = seznamNadrejenih + verNadrSef
    return seznamNadrejenih

def verigaNadrejenihV2(usluzbenec, slovar):
    '''Glede na zgornji opis vrne zaporedni seznam oseb, ki so uslužbencu nadrejeni'''
    '''brez rekurzije'''
    veriga = []
    while usluzbenec in slovar: # če uslužbenec ima šefa (ko ga ne bo imel, smo na koncu verige!)
        usluzbenec = slovar[usluzbenec] # vzamemo njegovega šefa - v naslednjem koraku bomo vzeli njegovega šefa ...
        veriga.append(usluzbenec) # in ga dodamo
    return veriga

4. podnaloga

Sestavite funkcijo mnozicaPodrejenih(usluzbenec, slovar), ki kot prvi argument dobi ime uslužbenca, kot drugi argument pa slovar neposredno podrejenih (v obliki kot ga vrne funkcija neposrednoPodrejeni). Funkcija naj sestavi in vrne množico vseh tistih oseb, ki so (posredno ali neposredno) podrejeni osebi usluzbenec. Zgled:

>>> mnozicaPodrejenih('Zoran', {'Zoran': {'Tilen'}, 'Tilen': {'Andrej', 'Mojca'}, 'Mojca' : {'Urša'}})
{'Andrej', 'Mojca', 'Tilen', 'Urša'}

Zoranju je neposredno podrejen Tilen, torej sta mu podrejena tudi Tilnova podrejena Andrej in Mojca, ter Urša, ker je ta podrejena Mojci.

Uradna rešitev

def mnozicaPodrejenih(usluzbenec, slovar):
    '''vrne  _množico_ vseh tistih oseb, ki so (posredno ali
       neposredno) podrejeni osebi `usluzbenec`.'''
    # komentarjev NAMENOMA ni !
    dodaj = [usluzbenec]
    podrejeni = set()
    while len(dodaj) > 0:
        u = dodaj[-1]
        del dodaj[-1]
        if u not in slovar:
            continue
        for v in slovar[u]:
            if v not in podrejeni:
                dodaj.append(v)
            podrejeni.add(v)
    return podrejeni

5. podnaloga

Tistemu uslužbencu, za katerega velja:

  • da nima nadrejenih;
  • je zadnji v verigi nadrejenih vseh ostalih uslužbencev (razen seveda samega sebe);

pravimo big boss. Sestavite funkcijo bigBoss(slovar), ki kot argument dobi slovar nadrejenih in vrne ime osebe, ki je big boss v podjetju oz. vrednost None, če to podjetje nima big boss-a. Zgled:

>>> bigBoss({'Andrej': 'Tilen', 'Mojca': 'Tilen', 'Tilen': 'Zoran'})
'Zoran'

Uradna rešitev

def bigBoss(slovar):
    '''nekdo, ki nima šefa in je šef vsem ostalim'''
    if len(slovar) == 0:
        return None # tak slovar ga nima!
    bigBoss = list(slovar.keys())[0] # kandidat za velikega šefa (če ne bo imel nadrejenih!)
    nadrejeni = verigaNadrejenih(bigBoss, slovar) # kdo so kandidatu nadrejeni?
    if len(nadrejeni) > 0: # če je prejšnji kandidat imel nadrejene
        bigBoss = nadrejeni[-1] # je samo zadnji kandidat za velikega šefa
    # dobili smo kandidata za big boss-a
    for uslužbenec in slovar: # pregledamo vse uslužbence
        if uslužbenec == bigBoss: # kandidata spustimo
            continue
        nadrejeni = verigaNadrejenih(uslužbenec, slovar) # pogledamo vse uslužbencu nadrejene
        if (len(nadrejeni) == 0) or (nadrejeni[-1] != bigBoss): # če jih ni, potem bigBNoss ni nadrejeni temu, torej ni veliki šef!
            return None                                         # ali pa najvišji nadrejeni temu uslužbencu ni bil naš veliki šef - v firmi velikega šefa ni
    return bigBoss

Permutacije se vračajo

Ker ste v komentarjih napisali, da je ukvarjanje s permutacijami vaša najbolj priljubbljena tema, se permutacije vračajo v vsem svojem sijaju!

V slovarju imamo shranjeno permutacijo naravnih števil od $1$ do $n$. Na primer permutacijo, ki slika $1$ v $3$, $3$ v $1$, število $2$ pa pusti pri miru, zapišemo s slovarjem {1: 3, 2: 2, 3: 1}.

Pri vseh nalogah predpostavite, da je število x zagotovo element permutacije. Prav tako tudi to, da je permutacija pravilna.

1. podnaloga

Le za ogrevanje (naloga "ne šteje") rešite naslednjo nalogo:

Sestavite funkcijo slika(permutacija, x), vrne pa sliko števila x s podano permutacijo.

Uradna rešitev

def slika(permutacija, x):
    return permutacija[x]

2. podnaloga

Sestavite funkcijo jePermutacija(slovar), ki vrne True, če dan slovar predstavlja permutacijo, in False sicer.

Npr. slovar {1: 3, 3: 1} ni permutacija, saj ne vemo, kam se slika 2. Tudi {1: 3, 2: 2, 3: 2, 4: 1} ni slovar, saj 4 ni slika nobenega števila!

Uradna rešitev

def jePermutacija(slovar):
    # Za vsa števila od 1 do n bomo pogledali, če nastopajo tako v domeni
    # kot v sliki permutacije. Imeli bomo seznam je_v_domeni, ki ima na
    # mestu i - 1 zapisano, če smo ugotovili, da i je v domeni permutacije.
    # Podobno storimo za sliko.
    n = len(slovar)
    je_v_domeni = [False for i in range(n)]
    je_v_sliki = [False for i in range(n)]

    # Nato gremo čez vse ključe k in pripadajoče vrednosti v v slovarju.
    # Če sta tako k kot v med 1 in n, označimo, da sta v domeni in sliki,
    # sicer pa vemo, da slovar ne predstavlja permutacije.
    for k, v in slovar.items():
        if 1 <= k <= n and 1 <= v <= n:
            je_v_domeni[k - 1] = True
            je_v_sliki[v - 1] = True
        else:
            return False

    # Na koncu pogledamo, ali so v domeni in sliki nastopala vsa števila.
    # Funkcija all vrne True natanko takrat, ko so vsi elementi v seznamu
    # enaki True.
    return all(je_v_domeni) and all(je_v_sliki)

3. podnaloga

Sestavite funkcijo slike(permutacija, x, n), ki vrne zaporedje slik, ki ga dobimo, če začnemo s številom x in na njem n-krat uporabimo permutacijo permutacija.

>>> slike({1: 3, 2: 4, 3: 2, 4: 1}, 1, 2)
[1, 3, 2]

Uradna rešitev

def slike(permutacija, x, n):
    # Funkcijo definiramo rekurzivno. Če je n > 0, naredimo sliko y, nato
    # pa dodamo še seznam n - 1 slik elementa y.
    if n == 0:
        return [x]
    else:
        y = permutacija[x]
        ostale = slike(permutacija, y, n - 1)
        return [x] + ostale
    
def slike_nerekurzivno(permutacija, x, n):
    r=[]
    for i in range(n+1):
        r.append(x)
        x = permutacija[x]
    return r

4. podnaloga

Sestavite funkcijo cikel(permutacija, x), ki vrne celoten cikel, ki se začne s številom x.

>>> cikel({1: 3, 2: 2, 3: 1}, 1)
[1, 3]
>>> cikel({1: 3, 2: 2, 3: 1}, 2)
[2]

Uradna rešitev

def cikel(permutacija, x):
    # Dokler slika zadnjega elementa, ki smo ga dodali v cikel, ni enaka
    # začetnemu elementu y, dodamo sliko ter ponavljamo.
    cikel = [x]
    y = permutacija[x]
    while y != x:
        cikel.append(y)
        y = permutacija[y]
    return cikel

5. podnaloga

Sestavite funkcijo cikli(permutacija), ki vrne seznam disjunktnih ciklov dane permutacije. Vsak cikel naj se začne z najmanjšim številom v ciklu, cikli pa naj bodo urejeni po začetnem številu.

>>> cikli({1: 3, 2: 2, 3: 1})
[[1, 3], [2]]

Uradna rešitev

def cikli(permutacija):
    # V seznam izracunaniCikli si shranjujemo do sedaj izračunane cikle, v seznam
    # ugotovljena pa vsa tista števila, za katera smo že ugotovili, kateremu
    # ciklu pripadajo.
    izracunaniCikli = []
    ugotovljena = []
    # Nato gremo zaporedoma čez vsa števila od 1 do n. Če za neko število
    # še nismo ugotovili, kam spada, je najmanjše v svojem ciklu. Zato
    # izračunamo njegov cikel, ga dodamo k ciklom, vsa števila iz cikla pa
    # dodamo med ugotovljena.
    for i in range(1, len(permutacija) + 1):
        if i not in ugotovljena:
            c = cikel(permutacija, i)
            izracunaniCikli.append(c)
            ugotovljena.extend(c)
    return izracunaniCikli

Ljubezen nam je vsem v pogubo II

Socialno omrežje zaljubljenosti podamo s slovarjem, ki ime osebe preslika v množico vseh, v katere je oseba zaljubljena (ena oseba je lahko zaljubljena v več oseb). Na primer, slovar

s = {'Ana' : {'Bine','Cene'},
     'Bine' : set(),
     'Cene' : {'Bine'},
     'Davorka' : {'Davorka'},
     'Eva' : {'Bine'}}

nam pove, da je Ana zaljubljena v Bineta in Cenete, Bine ni zaljubljen, Cene ljubi Bineta, Davorka samo sebe in Eva Bineta.

   Opomba*: Naloga je podobna nalogi"Ljubezen nam je vsem v pogubo", le da so tukaj ljubljene osebe v množici in ne v seznamu. Prav tako moramo vračati množice!_

1. podnaloga

Sestavite funkcijo narcisoidi(d), ki sprejme slovar zaljubljenih in vrne monžico tistih, ki ljubijo same sebe.

Uradna rešitev

def narcisoidi(d):
    return {oseba for oseba in d if oseba in d[oseba]}

2. podnaloga

Sestavite funkcijo ljubljeni(d), ki sprejme slovar zaljubljenih in vrne množico tistih, ki so ljubljeni.

Uradna rešitev

def ljubljeni(d):
    return {ljubljen for oseba in d for ljubljen in d[oseba]}

3. podnaloga

Sestavite funkcijo pari(d), ki sprejme slovar zaljubljenih in vrne množico vseh parov, ki so srečno ljubljeni. Vsak par naj se pojavi samo enkrat in sicer tako, da je sta zaljubljenca našteta po abecedi. Na primer, če sta Ana in Bine zaljubljena, dodamo par ('Ana','Bine').

Uradna rešitev

def pari(d):
    return {tuple(sorted((x, y))) for x in d for y in d[x] if x in d[y]}

4. podnaloga

Sestavite funkcijo ustrezljivi(oseba, d), ki sprejme ime osebe ter slovar zaljubljenih, vrne pa množico vseh ljudi, ki so do dane osebe še posebej ustrežljivi. Posebej ustrežljivi so seveda za to, ker so bodisi zaljubljeni v dano osebo, bodisi so zaljubljeni v osebo, ki je posebej ustrežljiva do nje, in tako naprej.

Na primer, če imamo slovar

s = {'Ana' : {'Bine', 'Cene'},
     'Bine' : {'Ana'},
     'Cene' : {'Bine'},
     'Davorka' : {'Davorka'},
     'Eva' : {'Bine'}}

so do Ceneta posebej ustrežljivi Ana (ki je zaljubljena vanj), Bine (ki je zaljubljen v Ano), Eva (ki je zaljubljena v Bineta), ter seveda Cene, ki je zaljubljen v Evo.

Uradna rešitev

def ustrezljivi(oseba, d):
    # seznam, v katerega nabiramo ustrežljive osebe
    ustrezljivi = set()
    # najprej dodamo tiste, ki ljubijo prvo osebo
    dodani = {o for o in d if oseba in d[o]}
    # dokler smo koga dodali, dodajamo ustrežljive
    while dodani:
        ustrezljivi.update(dodani)
        # sedaj pa dodajamo tiste, ki ljubijo nazadnje dodane osebe
        dodani = {o for o in d for dodan in dodani
                  if dodan in d[o] and o not in ustrezljivi}
    return ustrezljivi

Imena

V neki datoteki, ki ima lahko več vrstic, so zapisana imena. Znotraj posamične vrstice so imena ločena z vejicami (brez presledkov). Primer take datoteke:

Jaka,Peter,Miha,Peter,Anja
Franci,Roman,Renata,Jožefa
Pavle,Tadeja,Arif,Filip,Gašper

1. podnaloga

Sestavite funkcijo kolikokrat_se_pojavi(niz, ime), ki vrne število pojavitev imena ime v nizu imen niz.

>>> kolikokrat_se_pojavi('Alojz,Samo,Peter,Alojz,Franci', 'Alojz')
2

Uradna rešitev

def kolikokrat_se_pojavi(niz, ime):
    return niz.split(',').count(ime)

# Alternativna rešitev (brez uporabe metode count)
def kolikokrat_se_pojavi_alt(niz, ime):
    vsa_imena = niz.split(',')
    stevec = 0
    for s in vsa_imena:
        if s == ime:
            stevec += 1
    return stevec

2. podnaloga

Sestavite funkcijo koliko(niz, datoteka), ki na izhodno datoteko z imenom datoteka za vsako ime zapiše, kolikokrat se pojavi v nizu niz.

Na primer, če je niz enak 'Jaka,Luka,Ante,Luka', naj funkcija v izhodno datoteko zapiše

Jaka 1
Luka 2
Ante 1

Pozor: Imena naj bodo izpisana v takem vrstnem redu, kakor si sledijo njihove prve pojavitve v nizu niz.

Uradna rešitev

def koliko(niz, datoteka):
    imena = niz.split(',')
    brez_ponovitev = []
    for ime in imena:
        if ime not in brez_ponovitev:
            brez_ponovitev.append(ime)
    with open(datoteka, 'w', encoding='utf-8') as f:
        for ime in brez_ponovitev:
            print(ime, imena.count(ime), file=f)

# Alternativna rešitev
def koliko_alt(niz, datoteka):
    imena = []
    stevci = []
    for ime in niz.split(','):
        if ime not in imena:
            imena.append(ime)
            stevci.append(1)
        else:
            stevci[imena.index(ime)] += 1
    with open(datoteka, 'w', encoding='utf-8') as f:
        for ime, stevec in zip(imena, stevci):
            print(ime, stevec, file=f)

# Še ena možna rešitev (z uporabo slovarja)
def koliko_alt2(niz, datoteka):
    imena = niz.split(',')
    stevec = {}
    for ime in imena:
        stevec[ime] = stevec.get(ime, 0) + 1
    with open(datoteka, 'w', encoding='utf-8') as f:
        for ime in imena:
            if ime not in stevec:
                continue
            print(ime, stevec[ime], file=f)
            del stevec[ime]

3. podnaloga

Sestavite funkcijo koliko_iz_datoteke(vhodna, izhodna), ki naj naredi isto kot funkcija koliko, le da podatke prebere iz datoteke. Torej, na izhodno datoteko z imenom izhodna naj za vsako ime zapiše, kolikokrat se pojavi v datoteki z imenom vhodna.

Pozor: Vhodna datoteka ima lahko več vrstic. Imena izpišite v enakem vrstnem redu, kot si sledijo njihove prve pojavitve v datoteki vhodna.

Uradna rešitev

def koliko_iz_datoteke(vhodna, izhodna):
    vrstice = []  # Seznam vseh vrstic v datoteki vhodna.
    with open(vhodna, encoding='utf-8') as f:
        for vrstica in f:
            vrstice.append(vrstica.strip())  # Odstranimo '\n' s konca vrstice.
    imena = ','.join(vrstice)  # Niz z vsemi imeni iz datoteke.
    koliko(imena, izhodna)

4. podnaloga

Sestavite funkcijo koliko_urejen(vhodna, izhodna), ki na izhodno datoteko z imenom izhodna za vsako ime zapiše, kolikokrat se pojavi v datoteki z imenom vhodna. Imena naj bodo urejena padajoče po frekvenci pojavitev. Imena, ki imajo enako frekvenco, naj bodo nadalje urejena leksikografsko (tj. po abecednem vrstnem redu).

Primer: Če je na datoteki imena_vhod.txt vsebina

Luka,Jaka
Luka,Miha,Miha
Miha,Aleš,Aleš

naj bo po klicu funkcije koliko_urejen('imena_vhod.txt', 'imena_izhod.txt') na datoteki imena_izhod.txt naslednja vsebina:

Miha 3
Aleš 2
Luka 2
Jaka 1

Uradna rešitev

def koliko_urejen(vhod, izhod):
    imena = []  # Seznam vseh imen.
    brez_ponovitev = []  # Seznam imen brez ponovitev.
    with open(vhod, encoding='utf-8') as f:
        for vrstica in f:
            for ime in vrstica.strip().split(','):
                imena.append(ime)
                if ime not in brez_ponovitev:
                    brez_ponovitev.append(ime)
    # Seznam pari bo vseboval pare oblike (-3, 'Miha'). Druga komponenta bo
    # ime; prva komponenta bo število pojavitev tega imena, pomnoženo z -1.
    pari = []
    for ime in brez_ponovitev:
        pari.append((-imena.count(ime), ime))
    # Metoda sort uredi števila naraščajoče po vrednosti, nize pa leksikografsko
    # (tj. tako kot so urejeni v leksikonu). Pare uredi glede na prvo komponento,
    # tiste z enako prvo komponento pa še glede na drugo komponento.
    pari.sort()
    with open(izhod, 'w', encoding='utf-8') as f:
        for s, ime in pari:
            print(ime, -s, file=f)

# Alternativna rešitev (uporablja izpeljane sezname, množice in lambda funkcije)
def koliko_urejen_alt(vhod, izhod):
    with open(vhod, encoding='utf-8') as f:
        imena = ','.join([vrstica.strip() for vrstica in f])
    enkrat_imena = set(imena.split(','))
    pari = [(ime, kolikokrat_se_pojavi(imena, ime)) for ime in enkrat_imena]
    pari.sort(key=lambda p: (-p[1], p[0]))
    with open(izhod, 'w', encoding='utf-8') as f:
        for ime, s in pari:
            print(ime, s, file=f)

LEGO kocke

Vsako LEGO kocko lahko opišemo z dvema lastnostima: tipom (torej obliko) in barvo.

Če boste nalogo uspešno rešili še pred koncem, si lahko ogledate seznam vseh možnih barv kock, pri tej nalogi pa bomo predpostavili le tri barve: rdečo, modro in rumeno. Tipov LEGO kock pa je ogromno, zato jih bomo predstavili kar z nizi kot na primer '2x2', '4x1-tanka' ali pa 'nogice'. Sprejmimo dogovor, da nobeno ime tipa ne vsebuje pike.

1. podnaloga

Vsebino škatle podamo s seznamom vseh kock, ki so v njej. Vsaka kocka je opisana z nizom, ki vsebuje tip kocke in barvo, ki sta ločeni s piko. Npr. kocka tipa '2x2' rdeče barve je predstavljena z nizom '2x2.rdeča'.

Napišite funkcijo slovar_kock(skatla), ki dobi seznam skatla in sestavi slovar vsebovanih kock, urejen po tipih ter po barvah, kot prikazuje primer:

>>> slovar_kock(['2x2.rdeča', '2x2.rdeča', 'nogice.modra', '2x2.modra'])
{'nogice': {'modra': 1}, '2x2': {'rdeča': 2, 'modra': 1}}

Uradna rešitev

def slovar_kock(skatla):
    '''Vrne slovar lego kock'''
    sk = dict()
    for kocka in skatla:
        tip, barva = kocka.split('.')
        if tip not in sk:
            sk[tip] = dict()
        sk[tip][barva] = sk[tip].get(barva, 0) + 1
    return sk

2. podnaloga

Napišite funkcijo lahko_sestavimo(skatla, model), ki dobi dva slovarja kock, in vrne True natanko tedaj, ko je možno modelček (za katerega potrebujemo kocke, ki so podane s slovarjem model) sestaviti s kockami, ki so v škatli.

>>> model = {'4x1': {'modra': 1}, '2x2': {'rdeča': 2, 'modra': 1}}
>>> skatla = {'4x1': {'modra': 3, 'rdeča': 2}, '2x2': {'rdeča': 4, 'modra': 1, 'rumena': 5}}
>>> lahko_sestavimo(skatla, model)
True
>>> model_2 = {'4x1': {'rumena': 1}, '2x2': {'rdeča': 1, 'rumena': 2}}
>>> lahko_sestavimo(skatla, model_2)
False

Uradna rešitev

def lahko_sestavimo(skatla, model):
    '''Ali lahko sestavimo model, če imamo na voljo škatlo kock skatla'''
    for tip, barve in model.items():
        for barva, cnt in barve.items():
            if cnt > skatla.get(tip, {}).get(barva, 0):
                return False
    return True

3. podnaloga

Napišite funkcijo hitro_sestavljanje(skatla, model), ki vrne True, če je možno modelček sestaviti “na hitro”, torej tako, da barve niso pomembne (še vedno pa je pomembno, da uporabimo kocke ustreznega tipa).

>>> model = {'4x1': {'modra': 2}, '2x2': {'rdeča': 2, 'modra': 1}}
>>> skatla_1 = {'4x1': {'rdeča': 3}, '2x2': {'rdeča': 1, 'rumena': 5}}
>>> skatla_2 = {'4x1': {'rumena': 3, 'rdeča': 2}, '2x2': {'rumena': 1, 'rdeča': 1}}
>>> hitro_sestavljanje(skatla_1, model)
True
>>> hitro_sestavljanje(skatla_2, model)
False

Uradna rešitev

def hitro_sestavljanje(skatla, model):
    '''Ali lahko "na hitro"(brez upoštevanja barv)
       sestavimo model iz kock v škatli skatla
    '''
    for tip, barve in model.items():
        if sum(barve.values()) > sum(skatla.get(tip, {}).values()):
            return False
    return True

Ponavljamo ...


Aritmetika

1. podnaloga

Sestavite funkcijo vsota_potenc(k, n), ki vrne vsoto $k$-tih potenc prvih $n$ naravnih števil: $$1^k + 2^k + 3^k + \cdots + n^k.$$ Zgled:

>>> vsota_potenc(10, 2)
1025

Ali znaš napisati rešitev, kjer funkcija vsebuje en sam stavek oblike return nekIzraz

Uradna rešitev

def vsota_potenc(k, n):
    '''Vrne vsoto potenc'''
    vsota = 0
    for i in range(1, n+1): # preko vseh členov
        člen = i**k # trenutni člen
        vsota += člen
    return vsota

def vsota_potencV2(k, n):
    '''Vrne vsoto potenc s pomočjo izpeljanih seznamov'''
    return sum([i**k for i in range(1, n+1)])

2. podnaloga

Sestavite funkcijo inverzna_harmonicna(x), ki pove, koliko členov harmonične vrste $$1/1 + 1/2 + 1/3 + 1/4 + \cdots$$ moramo sešteti, da presežemo vrednost $x$. Zgled:

>>> inverzna_harmonicna(2)
4

Ali bi pri tej nalogi šlo z izpeljanim seznamom?

Uradna rešitev

def inverzna_harmonicna(x):
    '''Koliko členov harmonične vrste moramo sešteti, da presežemo x'''
    trenutnaVsota = 0
    k = 1 # števec členov (enak imenovalcu)
    while trenutnaVsota <= x: # nismo še preko vsote
        trenutnaVsota += 1 / k
        k += 1
    return k - 1 # ker smo začeli šteti z 1 pred zanko

3. podnaloga

Sestavite funkcijo vsota_stevk(n), ki vrne vsoto števk danega pozitivnega celega števila. Zgled:

>>> vsota_stevk(2014)
7

Če celo število ni pozitivno, vrnemo 0. Ali znaš nalogo rešiti z izpeljanim seznamom, s pomočjo enega samega stavka return?

Uradna rešitev

def vsota_stevk(n):
    '''Vrne vsoto števk pozitivnega celega števila'''
    return sum([int(stevka) for stevka in str(n)]) if n > 0 else 0

4. podnaloga

Sestavite funkcijo vsote(n), ki izračuna, na koliko načinov lahko zapišemo naravno število n kot vsoto naraščajočega zaporedja naravnih števil. Na primer, klic vsote(7) vrne 5, ker lahko 7 zapišemo na naslednje načine:

7 = 7
7 = 1 + 6
7 = 2 + 5
7 = 3 + 4
7 = 1 + 2 + 4

Opomba: Ta naloge je malce težja od ostalih iz tega sklopa.

Uradna rešitev

def stej(m, j):
    """
    Pomožna funkcija, ki šteje vsote števila m, pri čemer mora biti
    prvi člen v vsoti večji ali enak številu j.
    """
    if m == 0:
        return 1
    elif j > m:
        return 0
    else:
        return sum([stej(m-i, i+1) for i in range(j, m+1)])

def vsote(n):
    return stej(n, 1)

Bančni račun

1. podnaloga

V seznamu imamo podatke o prilivih in odlivih s tekočega računa. Pozitivna števila predstavljajo priliv (polog denarja), negativna pa dvig. Vsak element seznama predstavlja en dan. Če na nek dan ni prilivov ali dvigov, je vrednost v seznamu 0. Privzemite, da je na začetku stanje na računu 0.

Sestavite funkcijo koncno_stanje(spremembe), ki iz danega seznama prilivov in odlivov izračuna končno stanje.

Pri rešitvi ne uporabi zanke neposredno v kodi te funkcije.

Uradna rešitev

def koncno_stanje(spremembe):
    '''Izračuna končno stanje na računu'''
    return sum(spremembe)

2. podnaloga

Sestavite funkcijo stanja(spremembe), ki iz danega seznama prilivov in odlivov ustvari seznam vmesnih stanj na računu. Privzemite, da je na začetku stanje na računu 0. To naj bo prva "sprememba" v seznamu

 >>>stanja([10, -5, 20, -6])
 [0, 10, 5, 25, 19]

Ali znaš nalogo rešiti tako, da vsebuje le stavek oblike return <nekIzraz>?

Uradna rešitev

def stanja(spremembe):
    '''Vrne seznam vmesnih stanj na računu'''
    trenutnoStanje = 0
    stanja = [0]
    for sprememba in spremembe: # pregledamo vse transkacije v seznamu
        trenutnoStanje += sprememba
        stanja.append(trenutnoStanje)
    return stanja

def stanjaV2(spremembe):
    '''Vrne seznam vmesnih stanj na računu'''
    # nalogo lahko rešimo s pomočjo izpeljanih seznamov
    # na vsakem seznamu generiramo ustrezni začetni del seznama in ga seštejemo
    return [sum(spremembe[:i]) for i in range(len(spremembe)+1)]

3. podnaloga

Sestavite funkcijo ekstrema(spremembe), ki pri danih spremembah stanja poišče vrednosti, ko je bilo stanje na računu najnižje oziroma najvišje. Pri tem seveda zanemari začetno stanje 0 in prepostavi, da je prišlo do vsaj ene spremembe

Stanji naj vrne v obliki nabora.

 >>>ekstrema([10, -5, 20, -6])
 (5, 25)

Rešitev poišči brez uporabe zank neposredno v kodi te funkcije.

Uradna rešitev

def ekstrema(spremembe):
    '''Vrne najnižje in najvišje stanje'''
    s = stanja(spremembe)[1:] # uporabimo funkcijo prejšnje naloge, a začetno stanje (0) zavržemo
    najnizje = min(s)
    najvisje = max(s)
    return (najnizje, najvisje)

4. podnaloga

Sestavite funkcijo kdaj_ekstrema(spremembe), ki pri danih spremembah stanja poišče število dni od začetka, ko je bilo stanje na računu najnižje oziroma najvišje. Rezultat naj vrne v obliki nabora.

 >>>kdaj_ekstrema([10, -5, 20, -6])
 (2, 3)

Rešitev poišči brez uporabe zank neposredno v kodi te funkcije.

Uradna rešitev

def kdaj_ekstrema(spremembe):
    '''Vrne število dni od začetka, ko smo dosegli najnižje in najvišje stanje'''
    s = stanja(spremembe) [1:] # uporabimo funkcijo prejšnje naloge, a začetno stanje (0) zavržemo
    najnizje = s.index(min(s)) + 1
    najvisje = s.index(max(s)) + 1
    return (najnizje, najvisje)

5. podnaloga

Sestavite funkcijo razlika(spremembe, i, j), ki poišče razliko stanj med dnevoma z indeksom i in j. Na dan 0 je bilo seveda stanje 0.

 >>>razlika([10, -5, 20, -6], 1, 3)
 15
 >>>razlika([10, -5, 20, -6], 1, 2)
 -5

Predpostavite lahko, da je i manjši ali enak j. Rešitev poišči brez uporabe zank neposredno v kodi te funkcije.

Uradna rešitev

def razlika(spremembe,i,j):
    '''Vrne spremembo med dnevoma i in j'''
    return sum(spremembe[i:j])

6. podnaloga

Sestavite funkcijo najvecja_razlika(spremembe), ki poišče največji relativni priliv. Z drugimi besedami, poišče največjo vrednost, ki jo zavzame vsota poljubnega strnjenega podseznama. Tako na primer najvecja_razlika([10, -13, 3, 20, -2, 5]) vrne 3 + 20 - 2 + 5 = 26. Če je seznam spremembe prazen, naj funkcija vrne None.

Uradna rešitev

def najvecja_razlika(spremembe):
    '''Izračuna največji relativni priliv v nekem obdobju'''
    if spremembe == []: return None  # ni bilo nobenih sprememb

    n = len(spremembe)
    najRaz = spremembe[0] # načeloma je kandidat kar dogajanje prvi dan
    for začetek in range(0, n):
        for konec in range(začetek, n):  # vsi možni začeteki in konci podseznamov
            vsotaPodsez = 0
            for indeks in range(začetek, konec+1): # izračunamo vsoto podseznama
                vsotaPodsez += spremembe[indeks]
            if vsotaPodsez > najRaz:  # našli smo večji priliv
                najRaz = vsotaPodsez
    return najRaz

7. podnaloga

Sestavite funkcijo najvecja_razlikaV2(spremembe), ki poišče največji relativni priliv. Z drugimi besedami, poišče največjo vrednost, ki jo zavzame vsota poljubnega strnjenega podseznama. Tako na primer najvecja_razlikaV2([10, -13, 3, 20, -2, 5]) vrne 3 + 20 - 2 + 5 = 26. Če je seznam spremembe prazen, naj funkcija vrne None.

Uporabi izpeljanje sezname!

Uradna rešitev

def najvecja_razlikaV2(spremembe):
    '''Izračuna največji relativni priliv v nekem obdobju'''
    if spremembe == []: return None  # ni bilo nobenih sprememb

    n = len(spremembe)
    return max(sum(spremembe[i:j+1])
               for i in range(0, n)
               for j in range(i, n))  # generiramo vse možne podsezname, jih seštejemo
                                      # in med vsotami poiščemo največjo

Cik-cakasti sprehodi

Pravimo, da je sprehod po točkah v ravnini cik-cakast, če gremo iz točke $(x, y)$ vedno v točko $(x + 1, y - 1)$ ali pa v $(x + 1, y + 1)$. Torej, na vsakem koraku gremo za eno polje v desno ter bodisi navzdol, bodisi navzgor.

1. podnaloga

Sestavite funkcijo je_cikcakast(sprehod), ki vrne True natanko tedaj, kadar je sprehod, podan z zaporedjem točk sprehod, cik-cakast. Zgled:

>>> je_cikcakast([(1, 1), (2, 0), (3, -1), (4, 0), (5, 1)])
True

Uradna rešitev

def je_cikcakast(sprehod):
    '''Ali je sprehod, podan kot seznam parov koordinat, cik cakast'''
    for i in range(len(sprehod) - 1): # jemali bomo po dve točki - začetek in konec, zato končamo pri predzadnji točki
        (x1, y1), (x2, y2) = sprehod[i], sprehod[i + 1] # razpakiranje podatkov
        if x2 != x1 + 1 or abs(y2 - y1) != 1:
            return False # že en ne cikcakast premik pove rezultat
    return True # vsi premiki so bili cikcak

2. podnaloga

Sestavite funkcijo prestavi_v_1kvadrant(sprehod), ki sprehod zamakne tako, da v celoti poteka izključno v 1. kvadrantu ter da je njegova prva točka na ordinatni, njegova najnižja točka pa na abscisni osi. Zgled:

>>> prestavi_v_1kvadrant([(1, 1), (2, 0), (3, -1), (4, 0), (5, 1)])
[(0, 2), (1, 1), (2, 0), (3, 1), (4, 2)]

Uradna rešitev

def prestavi_v_1kvadrant(sprehod):
    '''Prestavi sprehod, podan kot seznam parov koordinat, v 1kvadrant'''
    # poiščemo minimalno x in min. y koordinato
    min_x, min_y = sprehod[0]
    for tockaX, tockaY in sprehod:
        min_x = min(min_x, tockaX) # manjši od obeh je kandidat za min
        min_y = min(min_y, tockaY)
    novSprehod = []
    for x, y in sprehod: #premaknemo za ustrezna minimuma
        novSprehod += [(x - min_x, y - min_y)]
    return novSprehod

def prestavi_v_1kvadrant_V2(sprehod):
    '''Prestavi sprehod, podan kot seznam parov koordinat, v 1kvadrant'''
    # s pomočjo izpeljanih seznamov
    min_x = min(x for x, _ in sprehod)
    min_y = min(y for _, y in sprehod)
    return [(x - min_x, y - min_y) for x, y in sprehod]

3. podnaloga

Sestavite funkcijo stevilo_cikcakastih(zac, kon), ki vrne število vseh cik-cakastih sprehodov med točkama zac in kon. Zgled:

>>> stevilo_cikcakastih((0, 0), (9, 3))
84
>>> stevilo_cikcakastih((0, 0), (0, 0))
1
>>> stevilo_cikcakastih((0, 0), (-1, 0))
0
>>> stevilo_cikcakastih((1, 1), (9, 3))
56
>>> stevilo_cikcakastih((1, -1), (9, 3))
28

Namig: Kam gre lahko prvi korak cik-cakastega sprehoda? Dobro si oglejte zgornji zgled.

Uradna rešitev

def stevilo_cikcakastih(zac, kon):
    '''Koliko cikcakastih sprehodov je med začetno in končno točko'''
    (xz, yz), (xk, yk) = zac, kon
    if zac == kon: 
        return 1
    elif xz >= xk or abs(yz - yk) > xk - xz:
        return 0
    else:
        # gremo lahko gor ali dol
        return stevilo_cikcakastih((xz + 1, yz + 1), kon) + stevilo_cikcakastih((xz + 1, yz - 1), kon)

4. podnaloga

Sestavite funkcijo narisi_sprehod(sprehod, izhod), ki v datoteko z imenom izhod z znaki / in \ izpiše cik-cakast sprehod, podan z množico točk sprehod. Sprehod naj premakne tako, da bo najbolj levi znak v prvem stolpcu, najvišji pa v prvi vrstici datoteke. Sprehod [(0, 0), (1, 1), (2, 0), (3, -1), (4, -2), (5, -1)] morate tako izpisati kot:

/\
  \
   \/

Enako sliko da npr. tudi

[(1, 1), (2, 2), (3, 1), (4, 0), (5, -1), (6, 0)]

sprehod [(5, -4), (6, -3), (7, -4), (8, -5), (9, -4), (10, -3), (11, -4), (12, -5)] pa kot:

/\  /\
  \/  \

Testni primeri delovanje primerjajo na sledečih cik-cakastih sprehodih:

  • [(0, 0), (1, 1), (2, 2), (3, 3), (4, 4), (5, 5)],
  • [(0, 0), (1, -1), (2, -2), (3, -3), (4, -4), (5, -5)],
  • [(0, 0), (1, 1), (2, 0), (3, -1), (4, -2), (5, -1)]
  • [(1, 1), (2, 2), (3, 1), (4, 0), (5, -1), (6, 0)]
  • [(5, -4), (6, -3), (7, -4), (8, -5), (9, -4), (10, -3), (11, -4), (12, -5)]

Namig:

Uradna rešitev

def narisi_sprehod(sprehod, izhod):
    '''Na izhodno datoteko nariše sprehod'''
    sprehod = prestavi_v_1kvadrant(sprehod) # prestavimo vse skupaj v prvi kvadrant
    # poiščemo maksimalno x in max. y koordinato (velikost "slike")
    max_x, max_y = sprehod[0]
    for tockaX, tockaY in sprehod:
        max_x = max(max_x, tockaX) # manjši od obeh je kandidat za min
        max_y = max(max_y, tockaY)
    # izhodna slika je najprej ustrezna matrika (seznam seznamov), sestavljena iz presledkov
    izhSlika = []
    for vr in range(max_y):
         vrstica = [' '] * (max_x + 1)
         izhSlika += [vrstica]
    for i in range(len(sprehod) - 1):
        (x1, y1), (x2, y2) = sprehod[i], sprehod[i + 1]
        if y2 - y1 == 1:
            izhSlika[max_y - y2][x1] = '/'
        elif y2 - y1 == -1:
            izhSlika[max_y - y1][x1] = '\\'
    with open(izhod, 'w') as f:
        for vrstica in izhSlika:
            izpVrstica = ''.join(vrstica) # združimo (zlepimo) vse znake v vrstici
            print(izpVrstica, file=f)

def narisi_sprehodV2(sprehod, izhod):
    '''Na izhodno datoteko nariše sprehod'''
    # uporaba izpeljanih seznamov
    sprehod = prestavi_v_1kvadrant(sprehod) # prestavimo vse skupaj v prvi kvadrant
    # poiščemo minimalno x in min. y koordinato
    max_x, max_y = max(x for x, _ in sprehod), max(y for _, y in sprehod)
    # izhodna slika je najprej ustrezna matrika (seznam seznamov), sestavljena iz presledkov 
    izhSlika = [[' ' for i in range(max_x + 1)] for j in range(max_y)]
    for i in range(len(sprehod) - 1):
        (x1, y1), (x2, y2) = sprehod[i], sprehod[i + 1]
        if y2 - y1 == 1:
            izhSlika[max_y - y2][x1] = '/'
        elif y2 - y1 == -1:
            izhSlika[max_y - y1][x1] = '\\'
    with open(izhod, 'w') as f:
        for vrstica in izhSlika:
            print(''.join(vrstica), file=f)

Družinski izlet

Na izlet v Hrčkovo deželo se je odpravilo nekaj družin. Seznam udeležencev izleta je podan v obliki seznama nizov priimkov in imen, ločenih z vejicami. Seveda ima lahko neka oseba več imen ali več priimkov, drugih znakov v imenih (npr. vezaj) pa ni. Primer seznama:

udelezenci = ['Novak, Janez Peter', 'Novak, Ana', 'Kovač Novak, Meta', 'Kovač, Petra']

1. podnaloga

Sestavi funkcijo vrniImePriimek(oseba), ki za dani niz oseba vrne par (ime, priimek)

Primeri:

>>> vrniImePriimek('Novak, Janez')
('Janez', 'Novak')
>>> vrniImePriimek('Kovač Novak, Matej')
('Matej', 'Kovač Novak')
>>> vrniImePriimek('Gott Hell, Ana Jana')
('Ana Jana', 'Gott Hell')

Uradna rešitev

def vrniImePriimek(niz):
    '''Niz razbijemo na ime in priimek - ločilo je vejica'''
    s = niz.split(',')
    return (s[1].strip(), s[0].strip())

2. podnaloga

Družine bi se rade posedle za mize tako, da bodo ob isti mizi sedeli člani iste družine. Napišite funkcijo razpored_po_mizah(udelezenci), ki naredi slovar, katerega ključi so priimki, vrednosti pa množice imen oseb iste družine. (Osebi sta člana iste družine, ko imata povsem enaka priimka.) Primer:

>>> razpored_po_mizah(['Novak, Janez', 'Kovač, Franc', 'Kovač, Jožefa', 'Novak, Micka', 'Kovač Novak, Matej'])
{'Novak': {'Janez', 'Micka'}, 'Kovač Novak': {'Matej'}, 'Kovač': {'Franc', 'Jožefa'}}

Uradna rešitev

def razpored_po_mizah(udelezenci):
    '''Vrne slovar z razporeditvijo po mizah'''
    mize = dict()
    for oseba in udelezenci:
        (ime, priimek) = vrniImePriimek(oseba)
        osebe = mize.get(priimek, set()) # iz slovarja dobimo množico oseb, ki že sedijo za mizo oz. prazno množico
        osebe.add(ime) # dodamo osebo k mizi
        mize[priimek] = osebe # sedaj pri družini priimek sede te osebe
    return mize

3. podnaloga

Organizator izleta se je odločil, da bo poiskal najbolj popularno ime. Če ima neka oseba več imen (npr. 'Kovač, Ana Beti'), štejemo vsako komponento posebej. Napišite funkcijo popularno_ime(udelezenci), ki vrne seznam najbolj popularnih imen, urejenih po abecedi. (Seznam ima več elementov le v primeru, ko je na vrhu popularnosti več imen.) Primer:

>>> popularno_ime(['Horvat, Ivan Jožef', 'Hočevar, Franc', 'Horvat, Franc', 'Novak, Jožef', 'Novak, Mici'])
['Franc', 'Jožef']

Uradna rešitev

def popularno_ime(udelezenci):
    '''Vrne seznam najbolj popularnih imen'''
    števciImena = dict()
    for oseba in udelezenci:
        (ime, priimek) = vrniImePriimek(oseba)
        komponente = ime.split() # ime razbijemo na dele 
        for k in komponente: # povečamo števce vseh imen
            števciImena[k] = števciImena.get(k, 0) + 1
    najPogostost = max(števciImena.values()) # kateri števec je največji
    # seznam imen s to največjo pogostostjo
    kateraImena = []
    for ime, frekvenca in števciImena.items():
        if frekvenca == najPogostost: 
            kateraImena.append(ime)           
    kateraImena.sort() # seznam imen mora biti urejen po abecedi
    return kateraImena

def popularno_imeV2(udelezenci):
    '''Vrne seznam najbolj popularnih imen'''
    # uporaba izpeljanih seznamov 
    števciImena = dict()
    for oseba in udelezenci:
        (ime, priimek) = vrniImePriimek(oseba)
        komponente = ime.split() # ime razbijemo na dele 
        for k in komponente: # povečamo števce vseh imen
            števciImena[k] = števciImena.get(k, 0) + 1
    m = max(števciImena.values()) # kateri števec je največji
    kateraImena = [ime for ime, frekvenca in števciImena.items() if frekvenca == m] # seznam imen s to pogostostjo
    kateraImena.sort() # seznam imen mora biti urejen po abecedi
    return kateraImena

4. podnaloga

Napišite funkcijo morda_sorodnika(prva_oseba, druga_oseba), ki za dve osebi, podani z nizoma oblike 'priimki, imena', vrne True, če se ujemata v vsaj enem od priimkov (in False sicer). Primer:

>>>> morda_sorodnika('Kovač Novak, Ana', 'Novak, Anja')
True
>>>> morda_sorodnika('Kovač Novak, Ana', 'Novak Novak, Anja')
True
>>>> morda_sorodnika('Kovač, Ana', 'Novak, Ana')
False

Uradna rešitev

def morda_sorodnika(prva_oseba, druga_oseba):
    '''Sta osebi morda sorodnika'''
    _, priimek_p = vrniImePriimek(prva_oseba) # imena sploh ne potrebujemo, zato _
    _, priimek_d = vrniImePriimek(druga_oseba)
    priimki_d = priimek_d.split() # vsi deli priimka
    for priimek in priimek_p.split():
        if priimek in priimki_d: # našli smo ujemanje v delu
            return True
    return False

5. podnaloga

Napišite funkcijo rodbina(oseba, udelezenci), ki vrne seznam oseb (skupaj z osebo oseba), ki so morda sorodniki dane osebe, ali pa morda sorodniki morebitnih sorodnikov dane oseba ali pa morda sorodniki morebitnih sorodnikov morebitnih sorodnikov danes osebe itn. Seznam naj bo urejen po abecedi. Primer:

>>> udelezenci = ['A, a', 'A B, b', 'C D, c', 'C A, c', 'Y, x']
>>> rodbina('A, x', udelezenci)
['A, x', 'A, a', 'A B, b', 'C A, c', 'C D, c']

Uradna rešitev

def rodbina_rek(nasi, vasi):
    '''Vrne seznam tistih iz seznama vasi, ki so morda sorodniki tistih iz nasi'''
    novi = [] # katere osebe bomo dodali na novo
    ostanek = [] # kateri del seznama vasi moramo še pregledati (kateri ne bodo neosredno sorodniki tistih iz nasi)
    for v in vasi: # pregledamo vse kandidate za sorodnike
        for n in nasi: # je v morda sorodnik komu iz nasi
            if morda_sorodnika(n, v): # če je, v dodamo med nove in gremo na naslednjega kandidata v vasi
                novi += [v]
                break
        else: # če v ni sorodnik nikomur (for se je izvedel do konca), ga bomo pogledali ali je sorodnik sorodnikov
            ostanek += [v] # ti bodo morda sorodniki sorodnikov ...
        
    if novi == []: # če v tem pregledu nismo dodali nikogra, so vsi sordniki že v nasi
        return nasi
    else:
        return rodbina_rek(nasi + novi, ostanek) # dodamo nove in zaradi tega pregledamo, če so v ostanek sorodniki teh dodanih
        
def rodbina(oseba, udelezenci):
    '''Vrne seznam  oseb (skupaj z osebo oseba), ki so morda sorodniki dane osebe, ali pa morda sorodniki
       morebitnih sorodnikov ... dane oseba'''       
    return sorted(rodbina_rek([oseba], udelezenci))

Slovensko-angleški slovarček

Slovar lahko v Pythonu predstavimo s slovarjem – presenetljivo, kajne? Natančneje, ključi v slovarju so besede, pripadajoče vrednosti pa so množice vseh možnih prevodov. Na primer:

{'je': {'is', 'eats'}, 'kosilo': {'lunch'},
 'mama': {'mother', 'mum', 'mummy'}}

1. podnaloga

Sestavite funkcijo obratni(slov), ki vrne obratni slovar danega slovarja slov.

>>> obratni({'je': {'is', 'eats'}, 'žre': {'bugs', 'eats'}})
{'bugs': {'žre'}, 'eats': {'je', 'žre'}, 'is': {'je'}}

Uradna rešitev

def obratni(slov):
    '''Vrne obratni slovar'''
    obrat = {}
    for beseda, prevodi in slov.items():
        for prevod in prevodi:
            besedePrevoda = obrat.get(prevod, set()) # obstoječi prevodi ali nova množica
            besedePrevoda.add(beseda)
            obrat[prevod] = besedePrevoda # popraviomo ali na novo postavimo eleemnt v slovar
    return obrat

2. podnaloga

Sestavite funkcijo nedvoumne_in_dvoumne(slov), ki vrne par dveh množic: v prvi naj bodo vse besede iz slovarja slov, ki imajo natanko en možen prevod, v drugi pa vse tiste, ki jih imajo več.

>>> nedvoumne_in_dvoumne({'je': {'is', 'eats'}, 'kosilo': {'lunch'}, 'solata': {'salad'}}
({'kosilo', 'solata'}, {'je'})

Uradna rešitev

def nedvoumne_in_dvoumne(slov):
    '''Vrne dv množici - prvo besede z enim prevodom in drugo vse ostale '''
    nedvoum, dvoum = set(), set()
    for beseda, prevodi in slov.items():
        if len(prevodi) == 1:
            nedvoum.add(beseda)
        else:
            dvoum.add(beseda)
    return nedvoum, dvoum

3. podnaloga

Sestavite funkcijo vsi_mozni_prevodi(slov, stav), ki vrne množico vseh možnih “prevodov” danega stavka stav glede na dani slovar slov. Stavek je niz, sestavljen iz besed, ločenih s presledki. Če besede ni v slovarju, naj v prevodu ostane taka, kakršna je.

Namig: sestavite še pomožno funkcijo, ki namesto niza stav sprejme seznam besed.

Uradna rešitev

def prevodi_pomozna(slov, besede):
    '''Vrne seznam, kjer so elementi seznami besed, ki predstavljajo možen prevod'''
    if len(besede) == 0:
        return [[]]
    else:
        vsi_prevodi = []
        prevodi = slov.get(besede[0], {besede[0]})
        prevodi_preostanka = prevodi_pomozna(slov, besede[1:])
        for prevod in prevodi:
            for prevod_preostanka in prevodi_preostanka:
                vsi_prevodi.append([prevod] + prevod_preostanka)
        return vsi_prevodi

def vsi_mozni_prevodi(slov, stav):
    '''Vrne seznam vse možnih prevodov'''
    besede = stav.split(' ') # razdelimo stavek na besede
    prevodi = prevodi_pomozna(slov, besede) #Seznam prevodov
    množicaPrevodov = set()
    for enPrevodSeznam in prevodi:
        enPrevod = ' '.join(enPrevodSeznam) # besede združimo s presledki
        množicaPrevodov.add(enPrevod)       
    return množicaPrevodov

4. podnaloga

Sestavite funkcijo preberi(vhod), ki prebere datoteko z imenom vhod in vrne slovar, zapisan v njej. Slovar je v datoteki predstavljen tako, da je vsaka beseda v svoji vrstici, pod njo pa so v vrsticah, ki se začnejo z znakom -, zapisani njeni prevodi. Na primer, v datoteki

je
- is
- eats
kosilo
- lunch
mama
- mother
- mum
- mummy

je zapisan ravno slovar

{'je': {'is', 'eats'}, 'kosilo': {'lunch'},
 'mama': {'mother', 'mum', 'mummy'}}

Predpostavite lahko, da je vhodna datoteka vedno zgornje oblike.

Uradna rešitev

def preberi(vhod):
    '''Iz datoteke sestavi slovar prevodov'''
    slov = {}
    for line in open(vhod):
        if line[0] == '-': # gre za prevod
            slov[beseda].add(line[2:].strip()) # znebimo se še - in presledka
        else:
            beseda = line.strip() # nopva beseda, ki so prevajamo
            slov[beseda] = set()
    return slov

Knjige

Primož rad bere knjige. Doma ima polne police samih dobrih strokovnih in leposlovnih knjig. Prijatelji si pri njem pogosto sposojajo knjige. Primož za vsako knjigo vodi evidenco o tem, komu jo je posodil. Podatke hrani v slovarju, kjer so ključi oznake knjig (nizi), vrednosti pa so seznami imen prijateljev (prav tako nizi), ki so si sposodili to knjigo, v kronološkem vrstnem redu. Tisti, ki ima knjigo trenutno pri sebi, je na zadnjem mestu v seznamu. Če ima Primož knjigo trenutno doma, potem je na zadnjem mestu v slovarju vrednost None. Primer:

evidenca_1 = {
    'hpotter1': ['Cilka', 'Bojan', 'Samo'],
    'hpotter2': ['Bojan', 'Cilka', None],
    'agot': ['Cilka', 'Špela', 'Bojan'],
    'acok': ['Samo', None],
    'vrabec90': ['Klemen', 'Bojan'],
    'vidav10': [None]
}

1. podnaloga

Napišite funkcijo imam_doma(evidenca), ki sprejme slovar, kot je opisan zgoraj, in vrne množico vseh knjig, ki jih ima Primož v tem trenutku doma. Zgled:

>>> imam_doma(evidenca_1)
{'hpotter2', 'acok', 'vidav10'}

Uradna rešitev

def imam_doma(evidenca):
    doma = set()
    for k, v in evidenca.items():
        if v[-1] is None:
            doma.add(k)
    return doma

2. podnaloga

Primož bi rad poznal za vsakega prijatelja množico knjig, ki jih ima ta prijatelj trenutno izposojene. Napišite funkcijo kdo_kaj(evidenca), ki sprejme slovar, kot je opisan zgoraj, in vrne slovar, kjer so ključi imena prijateljev, vrednosti pa množice knjig, ki jih imajo ti v tem trenutku sposojene. Zgled:

>>> kdo_kaj(evidenca_1)
{'Samo': {'hpotter1'}, 'Bojan': {'agot', 'vrabec90'}}

Uradna rešitev

def kdo_kaj(evidenca):
    trenutno = {}
    for k, v in evidenca.items():
        kdo = v[-1]
        if kdo is None:
            continue
        if kdo not in trenutno:
            trenutno[kdo] = set()
        trenutno[kdo].add(k)
    return trenutno

3. podnaloga

Primoža zanima, kaj njegovi prijatelji radi berejo. Sestavite funkcijo zgodovina(evidenca, ime), ki sprejme slovar, kot je opisan zgoraj, in ime enega od prijateljev. Funkcija naj vrne množico vseh knjig, ki jih je imel prijatelj z imenom ime kadarkoli izposojene. Zgled:

>>> zgodovina(evidenca_1, 'Bojan')
{'hpotter2', 'hpotter1', 'agot', 'vrabec90'}

Uradna rešitev

def zgodovina(evidenca, ime):
    malha = set()
    for k, v in evidenca.items():
        if ime in v:
            malha.add(k)
    return malha

4. podnaloga

Primoža zanima, kako strastni bralci so njegovi prijatelji. Napišite funkcijo statistika(evidenca), ki sprejme slovar, kot je opisan zgoraj in vrne nov slovar, kjer so ključi imena vseh prijateljev, ki so si pri Primožu kadarkoli izposodili kakšno knjigo, vrednost vsakega ključa pa je število različnih knjig, ki si jih je izposodil ta prijatelj. Zgled:

>>> statistika(evidenca_1)
{'Špela': 1, 'Cilka': 3, 'Bojan': 4, 'Samo': 2, 'Klemen': 1}

Uradna rešitev

def statistika(evidenca):
    drugovi = set()
    for v in evidenca.values():
        drugovi.update(v)
    if None in drugovi:
        drugovi.remove(None)
    return {druze: len(zgodovina(evidenca, druze)) for druze in drugovi}

Palindromi

1. podnaloga

Sestavite funkcijo palindrom(niz), ki vrne True kadar je niz palindrom, in False sicer.

Uradna rešitev

def palindrom(niz):
    return niz == niz[::-1]

2. podnaloga

Pravimo, da je beseda praktično palindrom, če ji je treba zbrisati natanko eno črko, da bi postala palindrom. Primer je beseda kolo, ki ji moramo zbrisati črko k, pa postane palindrom olo.

Sestavite funkcijo prakticno_palindrom(niz), ki preveri, ali je niz priktično palindrom. Vse znake (tudi presledke) v besedi obravnavamo enako.

Uradna rešitev

def prakticno_palindrom(niz):
    # za vsak i poskusimo izpustiti črko na i-tem mestu
    for i in range(len(niz)):
        # če smo dobili palindrom, končamo
        if palindrom(niz[:i] + niz[i + 1:]):
            return True
    # če je zanka prišla do konca, palindroma nismo našli
    return False

3. podnaloga

Pravimo, da je beseda skoraj palindrom, če ji je treba dodati ali izbrisati natanko eno črko oziroma zamenjati natanko eno črko z drugo, da bi postala palindrom. Primeri:

  • robot, kjer moramo t zamenjati z r,
  • jana, kjer moramo na konec dodati j.

Sestavite funkcijo skoraj_palindrom(niz), ki preveri, ali je niz skoraj palindrom. Vse znake (tudi presledke) v besedi obravnavamo enako.

Uradna rešitev

def skoraj_palindrom(niz):
    # Odstranjevanje in dodajanje črk preverimo s funkcijo prakticno_palindrom.
    # Razmislite, zakaj smo s tem pokrili tudi dodajanje ene črke.
    if prakticno_palindrom(niz):
        return True
    
    # Za vsak i poskusimo črko na i-tem mestu nadomestiti s pravilno.
    for i in range(len(niz)):
        if palindrom(niz[:i] + niz[-i - 1] + niz[i + 1:]):
            return True

    # Če je zanka prišla do konca, palindroma z zamenjavo ene črke nismo našli.
    return False

Datumi

1. podnaloga

Sestavite funkcijo je_prestopno(leto), ki vrne True, kadar je leto prestopno, in False, kadar ni.

Uradna rešitev

def je_prestopno(leto):
    return leto % 4 == 0 and leto % 100 != 0 or leto % 400 == 0

2. podnaloga

Sestavite funkcijo stevilo_dni(mesec, leto), ki vrne število dni danega meseca (podanega s številom med 1 in 12) v danem letu.

Uradna rešitev

def stevilo_dni(mesec, leto):
    if mesec == 2 and je_prestopno(leto):
        return 29
    elif mesec == 2:
        return 28
    elif mesec == 4 or mesec == 6 or mesec == 9 or mesec == 11:
        return 30
    else:
        return 31

3. podnaloga

Sestavite funkcijo je_veljaven_datum(dan, mesec, leto), ki vrne True natanko tedaj, kadar dan, mesec in leto določajo veljaven datum (torej mesec mora biti število med 1 in 12, dan pa mora ustrezati dnevu v tem mesecu).

Uradna rešitev

def je_veljaven_datum(dan, mesec, leto):
    return 1 <= mesec <= 12 and 1 <= dan <= stevilo_dni(mesec, leto)

Naloga 1 (str. 85)

http://lusy.fri.uni-lj.si/ucbenik/book/1205/index14.html

1. podnaloga

Naslednja funkcija naj bi vrnila število elementov tabele a, ki so manjši od x, vendar vsebuje napake. Popravi jo.

   def kolikoManjsih(a, x):
     n = len(a)
     koliko = 0
     for i in xrange(1, n):
       if a[i] != x:
         koliko = 1

Uradna rešitev

def kolikoManjsih(a, x):
  n = len(a)
  koliko = 0
  for i in range(0, n):
    if a[i] < x:
      koliko += 1
  return koliko

OOP I


Uporabnik

Dan je razred

def Uporabnik:
    def init(ime, priimek):
        self.ime = ime
        self.priimek = priimek

    def __str__(self)
        print(ime + " " + priimek)

1. podnaloga

Popravi napake v definiciji razreda. Atributa naj ostaneta ime in priimek.

 >>> jan = Uporabnik('Jan', 'Tisti')
 >>> print(jan)
 Jan Tisti
 >>> jan.priimek
 Tisti
 >>> kitajc = Uporabnik('A', 'Li')
 >>> print(kitajc)
 A Li
 >>> kitajc.ime
 A

Uradna rešitev

class Uporabnik:
    def __init__(self, ime, priimek):
        self.ime = ime
        self.priimek = priimek

    def __str__(self):
        return(self.ime + " " + self.priimek)

2. podnaloga

Spremeni konstruktor tako, da ne bo sprejemal imen in priimkov skupaj krajših od 5 črk.V tem primeru naj bo uporabnik Janez Slovenski.

 >>> jan = Uporabnik('Jan', 'Tisti')
 >>> print(jan)
 Jan Tisti
 >>> jan.priimek
 Tisti
 >>> kitajc = Uporabnik('A', 'Li')
 >>> print(kitajc)
 Janez Slovenski
 >>> kitajc.ime
 Janez

Uradna rešitev

class Uporabnik:
    def __init__(self, ime, priimek):
        if len(ime+priimek) < 5:
            ime = 'Janez'
            priimek = 'Slovenski'
        self.ime = ime
        self.priimek = priimek

    def __str__(self):
        return(self.ime + " " + self.priimek)

3. podnaloga

Dodaj metodo inicialke, ki vrne inicialke uporabnika. Pri tem predpostavi, da ima vsak ime in priimek iz ene besede.

 >>> jan = Uporabnik('Jan', 'Tisti')
 >>> jan.inicialke()
 J.T.
 >>> legenda = Uporabnik('Bruce', 'Lee')
 >>> legenda.inicialke()
 B.L.

Uradna rešitev

class Uporabnik:
    def __init__(self, ime, priimek):
        if len(ime+priimek) < 5:
            ime = 'Janez'
            priimek = 'Slovenski'
        self.ime = ime
        self.priimek = priimek

    def __str__(self):
        return(self.ime + " " + self.priimek)

    def inicialke(self):
        return self.ime[0] + '.' + self.priimek[0] + '.'

4. podnaloga

Ho Ši Minh se je pritožil in rekel, da so njegove inicialke H.Š.M. Podprla ga je tudi Ana Nina Vodičar Mehle, katere inicialke so po njenem A.N.V.M. . Peter Pavlovič Čehov pa je povedal, da se Ana Nina zmišljuje in da so njegove inicialke le P.Č. Da pa je bila zadeva še bolj zaostrena, se je v pogovor vmešal še Sebastian, ki pravi, da so njegove inicialke pač samo S.

Ker jim želiš vsem ustreči, dodaj še metodo polne_inicialke tako, da bo imela parameter polno. Če bo nastavljen na True, so inicialke sestavljene iz začetnic vseh besed, ki sestavljajo ime in priimek. Pri tem štejemo, da ime Ana Marija sestavljata dve besedi, ime Ana-Nuša pa le ena. Privzeta vrednost tega parametra pa je False, kar pomeni, da se vzame le prva črka imena in prva črka priimka (razen za tako pomembne osebe, kot so Sebastian ali pa Sting, ki so pač poznani le po imenu ali priimku - inicialke za oba sta S.).

 >>> jan = Uporabnik('Jan Jankovski', 'Tisti')
 >>> jan.polne_inicialke(True)
 J.J.T.
 >>> jan = Uporabnik('Jan Jankovski', 'Tisti')
 >>> jan.polne_inicialke()
 J.T.
 >>> jan = Uporabnik('Jan Jankovski', 'Tisti')
 >>> jan.polne_inicialke(False)
 J.T.

Uradna rešitev

class Uporabnik:
    def __init__(self, ime, priimek):
        if len(ime + priimek) < 5:
            ime = 'Janez'
            priimek = 'Slovenski'
        self.ime = ime
        self.priimek = priimek

    def __str__(self):
        return (self.ime + " " + self.priimek)

    def polne_inicialke(self, polno = False):
        imena = self.ime.split()
        priimki = self.priimek.split()
        ini = ''
        for im in imena:
            ini += im[0] + '.'
            if not polno: break # le prvo
        for im in priimki:
            ini += im[0] + '.'
            if not polno: break # le prvo
        return ini

Bitni cekini

Trenutno je na spletu zelo popularna digitalna valuta Bitcoin. Osnova za pošteno uporabo take valute so zapleteni kriptografski protokoli, mi pa bomo ubrali malo bolj poenostavljeno različico ter sestavili razred BitniCekin, s katerim bomo predstavili račun nekega lastnika te valute.

POZOR:

Da ne bo težav pri testiranju, pri 2., 3. in 4. podnalogi začnemo z

              class BitniCekini(BitniCekini):

To pomeni, da se v razred "skopirajo" vse definicije, ki ste jih v razred BitniCekini napisali prej. Seveda pa 1. podnalogo še vedno začnemo z

              class BitniCekini():

Druga možnost pa je, da podanloge vedno začnemo z

              class BitniCekini():

a potem v razred vedno napišemo vse metode, ki smo jih definirali v vseh prejšnjih podnalogah!

1. podnaloga

Sestavite razred BitniCekin s konstruktorjem __init__(self, stanje), ki sprejme začetno stanje na računu uporabnika (v valuti Bitcoin). Atribut, v katerega shranite stanje, naj bo poimenovan _stanje.

Argument stanje naj bo neobvezen in v primeru, ko ni podan, naj bo začetno stanje enako nič.

Uradna rešitev

class BitniCekin:
    def __init__(self, stanje=0):
        self._stanje = stanje

2. podnaloga

Sestavite metodo __str__(self), ki predstavi stanje na računu v obliki: 'Število bitnih cekinov na računu: ...'

Primer:

>>> racun = BitniCekin(6)
>>> print(racun)
Število bitnih cekinov na računu: 6

Uradna rešitev

class BitniCekin:
    def __init__(self, stanje=0):
        self._stanje = stanje

    def __str__(self):
        return 'Število bitnih cekinov na računu: {}'.format(self._stanje)

3. podnaloga

Sestavite metodi dvig(self, koliko) in polog(self, koliko), ki dvigneta oz. položita ustrezno količino bitnih cekinov na račun. Predpostavimo, da bo vrednost argumenta koliko vedno nenegativno celo število.

Pri metodi dvig upoštevajte, da stanje na računu ne sme biti negativno. V takšnem primeru se dvig ne sme izvesti.

Metoda dvig naj vrne True, če je dvig uspel in False, če ni. Metoda polog naj vrne stanje na računu po pologu.

Uradna rešitev

class BitniCekin:
    def __init__(self, stanje=0):
        self._stanje = stanje

    def __str__(self):
        return 'Število bitnih cekinov na računu: {}'.format(self._stanje)

    def dvig(self, koliko):
        '''dvig sresdtev in potrditev uspešnosti transakcije'''
        if koliko > self._stanje:
            return False
        else:
            self._stanje -= koliko
            return True

    def polog(self, koliko):
        '''polog sredstev in vrednost trenutnega stanja'''
        self._stanje += koliko
        return self._stanje

4. podnaloga

Sestavite funkcijo prenesi(racun1, racun2, koliko), ki iz računa racun1 prenese koliko cekinov na račun racun2. Funkcija prenesi naj ne bo znotraj razreda BitniCekin, saj ni objektna metoda, ampak je čisto običajna funkcija. Spremenljivki racun1 in racun2 sta seveda objekta tipa BitniCekin, kar ni potrebno preverjati!

Če na računu racun1 ni dovolj denarja, se transakcija ne sme izvršiti, torej mora stanje na obeh računih ostati nespremenjeno. Funkcija naj vrne uspešnost transakcije (True, če je transakcija uspela, in False, če ni).

Uradna rešitev

def prenesi(racun1, racun2, koliko):
    '''prenos iz enega bitnega cekina na drugega in
       indikacija uspešnosti transakcije '''
    if racun1.dvig(koliko):
        racun2.polog(koliko)
        return True
    return False

Datumi

POZOR:

Da ne bo težav pri testiranju, pri 2., 3., 4. in 5. podnalogi začnemo z

              class Datumi(Datumi):

To pomeni, da se v razred "skopirajo" vse definicije, ki ste jih v razred Datumi napisali prej. Seveda pa 1. podnalogo še vedno začnemo z

              class Datumi():

Druga možnost pa je, da podanloge vedno začnemo z

              class Datumi():

a potem v razred vedno napišemo vse metode, ki smo jih definirali v vseh prejšnjih podnalogah!

1. podnaloga

Definirajte razred Datum, s katerim predstavimo datum. Najprej sestavite konstruktor __init__(self, dan, mesec, leto). Atributi razreda Datum naj bodo poimenovani _dan, _mesec in _leto.

>>> d = Datum(21, 5, 2013)
>>> d.leto
2013

Uradna rešitev

class Datum:
    def __init__(self, dan, mesec, leto):
        self._dan = dan
        self._mesec = mesec
        self._leto = leto

2. podnaloga

Razredu Datum dodajte (torej vaš razred mora vsebovati to, kar zahteva 1. naloga) metodo __str__, ki predstavi datum v berljivi obliki 'dan. mesec. leto'.

>>> d = Datum(21, 5, 2013)
>>> print(d)
21. 5. 2013

Uradna rešitev

class Datum:
    def __init__(self, dan, mesec, leto):
        self._dan = dan
        self._mesec = mesec
        self._leto = leto

    def __str__(self):
        return "{0}. {1}. {2}".format(self._dan, self._mesec, self._leto)

3. podnaloga

Razredu Datum dodajte (torej vaš razred mora vsebovati to, kar zahteva 1. naloga) metodo __lt__, ki datum primerja z drugim datumom (metoda naj vrne True, če je prvi datum manjši, in False, če ni).

Ko definirate to metodo, lahko datume primerjate kar z operatorjema < in >. Na primer:

>>> Datum(31, 12, 1999) < Datum(1, 1, 2000)
True

Uradna rešitev

class Datum:
    def __init__(self, dan, mesec, leto):
        self._dan = dan
        self._mesec = mesec
        self._leto = leto

    def __str__(self):
        return "{0}. {1}. {2}".format(self._dan, self._mesec, self._leto)

    def __lt__(self, other):
        # uporabimo Pythonovo vgrajeno leksikografsko primerjavo
        return (self._leto, self._mesec, self._dan) < (other._leto, other._mesec, other._dan)

4. podnaloga

Razredu Datum dodajte (torej vaš razred mora vsebovati to, kar zahteva 1. naloga) metodo __eq__, ki datum primerja z drugim datumom (metoda naj vrne True, če sta datuma enaka, in False, če nista).

Ko definirate to metodo, lahko datume primerjate kar z operatorjema == in !=. Na primer:

>>> Datum(31, 12, 1999) != Datum(1, 1, 2000)
True

Uradna rešitev

class Datum:
    def __init__(self, dan, mesec, leto):
        self._dan = dan
        self._mesec = mesec
        self._leto = leto

    def __eq__(self, other):
        # uporabimo Pythonovo vgrajeno leksikografsko primerjavo
        return (self._leto, self._mesec, self._dan) == (other._leto, other._mesec, other._dan)

5. podnaloga

Razredu Datum dodajte (torej vaš razred mora vsebovati to, kar zahteva 1. naloga) metodo iso8601, ki vrne niz s predstavitvijo datuma oblike yyyy-mm-dd. Na prvem mestu je letnica, nato mesec in na koncu dan. Števila so ločena z znaki '-'. Letnica je štirimestno število, dan in mesec pa dvomestni števili (po potrebi jih dopolnite z vodilnimi ničlami.

>>> Datum(17, 3, 1999).iso8601()
'1999-03-17'

Uradna rešitev

class Datum:
    def __init__(self, dan, mesec, leto):
        self._dan = dan
        self._mesec = mesec
        self._leto = leto

    def iso8601(self):
        return "{0:04}-{1:02}-{2:02}".format(self._leto, self._mesec, self._dan)

Bakterije

1. podnaloga

Definirajte razred Bakterija, s katerim bomo predstavili bakterije. Sestavite konstruktor, ki kot parameter sprejema niz, ki opisuje genski zapis bakterije in ta niz priredi atributu DNA.

Preveriti pa morate, če je dani genski zapis veljaven, se pravi, da vsebuje samo črke 'A','G','C','T' (okrajšave za adenin, gvanin, citozin in timin). Če niz ni veljaven, naj atribut DNA postane prazen niz. Konstruktor mora narediti tudi atribut generacija, ki naj dobi začetno vrednost 0.

Primer:

>>> a = Bakterija('GAAATCGGT')
>>> a.DNA
'GAAATCGGT'
>>> a.generacija
0

Primer z neveljavnim genskim zapisom:

>>> a = Bakterija('ABCDEFGH')
>>> a.DNA
''

Uradna rešitev

class Bakterija:
    def __init__(self, dna):
        self.DNA = dna if all([x in 'ACGT' for x in dna]) else ''
        self.generacija = 0
        # če vam razumevanje zgornje kode dela težave - stvar bi lahko
        # napisali tudi
        #  self.DNA = dna
        #  # ali morda ni veljavno?
        #  for znak in dna:
        #     if znak not in 'ACGT':
        #         self.DNA = '' # napačen znak, zato ...
        #         break
        #  self.generacija = 0

2. podnaloga

Bakterije se zelo rade delijo. Običajne bakterije delijo tako, da iz ene nastaneta dve novi, naše bakterije pa so posplošene bakterije, ki se lahko delijo na poljubno število novih bakterij. Pri tem se njihov genski zapis prekopira, poveča pa se števec generacije.

Definirajte metodo __floordiv__, s katero je definirana operacija // (celoštevilčno deljenje). Metoda sprejema le en parameter (delitelj), ki pove, koliko bakterij dobimo po delitvi. Metoda mora vrniti seznam novih bakterij, ki imajo isti DNA kot začetna bakterija, imajo pa povečan števec generacije. Pri tem pa morate še upoštevati, da se bakterije s praznim genskim zapisom ne morejo deliti. V takšnem primeru naj metoda vrne prazen seznam.

Primer:

>>> a = Bakterija('GAAATCGGT')
>>> nove_bakterije = a//3
>>> len(nove_bakterije)
3
>>> print(nove_bakterije)
[<__main__.Bakterija object at 0x7ffd41edac50>, <__main__.Bakterija object at 0x7ffd41edab90>, <__main__.Bakterija object at 0x7ffd41edab50>]
>>> nove_bakterije[0].generacija
1
>>> nove_bakterije[0].DNA
'GAAATCGGT'

Uradna rešitev

class Bakterija(Bakterija):
    # zgornji zapis pomeni, da "poberemo" vse, kar smo do sedaj
    # že napisali v razredu Bakterija
    def __floordiv__(self, n):
        if not self.DNA:
            return []
        nove = []
        for i in range(n):
            b = Bakterija(self.DNA)
            b.generacija = self.generacija + 1
            nove.append(b)
        return nove

3. podnaloga

Bakterije se lahko tudi združujejo. Pri tem nastane nova bakterija, katere genski zapis je kombinacija genskih zapisov obeh bakterij, ki nastopata v združevanju. Zapis se združuje po takšnem pravilu: izmenično se jemlje po eno črko iz obeh genskih zapisov, ko pa pridemo do konca krajšega genskega zapisa, se preostanek daljšega doda na konec novega zapisa. Tako bi na primer pri združevanju zapisov ACT in GCTATGCCC dobili AGCCTTATGCCC.

Definirajte metodo __add__, s katero je definirana operacija + (seštevanje). To pomeni, da bomo bakterije lahko združevali kar z uporabo operatorja +. Metoda sprejema en parameter: drugo bakterijo, ki jo bomo združili s prvo. Metoda naj vrne novo bakterijo, ki ima združen genski zapis ter za ena večji števec generacije kot starejša od obeh bakterij, ki nastopata v združevanju.

Primer:

>>> a = Bakterija('ACT')
>>> b = Bakterija('GCTATGCCC')
>>> c = a + b
>>> c.DNA
'AGCCTTATGCCC'
>>> c.generacija
1

Uradna rešitev

class Bakterija(Bakterija):
    # zgornji zapis pomeni, da "poberemo" vse, kar smo do sedaj
    # že napisali v razredu Bakterija
    def __add__(self, other):
        dna = ''
        ml = min(len(self.DNA), len(other.DNA))
        for i in range(ml):
            dna += self.DNA[i] + other.DNA[i]
        dna += self.DNA[ml:] + other.DNA[ml:]
        nova = Bakterija(dna)
        nova.generacija = 1 + max(self.generacija, other.generacija)
        return nova

4. podnaloga

Bakterije lahko tudi mutirajo, pri čemer se jim spremeni genski zapis. Na primer, podzaporedje AAG se pri mutaciji spremeni v podzaporedje AAT. Pri mutaciji se vedno spremenijo vse pojavitve danega podzaporedja, če pa genski zapis bakterije takšnega podzaporedja ne vsebuje, pa se med mutacijo DNA seveda ne spremeni.

Napišite metodo mutacija, ki sprejme dva parametra, ki povesta, v kaj se pri mutaciji spremeni dano podzaporedje genskega zapisa. Metoda naj vrne novo bakterijo, ki ima mutiran genski zapis in za ena večji števec generacije. Upoštevajte, da se mutacije genskega zapisa vedno odvijajo v smeri od leve proti desni (običajna smer v katero naraščajo indeksi).

Upoštevajte tudi, da bakterije brez genskega zapisa ne morejo mutirati. V takšnem primeru naj metoda vrne isto, nespremenjeno bakterijo (nasvet: self).

Nasvet: pomagajte si z metodo replace!

Primer:

>>> a = Bakterija('GAAATCGGT')
>>> m = a.mutacija('AAT', 'CAT')
>>> m.DNA
'GACATCGGT'
>>> m.generacija
1

Uradna rešitev

class Bakterija(Bakterija):
    # zgornji zapis pomeni, da "poberemo" vse, kar smo do sedaj
    # že napisali v razredu Bakterija
    def mutacija(self, fr, to):
        if not self.DNA:
            return self

        MDNA = self.DNA.replace(fr, to)
        nova = Bakterija(MDNA)
        nova.generacija = self.generacija + 1
        return nova

Mnogokotnik

Mnogokotnik v ravnini lahko predstavimo s seznamom njegovih oglišč (pari števil). Na primer seznam

[(0, 0), (1, 0), (1, 1), (0, 1)]

opisuje kvadrat, seznam

[(2, 0), (2, 1), (0, 2), (-2, 1), (-2, 0), (0, 1)]

pa opisuje (nekonveksen) šestkotnik.

1. podnaloga

Sestavite razred Mnogokotnik, s katerim predstavimo ravninski mnogokotnik. Najprej sestavite konstruktor __init__(self, ogl), kjer je ogl seznam njegovih oglišč. Atribut razreda naj bo poimenovan oglisca.

Nekateri ljudje mnogokotnike zapisujejo tako, da na konec seznama oglišč ponovno postavijo prvo oglišče (s tem poudarijo, da je mnogokotnik sklenjen). Zgornji kvadrat bi torej zapisali takole:

[(0, 0), (1, 0), (1, 1), (0, 1), (0, 0)]

V našem razredu Mnogokotnik naj se dolžina seznama oglisca ujema s številom oglišč mnogokotnika. Podvojeno oglišče na koncu seznama naj konstruktor po potrebi odstrani. Zgled:

>>> p = Mnogokotnik([(0, 0), (1, 0), (1, 1), (0, 1), (0, 0)])
>>> p.oglisca
[(0, 0), (1, 0), (1, 1), (0, 1)]

Predpostavite lahko, da mnogokotniki ne bodo izrojeni (tj. če imata dve stranici neprazen presek, sta nujno sosednji, presek pa je natanko njuno skupno oglišče).

Uradna rešitev

class Mnogokotnik(object):
    def __init__(self, oglisca):
        self.oglisca = list(oglisca)
        if self.oglisca[0] == self.oglisca[-1]:
            self.oglisca.pop()

2. podnaloga

V razredu Mnogokotnik definirajte metodo obseg(self), ki izračuna in vrne njegov obseg. Zgled:

>>> p = Mnogokotnik([(0, 0), (1, 0), (1, 1), (0, 1), (0, 0)])
>>> p.obseg()
4.0

Uradna rešitev

def dolzina_daljice(a, b):
    ax, ay = a
    bx, by = b
    return ((ax - bx) ** 2 + (ay - by) ** 2) ** 0.5


class Mnogokotnik(Mnogokotnik):
    def obseg(self):
        ob = 0
        n = len(self.oglisca)
        for i in range(n):
            ob += dolzina_daljice(self.oglisca[i], self.oglisca[(i + 1) % n])
        return ob

3. podnaloga

V razredu Mnogokotnik definirajte metodo ploscina(self), ki izračuna in vrne njegovo ploščino.

Ploščina mnogokotnika (brez samopresečišč) z oglišči $(x_1, y_1), (x_2, y_2), \ldots, (x_n, y_n)$ je $|D|/2$, kjer je $D = x_1 y_2 - x_2 y_1 + x_2 y_3 - x_3 y_2 + \ldots + x_{n-1} y_n - x_n y_{n-1} + x_n y_1 - x_1 y_n$.

>>> p = Mnogokotnik([(0, 0), (1, 0), (1, 1), (0, 1)])
>>> p.ploscina()
1.0

Uradna rešitev

class Mnogokotnik(Mnogokotnik):
    def ploscina(self):
        a = 0
        n = len(self.oglisca)
        for i in range(n):
            x, y = self.oglisca[i]
            x2, y2 = self.oglisca[(i + 1) % n]
            a += x * y2 - x2 * y
        return abs(a) / 2

4. podnaloga

V razredu Mnogokotnik definirajte metodo je_konveksen(self), ki vrne True, če je mnogokotnik konveksen in False sicer.

Predstavljajte si, da je ravnina, na kateri leži mnogokotnik, vložena v trirazsežni prostor tako, da je $z = 0$. Na bodo $P$, $Q$ in $R$ tri zaporedna oglišča mnogokotnika. Naj bo $u$ vektor od $P$ do $Q$ in naj bo $v$ vektor od $Q$ do $R$. Potem je $z$-komponenta vektorskega produkta vektorjev $u$ in $v$ pozitivna, če $PQR$ "zavije v levo", in negativna sicer. Mnogokotnik je konveksen, če vedno "zavijamo v isto smer", ko delamo obhod po robu mnogokotnika. Zgled:

>>> p = Mnogokotnik([(0, 0), (1, 0), (1, 1), (0, 1)])
>>> p.je_konveksen()
True
>>> q = Mnogokotnik([(2, 0), (2, 1), (0, 2), (-2, 1), (-2, 0), (0, 1)])
>>> q.je_konveksen()
False

Predpostavite lahko, da nobeni dve zaporedni stranici ne oklepata iztegnjenega kota (180 °).

Uradna rešitev

def vektorski_produkt(a, b):
    ax, ay, az = a
    bx, by, bz = b
    return (ay * bz - az * by, az * bx - ax * bz, ax * by - ay * bx)

def smer_vrtenja(u, v, w):
    ux, uy = u
    vx, vy = v
    wx, wy = w
    z = vektorski_produkt((vx - ux, vy - uy, 0), (wx - vx, wy - vy, 0))[2]
    return 1 if z > 0 else -1

class Mnogokotnik(Mnogokotnik):
    def je_konveksen(self):
        n = len(self.oglisca)
        znak = smer_vrtenja(self.oglisca[0], self.oglisca[1], self.oglisca[2])
        for i in range(1, n):
            u = self.oglisca[i % n]
            v = self.oglisca[(i + 1) % n]
            w = self.oglisca[(i + 2) % n]
            znak2 = smer_vrtenja(u, v, w)
            if znak2 * znak < 0:
                return False
        return True

OOP II


Ulomki

POZOR:

Da ne bo težav pri testiranju, pri vseh podnalogah začnemo z

              class Ulomek(Ulomek):

To pomeni, da se v razred "skopirajo" vse definicije, ki ste jih v razred Ulomek napisali prej. Seveda pa 1. podnalogo še vedno začnemo z

              class Ulomek():

Druga možnost pa je, da podnaloge vedno začnemo z

              class Ulomek():

a potem v razred vedno napišemo vse metode, ki smo jih definirali v vseh prejšnjih podnalogah!

1. podnaloga

Izven razreda sestavite funkcijo gcd(m, n), ki izračuna največji skupni delitelj števil m in n.

Uradna rešitev

def gcd(m, n):
    while n != 0:
        m, n = n, m % n
    return m

2. podnaloga

Definirajte razred Ulomek, s katerim predstavimo ulomek. Števec in imenovalec sta celi števili, pri čemer je morebiten negativen predznak vedno v števcu. Komponenti naj se imenujeta _st in _im. Ulomki naj bodo vedno okrajšani.

Najprej definirajte konstruktor __init__(self, st, im).

Uradna rešitev

class Ulomek:
    def __init__(self, st, im):
        if im < 0:
            st, im = -st, -im
        d = gcd(st, im)
        self._st = st // d
        self._im = im // d

3. podnaloga

Definirajte metodo __str__, ki predstavi ulomek v obliki "st/im".

Uradna rešitev

class Ulomek(Ulomek):
    def __str__(self):
        return "{0}/{1}".format(self._st, self._im)

4. podnaloga

Definirajte metodo __eq__, ki vrne True če sta dva ulomka enaka, in False sicer.

Uradna rešitev

class Ulomek(Ulomek):
    def __eq__(self, other):
        return self._st == other._st and self._im == other._im

5. podnaloga

Definirajte metodo __add__, ki vrne vsoto dveh ulomkov. Ko definirate to metodo, lahko ulomke seštevate kar z operatorjem +. Na primer:

>>> print(Ulomek(1, 6) + Ulomek(1, 4))
5/12

Uradna rešitev

class Ulomek(Ulomek):
    def __add__(self, other):
        a, b = self._st, self._im
        c, d = other._st, other._im
        return Ulomek(a * d + b * c, b * d)

6. podnaloga

Definirajte metodo __sub__, ki vrne razliko dveh ulomkov. Ko definirate to metodo, lahko ulomke odštevate kar z operatorjem -.

Uradna rešitev

class Ulomek(Ulomek):
    def __sub__(self, other):
        a, b = self._st, self._im
        c, d = other._st, other._im
        return Ulomek(a * d - b * c, b * d)

7. podnaloga

Definirajte metodo __mul__, ki vrne zmnožek dveh ulomkov. Ko definirate to metodo, lahko ulomke množite kar z operatorjem *.

Uradna rešitev

class Ulomek(Ulomek):
    def __mul__(self, other):
        a, b = self._st, self._im
        c, d = other._st, other._im
        return Ulomek(a * c, b * d)

8. podnaloga

Definirajte metodo __truediv__, ki vrne kvocient dveh ulomkov. Ko definirate to metodo, lahko ulomke delite kar z operatorjem /.

Uradna rešitev

class Ulomek(Ulomek):
    def __truediv__(self, other):
        a, b = self._st, self._im
        c, d = other._st, other._im
        return Ulomek(a * d, b * c)

9. podnaloga

Definirajte funkcijo priblizek(n), ki vrne vsoto $1/0! + 1/1! + … + 1/n!$. Poglejte, ali je izračunana vrednost blizu števila $e$.

Uradna rešitev

def priblizek(n):
    def fakulteta(i):
        return 1 if i == 0 else i * fakulteta(i - 1)

    return sum((Ulomek(1, fakulteta(i)) for i in range(n + 1)), Ulomek(0, 1))

Preiskovanje dreves

Dan je razred Drevo, ki predstavlja dvojiško drevo. Za osnovno vzemite naslednjo kodo:

class Drevo:

    def __init__(self, *args, **kwargs):
        if args:
            self.prazno = False
            self.vsebina = args[0]
            self.levo = kwargs.get('levo', Drevo())
            self.desno = kwargs.get('desno', Drevo())
        else:
            self.prazno = True

    def __repr__(self, zamik=''):
        if self.prazno:
            return 'Drevo()'.format(zamik)
        elif self.levo.prazno and self.desno.prazno:
            return 'Drevo({1})'.format(zamik, self.vsebina)
        else:
           return 'Drevo({1},\n{0}      levo = {2},\n{0}      desno = {3})'.\
               format(
                   zamik,
                   self.vsebina,
                   self.levo.__repr__(zamik + '             '),
                   self.desno.__repr__(zamik + '              ')
               )

    def __eq__(self, other):
        return ((self.prazno and other.prazno) or
                (not self.prazno and not other.prazno and
                self.vsebina == other.vsebina and
                self.levo == other.levo and
                self.desno == other.desno))

Konstruktor je že implementiran; prav tako metodi __repr__ in __eq__. Drevo na spodnji (ASCII art) sliki:

     5
   /   \
  3     2
 /     / \
1     6   9

sestavimo takole:

>>> d = Drevo(5,
              levo=Drevo(3, levo=Drevo(1)),
              desno=Drevo(2, levo=Drevo(6), desno=Drevo(9)))

1. podnaloga

Razredu dodajte metodo vsota(self), ki vrne vsoto vseh števil v drevesu. Zgled (kjer je d kot zgoraj):

>>> d.vsota()
26

Uradna rešitev

class Drevo:

    def __init__(self, *args, **kwargs):
        if args:
            self.prazno = False
            self.vsebina = args[0]
            self.levo = kwargs.get('levo', Drevo())
            self.desno = kwargs.get('desno', Drevo())
        else:
            self.prazno = True

    def __repr__(self, zamik=''):
        if self.prazno:
          return 'Drevo()'.format(zamik)
        elif self.levo.prazno and self.desno.prazno:
          return 'Drevo({1})'.format(zamik, self.vsebina)
        else:
          return 'Drevo({1},\n{0}      levo = {2},\n{0}      desno = {3})'.\
            format(
              zamik,
              self.vsebina,
              self.levo.__repr__(zamik + '             '),
              self.desno.__repr__(zamik + '              ')
            )

    def __eq__(self, other):
        return ((self.prazno and other.prazno) or
                (not self.prazno and not other.prazno and
                 self.vsebina == other.vsebina and
                 self.levo == other.levo and
                 self.desno == other.desno))
    
    def vsota(self):
        if self.prazno:
            return 0
        else:
            return self.vsebina + self.levo.vsota() + self.desno.vsota()

2. podnaloga

Dodajte metodo stevilo_listov(self), ki vrne število listov v drevesu. Zgled (kjer je d kot zgoraj):

>>> d.stevilo_listov()
3

Uradna rešitev

class Drevo():
    def __init__(self, *args, **kwargs):
        if args:
            self.prazno = False
            self.vsebina = args[0]
            self.levo = kwargs.get('levo', Drevo())
            self.desno = kwargs.get('desno', Drevo())
        else:
            self.prazno = True

    def __repr__(self, zamik=''):
        if self.prazno:
            return 'Drevo()'.format(zamik)
        elif self.levo.prazno and self.desno.prazno:
            return 'Drevo({1})'.format(zamik, self.vsebina)
        else:
            return 'Drevo({1},\n{0}      levo = {2},\n{0}      desno = {3})'. \
                format(
                zamik,
                self.vsebina,
                self.levo.__repr__(zamik + '             '),
                self.desno.__repr__(zamik + '              ')
            )

    def __eq__(self, other):
        return ((self.prazno and other.prazno) or
                (not self.prazno and not other.prazno and
                 self.vsebina == other.vsebina and
                 self.levo == other.levo and
                 self.desno == other.desno))

    def vsota(self):
        if self.prazno:
            return 0
        else:
            return self.vsebina + self.levo.vsota() + self.desno.vsota()

    def stevilo_listov(self):
        if self.prazno:
            return 0
        elif self.levo.prazno and self.desno.prazno:
            return 1
        else:
            return self.levo.stevilo_listov() + self.desno.stevilo_listov()

3. podnaloga

Dodajte metodo minimum(self), ki vrne najmanjše število v drevesu. Če je drevo prazno, naj metoda vrne None. Zgled (kjer je d kot zgoraj):

>>> d.minimum()
1
>>> Drevo().minimum()
None

Uradna rešitev

class Drevo(Drevo):
    def __init__(self, *args, **kwargs):
        if args:
            self.prazno = False
            self.vsebina = args[0]
            self.levo = kwargs.get('levo', Drevo())
            self.desno = kwargs.get('desno', Drevo())
        else:
            self.prazno = True

    def __repr__(self, zamik=''):
        if self.prazno:
            return 'Drevo()'.format(zamik)
        elif self.levo.prazno and self.desno.prazno:
            return 'Drevo({1})'.format(zamik, self.vsebina)
        else:
            return 'Drevo({1},\n{0}      levo = {2},\n{0}      desno = {3})'. \
                format(
                zamik,
                self.vsebina,
                self.levo.__repr__(zamik + '             '),
                self.desno.__repr__(zamik + '              ')
            )

    def __eq__(self, other):
        return ((self.prazno and other.prazno) or
                (not self.prazno and not other.prazno and
                 self.vsebina == other.vsebina and
                 self.levo == other.levo and
                 self.desno == other.desno))

    def vsota(self):
        if self.prazno:
            return 0
        else:
            return self.vsebina + self.levo.vsota() + self.desno.vsota()

    def stevilo_listov(self):
        if self.prazno:
            return 0
        elif self.levo.prazno and self.desno.prazno:
            return 1
        else:
            return self.levo.stevilo_listov() + self.desno.stevilo_listov()

    def minimum(self):
        if self.prazno:
            return None
        else:
            # Vrednost izraza `a or b` je `a`, če je `bool(a) == True`,
            # in `b` sicer. Torej, to je ekvivalentno izrazu `a if a else b`.
            # Opomba: `bool(None) == False`.
            levi_minimum = self.levo.minimum() or float('inf')
            desni_minimum = self.desno.minimum() or float('inf')
            return min(self.vsebina, levi_minimum, desni_minimum)

Mafija

V neki mafijski organizaciji so člani urejeni hierarhično. Vsakdo razen botra (vrhovnega šefa) ima natanko enega nadrejenega. Vsak mafijec ima lahko pod seboj največ dva podrejena (levega in desnega). Primer takšne mafijske organizacije:

mafija = Drevo('Salvatore', 320,
               levo=Drevo('Bernardo', 200,
                          levo=Drevo('Matteo', 50),
                          desno=Drevo('Carlo', 100,
                                      levo=Drevo('Rosalia', 70),
                                      desno=Drevo('Tommaso', 50))),
               desno=Drevo('Francesco', 120,
                           levo=Drevo('Giuseppe', 70),
                           desno=Drevo('Antonio', 60)))

Mafijci morajo zbirati denar s kriminalnimi dejavnostmi. Tisti, ki imajo podrejene, pa ga poleg tega poberejo še od svojih podrejenih. Ves “prisluženi” in prejeti denar morajo oddati svojemu nadrejenemu.

Boter je posumil, da nekateri člani goljufajo. Nekaj denarja, ki ga poberejo od podrejenih, zadržijo zase. Od vsakega člana je pridobil podatek o tem, koliko denarja je oddal naprej. Podatke je shranil v podatkovno strukturo Drevo, ki je že implementirana.

1. podnaloga

Botra zanima, koliko denarja zaradi goljufov “ponikne”. Želi, da sestavite metodo koliko_ponikne(self), ki vrne skupno vsoto denarja, ki ponikne. Primer (če mafija ustreza zgornjemu drevesu):

>>> mafija.koliko_ponikne()
30

Uradna rešitev

class Drevo:
    
    def __init__(self, *args, **kwargs):
        if len(args) > 0:
            self.prazno = False
            self.ime = args[0]
            self.vrednost = args[1]
            self.levo = kwargs.get('levo', Drevo())
            self.desno = kwargs.get('desno', Drevo())
        else:
            self.prazno = True

    def __repr__(self):
        if self.prazno:
            return 'Drevo()'
        else:
            opis = repr(self.ime) + ', ' + repr(self.vrednost)
            if not self.levo.prazno: opis += ', levo={0}'.format(self.levo)
            if not self.desno.prazno: opis += ', desno={0}'.format(self.desno)
            return 'Drevo({0})'.format(opis)
    
    def get_vrednost(self):
        return self.vrednost if not self.prazno else 0
    
    def koliko_ponikne(self):
        if self.prazno:
            return 0
        utajil = max(self.levo.get_vrednost() + self.desno.get_vrednost() - self.get_vrednost(), 0)
        return utajil + self.levo.koliko_ponikne() + self.desno.koliko_ponikne()

2. podnaloga

Ko je boter dognal, koliko denarja ponikne, je totalno po████. Pri priči hoče imeti imena vseh goljufov! Napišite metodo goljufi(self), ki vrne množico goljufov. Vsak goljuf naj bo predstavljen z naborom. Prva kompotenta naj bo ime goljufa, druga komponenta pa količina denarja, ki ga je utajil. Primer:

>>> mafija.goljufi()
{(’Carlo’, 20), (’Francesco’, 10)}

Uradna rešitev

class Drevo:
    def __init__(self, *args, **kwargs):
        if len(args) > 0:
            self.prazno = False
            self.ime = args[0]
            self.vrednost = args[1]
            self.levo = kwargs.get('levo', Drevo())
            self.desno = kwargs.get('desno', Drevo())
        else:
            self.prazno = True

    def __repr__(self):
        if self.prazno:
            return 'Drevo()'
        else:
            opis = repr(self.ime) + ', ' + repr(self.vrednost)
            if not self.levo.prazno: opis += ', levo={0}'.format(self.levo)
            if not self.desno.prazno: opis += ', desno={0}'.format(self.desno)
            return 'Drevo({0})'.format(opis)

    def get_vrednost(self):
        return self.vrednost if not self.prazno else 0

    def goljufi(self):
        if self.prazno:
            return set()
        utajil = max(self.levo.get_vrednost() + self.desno.get_vrednost() - self.get_vrednost(), 0)
        return ({(self.ime, utajil)} if utajil > 0 else set()) | self.levo.goljufi() | self.desno.goljufi()

3. podnaloga

Botru se dozdeva, da so najbolj pridne majhne ribe. To so tisti mafijci, ki nimajo pod seboj nobenega podrejenega. Tistim, ki imajo podrejene, se reče velike ribe. Napišite metodo zasluzek(self), ki vrne par (nabor) dveh števili, pri čemer je:

  • prvo število skupna vsota denarja, ki ga zaslužijo majhne ribe;
  • drugo število skupna vsota denarja, ki ga zaslužijo velike ribe (brez pobirkov od podrejenih).

Primer:

>>> mafija.zasluzek()
(300, 50)

Uradna rešitev

class Drevo:
    def __init__(self, *args, **kwargs):
        if len(args) > 0:
            self.prazno = False
            self.ime = args[0]
            self.vrednost = args[1]
            self.levo = kwargs.get('levo', Drevo())
            self.desno = kwargs.get('desno', Drevo())
        else:
            self.prazno = True

    def __repr__(self):
        if self.prazno:
            return 'Drevo()'
        else:
            opis = repr(self.ime) + ', ' + repr(self.vrednost)
            if not self.levo.prazno: opis += ', levo={0}'.format(self.levo)
            if not self.desno.prazno: opis += ', desno={0}'.format(self.desno)
            return 'Drevo({0})'.format(opis)

    def get_vrednost(self):
        return self.vrednost if not self.prazno else 0
    def zasluzek(self):
        if self.prazno:
            return 0, 0
        l_male, l_velike = self.levo.zasluzek()
        d_male, d_velike = self.desno.zasluzek()
        mala = self.levo.prazno and self.desno.prazno
        prispevek = max(self.get_vrednost() - self.levo.get_vrednost() - self.desno.get_vrednost(), 0)
        m, v = l_male + d_male, l_velike + d_velike
        if mala:
            m += prispevek
        else:
            v += prispevek
        return m, v

Polinomi

Definirajte razred Polinom, s katerim predstavimo polinom v spremenljivki $x$. Polinom predstavimo s seznamom njegovih koeficientov, kjer je $k$-ti element seznama koeficient pri $x^k$. Na primer, polinom $x^3 + 2 x + 7$ predstavimo s Polinom([7, 2, 0, 1]). Razmislite, kaj predstavlja Polinom([]). Zadnji koeficient v seznamu mora biti neničelen.

1. podnaloga

Sestavite konstruktor __init__(koef), ki nastavi tabelo koeficientov.

Uradna rešitev

class Polinom:

    def __init__(self, koef):
        # že na začetku se znebimo vseh ničelnih vodilnih koeficientov
        zadnji = len(koef)
        while zadnji > 0 and koef[zadnji - 1] == 0:
            zadnji -= 1
        self._koef = koef[:zadnji]

2. podnaloga

Sestavite metodo stopnja, ki vrne stopnjo polinoma. Stopnja ničelnega polinoma je v matematiki po dogovoru $-\infty$, pri nas pa bo $-1$.

Uradna rešitev

class Polinom(Polinom):
    def stopnja(self):
        return len(self._koef) - 1

3. podnaloga

Sestavite metodo __eq__ za primerjanje polinomov.

Uradna rešitev

class Polinom(Polinom):
    def __eq__(self, other):
        return self._koef == other._koef

4. podnaloga

Sestavite metodo __add__ za seštevanje polinomov. Pozor: pri seštevanju se lahko zgodi, da se nekateri začetni koeficienti pokrajšajo: $(x^3 + 2 x + 7) + (- x^3 - 2 x + 10) = 17$

Uradna rešitev

class Polinom(Polinom):
    def __add__(self, other):
        # predpostavimo, da ima levi polinom vsaj toliko členov kot desni
        if len(self._koef) < len(other._koef):
            return other + self

        # nato vzamemo levi polinom in kosoma prištevamo desnega
        koef_vsote = self._koef
        for n, a in enumerate(other._koef):
            koef_vsote[n] += a

        return Polinom(koef_vsote)

5. podnaloga

Sestavite metodo __mul__ za množenje polinomov.

Uradna rešitev

class Polinom(Polinom):
    def __mul__(self, other):
        # če je eden od polinomov ničelen, je ničelen tudi produkt
        if not self._koef or not other._koef:
            return Polinom([])

        # oba polinoma z ničlami podaljšamo do iste dolžine
        levi = self._koef + len(other._koef) * [0]
        desni = other._koef + len(self._koef) * [0]

        koef_prod = [sum(levi[i] * desni[n - i] for i in range(n + 1))
                     for n in range(len(levi))]
        return Polinom(koef_prod)

6. podnaloga

Sestavite metodo __str__, ki predstavi polinom v čitljivi obliki.

>>> print(Polinom([1, -2, 3, -1]))
-x^3 + 3 x^2 - 2 x + 1
>>> print(Polinom([-1, 0, 0, 1]))
x^3 - 1

Uradna rešitev

class Polinom(Polinom):
    def __str__(self):
        def monom(a, n):
            # Sestavimo si člen za monom. Na začetku in na koncu damo še oglate
            # oklepaje, ki jih bomo na koncu sicer pobrisali, da bomo vmes lahko
            # na poseben način obravnavali nekatere kombinacije znakov.
            monom = "[{0} x^{1}]".format(a, n)
            # popravimo potence in koeficiente
            monom = monom.replace("^1]", "") # ^1 pobrišemo le na koncu niza
            monom = monom.replace(" x^0", "") # pobrišemo presledek pred x^0
            monom = monom.replace("[1 x", "x") # 1 mora biti na začetku monoma
            monom = monom.replace("-1 x", "-x")
            monom = monom.replace("[", "").replace("]", "")
            return monom

        # "seštejemo" vse momone z neničelnimi koeficienti
        # pred tem moramo koeficiente obrniti v vrstni red kot v izpisu
        niz = " + ".join(reversed([monom(a, n)
                         for n, a in enumerate(self._koef) if a != 0]))
        niz = niz.replace("+ -", "- ") # popravimo negativne koeficiente

        # če smo dobili niz, ga vrnemo, sicer izpišemo ničelni polinom
        return niz or "0"

Redki vektorji

Vektorjem, ki vsebujejo veliko število ničelnih elementov, pravimo redki vektorji. Namesto običajne predstavitve, pri kateri navedemo vse elemente vektorja, lahko uporabimo bolj kompaktno predstavitev, kjer ničelne elemente izpustimo, za neničelne pa si zapomnimo, na katerih mestih so.

Primer: Vektor

a = [1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 4, 0, 0]

lahko namesto v obliki dolgega seznama z veliko ničlami zapišemo v kompaktni obliki kot slovar, kjer so ključi indeksi neničelnih elementov iz dolgega zapisa:

r = {0: 1, 10: 4, 5: 5, 6: 1}

Vrstni red indeksov seveda ni pomemben, saj imamo slovar. Ničelni vektor predstavimo kar s praznim slovarjem {}.

1. podnaloga

Napišite funkcijo stisni(vektor), ki sprejme vektor kot seznam števil ter vrne redko obliko vektorja, tj. slovar, v katerem so ključi indeksi neničelnih vrednosti, vrednosti pa te neničelne vrednosti.

Primer:

>>> stisni([1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 4])
{0: 1, 10: 4, 5: 5, 6: 1}
>>> stisni([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 99])
{0: 1, 15: 99}

Uradna rešitev

def stisni(vektor):
    '''vrne redko obliko vektorja, v obliki indeks : vrednost'''
    sv = dict()
    for i, x in enumerate(vektor):
        if x != 0: # zanimajo nas le neničelni elementi
            sv[i] = x
    return sv

2. podnaloga

Definirajte razred Vektor, s katerim bomo predstavili redke vektorje. Sestavite konstruktor, ki kot parameter sprejme vektor v stisnjeni obliki (slovar) ter ga priredi atributu elementi.

Sestavite še metodo __len__, ki vrne število neničelih elementov redkega vektorja. Metoda nima argumentov (razen obveznega argumenta self, ki pove, da gre za metodo danega razreda). Metoda __len__ se bo izvedla, kadar bomo na objektu tipa Vektor uporabili Pythonovo vgrajeno funkcijo len, kot je prikazano v spodnjem primeru.

Primer:

>>> a = Vektor({0: 1, 10: 4, 5: 5, 6: 1})
>>> a.elementi
{0: 1, 10: 4, 5: 5, 6: 1}
>>> len(a)
4

Uradna rešitev

class Vektor:
    def __init__(self, data):
        self.elementi = data

    def __len__(self):
        return len(self.elementi)

3. podnaloga

Definirajte metodi __add__ in __sub__, s katerima boste definirali operatorja + in - nad objekti tipa Vektor. Gre za običajno seštevanje in odštevanje vektorjev, le da morate upoštevati, da seštevamo oz. odštevamo redki obliki, ki sta predstavljeni s slovarjema.

Vsaka od metod sprejema en argument in sicer drugi vektor, ki nastopa v aritmetični operaciji, vrne pa naj nov objekt tipa Vektor, ki je vsota oz. razlika danih vektorjev.

Nasvet: Upoštevajte, da so v rezultatu lahko neničelne vrednosti le na tistih indeksih, pri katerih je neničeln vsaj en od obeh operandov – pomagajte si z unijo obeh množic indeksov.

Primer:

>>> a = Vektor({0: 1})
>>> b = Vektor({0: 1, 3: 1})
>>> c = a + b
>>> c.elementi
{0: 2, 3: 1}

Uradna rešitev

class Vektor(Vektor):
    def __add__(self, other):
        data = dict()
        keys = set(self.elementi).union(other.elementi) # set iz slovarja pobere le ključe!
        for k in keys:
            # poberemo k kar iz obeh, če ga ni, vzamemo 0
            v = self.elementi.get(k, 0) + other.elementi.get(k, 0)
            if v != 0: # če se vrednosti nista izničili (negativna št!)
                data[k] = v
        return Vektor(data)

    def __sub__(self, other):
        data = dict()
        keys = set(self.elementi).union(other.elementi) # unija ključev, ker je set(slovar) istio kot set(slovar.keys())
        for k in keys:
            v = self.elementi.get(k, 0) - other.elementi.get(k, 0)
            if v != 0: # če se vrednosti nista izničili
                data[k] = v
        return Vektor(data)

4. podnaloga

Definirajte še metodo razsiri, ki vrne običajno (dolgo) obliko vektorja. Pri tem se razširjena oblika ne sme končati z 0, saj drugače razširitev ni enolična (glej drugi primer!)

Primer:

>>> a = Vektor(stisni([1, 0, 0, 0, 0, 0, 0, 0, 9]))
>>> a.elementi
{0: 1, 8: 9}
>>> a.razsiri()
[1, 0, 0, 0, 0, 0, 0, 0, 9]
>>> Vektor(stisni([1, 0, 0, 0])).razsiri()
[1]

Uradna rešitev

class Vektor(Vektor):
    def razsiri(self):
        '''razpihne redki vektor v "običajno" obliko'''
        if self.elementi == dict():
            return list() # prazen seznam
        else:
            # maksimalni indeks je maksimalna vrednost ključa v slovarju
            # vrednosti za vsak indeks bodo bodisi 0 bodisi ustrezne vrednosti iz slovarja
            return [self.elementi.get(i, 0) for i in range(max(self.elementi) + 1)]

Verižni seznam


Vozel

1. podnaloga

Razred Vozel predstavlja osnovni gradnik verižnega seznama. Pri tej nalogi bomo dodali razredu Vozel nekaj novih metod. Za osnovno vzemite naslednjo implementacijo:

class Vozel:
    """
    Osnovni sestavni del verižnega seznama.
    """

    def __init__(self, podatek=None, naslednji=None):
        self.podatek = podatek
        self.naslednji = naslednji

    def __str__(self):
        return str(self.podatek)

Razredu Vozel dodajte metodo vrni_seznam(self), ki vrne seznam vseh podatkov v verižnem seznamu. Zgled:

>>> v1 = Vozel('nedelja')
>>> v2 = Vozel('sobota', v1)
>>> v3 = Vozel('petek', v2)
>>> v3.vrni_seznam()
['petek', 'sobota', 'nedelja']

Uradna rešitev

class Vozel:
    """
    Osnovni sestavni del verižnega seznama.
    """

    def __init__(self, podatek=None, naslednji=None):
        self.podatek = podatek
        self.naslednji = naslednji
    
    def __str__(self):
        return str(self.podatek)

    def vrni_seznam(self):
        """
        Vrni seznam s podatki, ki so shranjeni v vozlih.
        """
        sez = []
        p = self
        while p is not None:
            sez.append(p.podatek)
            p = p.naslednji
        return sez

2. podnaloga

Razredu Vozel dodajte metodo dodaj_na_zacetek(self, x), ki doda nov vozel na začetek verižnega seznama self, ter vrne referenco na novi vozel. Zgled:

>>> v = Vozel('nedelja')
>>> v = v.dodaj_na_zacetek('sobota')
>>> v = v.dodaj_na_zacetek('petek')
>>> v.vrni_seznam()
['petek', 'sobota', 'nedelja']

Uradna rešitev

class Vozel(Vozel):
    
    def dodaj_na_zacetek(self, x):
        """
        Dodaj nov vozel na začetek verižnega seznama.
        """
        return Vozel(x, self)

3. podnaloga

Razredu Vozel dodajte metodo dodaj_na_konec(self, x), ki doda nov vozel na konec verižnega seznama self. Zgled:

>>> v = Vozel('petek')
>>> v.dodaj_na_konec('sobota')
>>> v.dodaj_na_konec('nedelja')
>>> v.vrni_seznam()
['petek', 'sobota', 'nedelja']

Uradna rešitev

class Vozel(Vozel):
    
    def dodaj_na_konec(self, x):
        """
        Dodaj nov vozel na konec verižnega seznama.
        """
        p = self
        while p.naslednji != None:
            p = p.naslednji
        p.naslednji = Vozel(x)

4. podnaloga

Razredu Vozel dodajte metodo __len__(self), ki vrne število vozlov v verižnem seznamu. Zgled:

>>> v = Vozel('sobota')
>>> v.dodaj_na_konec('nedelja')
>>> v = v.dodaj_na_zacetek('petek')
>>> len(v)
3

Uradna rešitev

class Vozel(Vozel):
    
    def __len__(self):
        """
        Vrni število vozlov v verižnem seznamu.
        """
        if self.naslednji is None:
            return 1
        return 1 + len(self.naslednji)

5. podnaloga

Dodali bomo še nekaj funkcij za delo z verižnimi seznami.

Sestavite funkcijo stevilska_veriga(a, b), ki vrne verižni seznam, ki vsebuje števila od a do b. Funkcija naj vrne referenco na prvi vozel. Predpostavite, da velja a <= b. Zgled:

>>> v = stevilska_veriga(3, 7)
>>> v.vrni_seznam()
[3, 4, 5, 6, 7]

Uradna rešitev

def stevilska_veriga(a, b):
    """
    Vrni verižni seznam, ki vsebuje vsa števila od a do b.
    """
    if a == b:
        return Vozel(a)
    return Vozel(a, stevilska_veriga(a + 1, b))

6. podnaloga

Sestavite funkcijo seznam_sodih(vozel), ki kot argument dobil referenco na vozel in vrne seznam z vsemi sodimi elementi v tem verižnem seznamu. Predpostavite lahko, da so vsi podatki cela števila. Zgled:

>>> v = stevilska_veriga(3, 11)
>>> seznam_sodih(v)
[4, 6, 8, 10]

Uradna rešitev

def seznam_sodih(vozel):
    """
    Vrni seznam vseh sodih elementov v verižnem seznamu.
    """
    l = []
    while vozel is not None:
        if vozel.podatek % 2 == 0:
            l.append(vozel.podatek)
        vozel = vozel.naslednji
    return l

7. podnaloga

Sestavite funkcijo iz_seznama(l), ki kor argument sprejme seznam l ter vrne verižni seznam, ki kot podatke vsebuje elemente seznama l. Funkcija naj vrne referenco na prvi vozel. Predpostavite lahko, da je seznam l neprazen. Zgled:

>>> v = iz_seznama(['torek', 'sreda', 'četrtek', 'petek'])
>>> v.dodaj_na_konec('sobota')
>>> v.vrni_seznam()
['torek', 'sreda', 'četrtek', 'petek', 'sobota']

Uradna rešitev

def iz_seznama(l):
    """
    Vrni verižni seznam, ki bo imel kot podatke elemente seznama l.
    """
    prvi = Vozel(l[0])
    p = prvi
    for i in range(1, len(l)):
        p.naslednji = Vozel(l[i])
        p = p.naslednji
    return prvi

8. podnaloga

Razredu Vozel dodajte metodo zadnji_vozel(self), ki vrne referenco na zadnji vozel v verižnem seznamu. Zgled:

>>> v = iz_seznama(['sreda', 'četrtek', 'petek', 'sobota'])
>>> z = v.zadnji_vozel()
>>> print(z)
'sobota'

Uradna rešitev

class Vozel(Vozel):
    
    def zadnji_vozel(self):
        p = self
        while p.naslednji is not None:
            p = p.naslednji
        return p

9. podnaloga

Razredu Vozel dodajte metodo vstavi_na_mesto(self, n, x), ki dobi število n in podatek x. Metoda naj na n-to mesto v verižni seznam vstavi nov vozel s podatkom x. Funkcija naj vrne referenco na prvi vozel v verižnem seznamu. Če verižni seznam ni dovolj dolg, dodajte ustrezno število vozlov, ki imajo kot podatek None. Zgled:

>>> v = iz_seznama(['sreda', 'četrtek', 'sobota'])
>>> v = v.vstavi_na_mesto(2, 'petek')
>>> v.vrni_seznam()
['sreda', 'četrtek', 'petek', 'sobota']

Uradna rešitev

class Vozel(Vozel):

    def vstavi_na_mesto(self, n, x):
        if n == 0:
            return self.dodaj_na_zacetek(x)
        if self.naslednji == None:
            if n == 1:
                self.naslednji = Vozel(x)
                return self
            else:
                self.naslednji = Vozel()
        self.naslednji = self.naslednji.vstavi_na_mesto(n - 1, x)
        return self

Še o vozlih

1. podnaloga

Razred Vozel predstavlja osnovni gradnik verižnega seznama. Pri tej nalogi bomo dodali razredu Vozel nekaj novih metod. Za osnovno vzemite naslednjo implementacijo:

class Vozel:
    """
    Osnovni sestavni del verižnega seznama.
    """

    def __init__(self, podatek=None, naslednji=None):
        self.podatek = podatek
        self.naslednji = naslednji

    def __str__(self):
        return str(self.podatek)

Dodajte še funkcijo iz_seznama, ki ste jo sestavili pri prejšnji nalogi in metodo vrni_seznam. Če želite, lahko dodate še kakšno metodo oz. funkcijo.

Razredu Vozel dodajte metodo podvoji_verigo(self), ki naj sestavi in vrne novo kopijo verige, stare pa naj ne spreminja. Zgled:

>>> v = iz_seznama([2, 3, 4, 5])
>>> v2 = v.podvoji_verigo()
>>> v2.podatek = 11
>>> v.vrni_seznam()
[2, 3, 4, 5]
>>> v2.vrni_seznam()
[11, 3, 4, 5]

Uradna rešitev

class Vozel:
    """
    Osnovni sestavni del verižnega seznama.
    """

    def __init__(self, podatek=None, naslednji=None):
        self.podatek = podatek
        self.naslednji = naslednji

    def __str__(self):
        return str(self.podatek)
    
    def vrni_seznam(self):
        """
        Vrni seznam s podatki, ki so shranjeni v vozlih.
        """
        sez = []
        p = self
        while p is not None:
            sez.append(p.podatek)
            p = p.naslednji
        return sez


def iz_seznama(l):
    """
    Vrni verižni seznam, ki bo imel kot podatke elemente seznama l.
    """
    if not l: # če je seznam prazen
        return None
    prvi = Vozel(l[0])
    p = prvi
    for i in range(1, len(l)):
        p.naslednji = Vozel(l[i])
        p = p.naslednji
    return prvi


class Vozel(Vozel):
    
    def podvoji_verigo(self):
        if self.naslednji is None:
            return Vozel(self.podatek)
        return Vozel(self.podatek, self.naslednji.podvoji_verigo())

2. podnaloga

Sestavite funkcijo stakni_verigi(v1, v2), ki kot argumenta prejme referenci na dva vozla in sestavi novo verigo, ki jo dobi tako, da stakne obe verigi. To pomeni, da morata ostati verigi v1 in v2 nespremenjeni, funkcija pa vrne povsem novo verigo! Na primer:

>>> v1 = iz_seznama([1, 2, 3])
>>> v2 = iz_seznama([4, 5, 6])
>>> v = stakni_verigi(v1, v2)
>>> v.vrni_seznam()
[1, 2, 3, 4, 5, 6]
>>> v1.vrni_seznam()
[1, 2, 3]

Uradna rešitev

def stakni_verigi(v1, v2):
    '''Iz verig v1 in v2 naredi novo verigo.
       Prvotni ostaneta nespremenjeni! '''
    v = v1.podvoji_verigo() # nova, podvojena veriga
    # postavimo se na konec nove
    p = v
    while p.naslednji is not None:
        p = p.naslednji
    # in tej verigi dodamo kopijo verige v2
    p.naslednji = v2.podvoji_verigo()
    return v

def stakni_verigi(v1, v2):
    ''' "Grša oblika"
       Iz verig v1 in v2 naredi novo verigo.
       Prvotni ostaneta nespremenjeni! '''
    s1 = v1.vrni_seznam()
    s2 = v2.vrni_seznam()
    return iz_seznama(s1 + s2)

3. podnaloga

Sestavite funkcijo multipliciraj_verigo(v, n), ki kot argumenta prejme referenco na vozel in naravno število n ter vrne verigo spremeni, tako da iz vsakega vozla naredi n zaporednih kopij. Funkcija naj vrne referenco na prvi vozel. Na primer:

>>> v = iz_seznama([7, 5, 2])
>>> v = multipliciraj_verigo(v, 3)
>>> v.vrni_seznam()
[7, 7, 7, 5, 5, 5, 2, 2, 2]

Uradna rešitev

def multipliciraj_verigo(v, n, k=None):
    if v is None:
        return None
    if k is None:
        k = n
    if k == 1:
        v.naslednji = multipliciraj_verigo(v.naslednji, n)
        return v
    v2 = Vozel(v.podatek, multipliciraj_verigo(v, n, k-1))
    return v2

4. podnaloga

Sestavite funkcijo na_zadrgo(v1, v2), ki kot argumenta prejme referenci na dva vozla. Funkcija naj vrne verigo, ki jo dobi tako, da "na zadrgo" združi obe verigi. Funkcija naj vrne referenco na prvi vozel tako dobljene verige. Na primer:

>>> v1 = iz_seznama([7, 5, 2])
>>> v2 = iz_seznama([1, 3, 4, 9, 8])
>>> v = na_zadrgo(v1, v2)
>>> v.vrni_seznam()
[7, 1, 5, 3, 2, 4, 9, 8]

Uradna rešitev

def na_zadrgo(v1, v2):
    if v1 is None:
        return v2
    if v2 is None:
        return v1
    v = na_zadrgo(v1.naslednji, v2.naslednji)
    v1.naslednji = v2
    v2.naslednji = v
    return v1

5. podnaloga

Sestavite funkcijo odpni_zadrgo(v), ki kot argument prejme referenco na vozel ter vrne dve verigi, ki jih dobi tako, da "odpne zadrgo", tj. vozle naj razdeli med dve verigi. Funkcija naj vrne par in sicer referenci na začetna vozla v obeh verigah. Na primer:

>>> v = iz_seznama([7, 5, 2, 1, 3, 4, 9, 8])
>>> v1, v2 = odpni_zadrgo(v)
>>> v1.vrni_seznam()
[7, 2, 3, 9]
>>> v2.vrni_seznam()
[5, 1, 4, 8]

Uradna rešitev

def odpni_zadrgo(v):
    if v is None:
        return None, None
    vn = v.naslednji
    if vn is None:
        return v, None
    v1, v2 = odpni_zadrgo(v.naslednji.naslednji)
    v.naslednji = v1
    vn.naslednji = v2
    return v, vn

6. podnaloga

Sestavite funkcijo odstrani(v, x), ki kot argumenta prejme referenco na vozel ter element x. Funkcija naj verigo popravi, tako da odstrani vozle, ki kot podatek vsebujejo element x. Funkcija naj vrne referenco na začetni vozel verige. Na primer:

>>> v = iz_seznama([7, 5, 2, 5, 3, 5])
>>> v = odstrani(v, 5)
>>> v.vrni_seznam()
[7, 2, 3]

Uradna rešitev

def odstrani(v, x):
    if v is None:
        return None
    if v.podatek != x:
        v.naslednji = odstrani(v.naslednji, x)
        return v
    return odstrani(v.naslednji, x)

7. podnaloga

Sestavite funkcijo sodi_in_lihi(v), ki kot argument prejme referenco na vozel ter vrne dve verigi, ki jih dobi tako, da v eno zloži vozle, ki vebujejo sode podatke, v drugo pa vozle, ki vsebujejo lihe podatke. Funkcija naj vrne par in sicer referenci na začetna vozla v obeh verigah. Na primer:

>>> v = iz_seznama([7, 5, 2, 1, 3, 4, 9, 8])
>>> v1, v2 = sodi_in_lihi(v)
>>> v1.vrni_seznam()
[2, 4, 8]
>>> v2.vrni_seznam()
[7, 5, 1, 3, 9]

Uradna rešitev

def sodi_in_lihi(v):
    if v is None:
        return None, None
    v1, v2 = sodi_in_lihi(v.naslednji)
    if v.podatek % 2 == 0:
        v.naslednji = v1
        return v, v2
    v.naslednji = v2
    return v1, v

8. podnaloga

Razredu Vozel dodajte metodo je_urejen(self), ki vrne True, če je verižni seznam urejen naraščajoče in False, če ni. Zgled:

>>> v = iz_seznama([1, 2, 4, 7, 8])
>>> v.je_urejen()
True
>>> v = iz_seznama([1, 2, 4, 3, 7])
>>> v.je_urejen()
False

Uradna rešitev

class Vozel(Vozel):

    def je_urejen(self):
        if self.naslednji is None:
            return True
        if self.podatek > self.naslednji.podatek:
            return False
        return self.naslednji.je_urejen()

9. podnaloga

Razredu Vozel dodajte metodo vstavi_v_urejenega(self, x), ki podatek x vstavi na primerno mesto v verižni seznam. Predpostavka je, da je verižni seznam urejen. Metoda naj vrne začetni vozel tega verižnega seznama. Zgled:

>>> v = iz_seznama([1, 2, 4, 7, 8])
>>> v = v.vstavi_v_urejenega(3)
>>> v.vrni_seznam()
[1, 2, 3, 4, 7, 8]

Uradna rešitev

class Vozel(Vozel):

    def vstavi_v_urejenega(self, x):
        if x <= self.podatek:
            v = Vozel(x, self)
            return v
        if self.naslednji is None:
            self.naslednji = Vozel(x)
        else:
            self.naslednji = self.naslednji.vstavi_v_urejenega(x)
        return self

10. podnaloga

Sestavite funkcijo preuredi_parnost(v), ki kot argument prejme referenco na vozel ter verižni seznam tako preuredi, da postavi vse vozle, ki vsebujejo lih podatek na začetek, vozle, ki vsebujejo sod podatek pa na konec. Funkcija naj vrne par in sicer referenci na začetna vozla v obeh verigah. Na primer:

>>> v = iz_seznama([7, 5, 2, 1, 3, 4, 9, 8])
>>> v = preuredi_parnost(v)
>>> v.vrni_seznam()
[7, 5, 1, 3, 9, 2, 4, 8]

Uradna rešitev

def pripoji_verigo(v1, v2):
    p = v1
    while p.naslednji is not None:
        p = p.naslednji
    p.naslednji = v2
    return v1

def preuredi_parnost(v):
    s, l = sodi_in_lihi(v)
    return pripoji_verigo(l, s)

11. podnaloga

Pepi je sestavil verižni seznam in ugotovil, da je elemente nizal v seznam v napačnem vrstnem redu. Zdaj je treba seznam obrniti, tako da obstoječih vozlov ne brišemo in ne dodajamo novih. Sestavite funkcijo obrni_na_mestu(v), ki dobi verižni seznam in ga obrne na mestu. Funkcijo mora vrniti referenco na prvi vozel. Zgled:

>>> v = iz_seznama([7, 5, 2, 1])
>>> v = obrni_na_mestu(v)
>>> v.vrni_seznam()
[1, 2, 5, 7]

Uradna rešitev

def obrni_na_mestu(v):
    prejsnji = None
    while v is not None:
        nasl = v.naslednji
        v.naslednji = prejsnji
        prejsnji = v
        v = nasl
    return prejsnji

Izštevanka

1. podnaloga

Otroci se bodo igrali skrivalnice. Izbrati morajo nekoga, ki bo mižal, zato so se vsi postavili v krog. Uporabljajo zelo popularno izštevanko, ki ima n zlogov. Sestavite funkcijo kdo_mizi(imena, n), ki kot argument dobi seznam imen in naravno število n. Funkcija naj najprej sestavi krožni seznam (tako kot verižni seznam, samo da zadnji element kaže na prvega). Nato vozle izločamo iz kroga, tako da zbrišemo $n$-tega in začnemo ponovno izštevanje pri naslednjemu. Funkcija na vrne ime otroka, ki bo mižal. Zgled:

>>> kdo_mizi(['Mojca', 'Janez', 'Micka', 'Peter', 'Matjaž', 'Jožefa'], 5)
'Mojca'

Uradna rešitev

class Vozel:
    """
    Osnovni sestavni del verižnega seznama.
    """

    def __init__(self, podatek=None, naslednji=None):
        self.podatek = podatek
        self.naslednji = naslednji

    def __str__(self):
        return str(self.podatek)
    
    def vrni_seznam(self):
        """
        Vrni seznam s podatki, ki so shranjeni v vozlih.
        """
        sez = []
        p = self
        while p is not None:
            sez.append(p.podatek)
            p = p.naslednji
        return sez


def iz_seznama(l):
    """
    Vrni verižni seznam, ki bo imel kot podatke elemente seznama l.
    """
    prvi = Vozel(l[0])
    p = prvi
    for i in range(1, len(l)):
        p.naslednji = Vozel(l[i])
        p = p.naslednji
    return prvi

def kdo_mizi(imena, n):
    i = 0
    while len(imena) > 1:
        i = (i + n - 1) % len(imena)
        del imena[i]
    return imena[0]

2. podnaloga

Sestavite še funkcijo zgodovina_izstevanja(imena, n), ki naj deluje podobno kot funkcija iz prve podnaloge, le da vrne seznam, ki vsebuje imena v takem vrstnem redu, kot smo jih izločali pri izštevanki. Zgled:

>>> zgodovina_izstevanja(['Mojca', 'Janez', 'Micka', 'Peter', 'Matjaž', 'Jožefa'], 5)
['Matjaž', 'Peter', 'Jožefa', 'Janez', 'Micka', 'Mojca']

Uradna rešitev

def zgodovina_izstevanja(imena, n):
    i = 0
    ret = []
    while len(imena) > 0:
        i = (i + n - 1) % len(imena)
        ret.append(imena[i])
        del imena[i]
    return ret

3. podnaloga

Zdaj pa sestavite še funkcijo odhajanje_iz_kroga(imena, n), ki deluje podobno kot prejšnja funkcija, le da vrne verižni seznam namesto običajnega seznama. Vozli naj bodo urejeni tako, da je v zadnjem vozlu shranjeno ime otroka, ki je najprej zapustil krog. Zgled:

>>> v = odhajanje_iz_kroga(['Mojca', 'Janez', 'Micka', 'Peter', 'Matjaž', 'Jožefa'], 5)
>>> v.vrni_seznam()
['Mojca', 'Micka', 'Janez', 'Jožefa', 'Peter', 'Matjaž']

Uradna rešitev

def odhajanje_iz_kroga(imena, n):
    i = 0
    ret = []
    while len(imena) > 0:
        i = (i + n - 1) % len(imena)
        ret.append(imena[i])
        del imena[i]
    return iz_seznama(ret[::-1])

OOP III


Zajec

Vaš stari znanec Jože goji zajce. Zajci so se v zadnjih letih tako namnožili, da si Jože enostavno ne more več zapomniti vseh. Zato potrebuje primeren informacijski sistem. V pomoč mu sestavite razred, ki bo vseboval vse potrebne podatke o resničnem zajcu.

1. podnaloga

Sestavite razred Zajec s konstruktorjem __init__(self, teza, starost), ki predstavlja zajca z dano težo in starostjo. Vrednosti shranite v atributa z imenoma teza in starost.

Uradna rešitev

class Zajec:
    
    def __init__(self, teza, starost):
        self.teza = teza
        self.starost = starost

2. podnaloga

Sestavite metodo nahrani(self, hrana), kjer je argument hrana teža hrane, ki jo damo zajcu. Pri hranjenju se teža zajca poveča za 30 % teže hrane, ki jo zajec poje. Zgled:

>>> z = Zajec(5, 2)
>>> z.nahrani(2)
>>> z.teza
5.6

Uradna rešitev

class Zajec(Zajec):
    
    def nahrani(self, hrana):
        self.teza += hrana * 0.3

3. podnaloga

Sestavite metodo __str__(self), ki vrne predstavitev razreda Zajec z nizom oblike 'Zajec težak X kg, star Y let.'.

Primer:

>>> z = Zajec(5, 2)
>>> print(z)
'Zajec težak 5 kg, star 2 let.'

Opomba: Funkcija print na svojem argumentu pokliče metodo __str__ in izpiše niz, ki ga ta metoda vrne. Metoda __str__ običajno vrne razumljiv opis objekta, ki naj bi ga razumeli tudi ne-programerji.

Uradna rešitev

class Zajec(Zajec):
    
    def __str__(self):
        return "Zajec težak {1} kg, star {0} let.".format(self.starost, self.teza)

4. podnaloga

Sestavite še metodo __repr__(self), ki vrne predstavitev razreda Zajec kot niz oblike 'Zajec(X, Y)', kjer je X teža, Y pa starost zajca.

Primer:

>>> z = Zajec(5, 2)
>>> z
Zajec(5, 2)

Opomba: Če v interaktivni konzoli pokličemo nek objekt, se izpiše niz, ki ga vrne klic metode __repr__ na tem objektu. Priporočilo je, da je niz, ki ga vrne metoda __repr__, veljavna programska koda v Pythonu, ki ustvari identično kopijo objekta.

Uradna rešitev

class Zajec(Zajec):
    
    def __repr__(self):
        return 'Zajec({1}, {0})'.format(self.starost, self.teza)

5. podnaloga

Sestavite metodo __lt__(self, drugi), ki dva zajca primerja med sabo. Metoda naj vrne True, če je prvi zajec manjši od drugega in False sicer.

Manjši zajec je tisti, ki je lažji. Če pa imata zajca enako maso, je manjši tisti, ki je mlajši (tj. ima manjše število let).

>>> Zajec(5, 3) < Zajec(6, 2)
True
>>> Zajec(3, 1) < Zajec(2, 2)
False
>>> Zajec(4, 3) < Zajec(4, 2)
False

Uradna rešitev

class Zajec(Zajec):
    
    def __lt__(self, drugi):
        if self.teza < drugi.teza:
            return True
        if self.teza == drugi.teza and self.starost < drugi.starost:
            return True
        return False

6. podnaloga

Sestavite funkcijo uredi(teze, starosti). Argumenta teze in starosti sta enako dolga seznama števil, kjer $i$-ti element predstavlja težo oz. starost $i$-tega zajca. Funkcija uredi naj ne bo znotraj razreda Zajec, saj ni objektna metoda, ampak je čisto običajna funkcija.

Funkcija naj ustvari seznam ustreznih primerkov razreda Zajec, ga uredi po velikosti glede na zgoraj opisano relacijo in ta seznam vrne kot rezultat.

>>> l = uredi([5, 4, 4], [3, 2, 3])
>>> for z in l:
...     print(z)
...
Zajec težak 4 kg, star 2 let.
Zajec težak 4 kg, star 3 let.
Zajec težak 5 kg, star 3 let.

Uradna rešitev

def uredi(teze, starosti):
    zajci = [Zajec(teze[i], starosti[i]) for i in range(len(teze))]
    zajci.sort()
    return zajci

Geometrija

Pri tej vaji bomo implementirali razrede Vektor, Tocka in Premica, ki predstavljajo vektor, točko in premico v evklidski ravnini. Nekaj metod je že implementiranih, nekaj pa jih boste implementirali sami.

Najprej si oglejte naslednjo kodo. To potem tudi skopirajte na začetek prve naloge!

import math
EPS = 1e-12

def eq(a, b, eps=EPS): # Pomožna funkcija
    ''' se a in b razlikujeta za manj kot EPS'''
    return abs(a - b) < eps

class Vektor:
     """
     Vektor v ravnini.
     """

     def __init__(self, x, y):
         self.x = x
         self.y = y

     def __eq__(self, other):
         """
         Vrni True, če sta vektorja enaka.
         """
         return eq(self.x, other.x) and eq(self.y, other.y)

     def __add__(self, other):
         """
         Vrni vsoto vektorjev self in other.
         """
         return Vektor(self.x + other.x, self.y + other.y)

     def skalarni_produkt(self, other):
         """
         Vrni standardni skalarni produkt vektorjev self in other.
         """
         return self.x * other.x + self.y * other.y

     def __mul__(self, other):
         """
         Vrni skalarni produkt vektorjev self in other, če je other tudi vektor.
         Sicer vrni produkt vektorja self s skalarjem other (če je other skalar).
         """
         if type(other) == Vektor:
             return self.skalarni_produkt(other)
         else:
             return Vektor(self.x * other, self.y * other)

     def normiran(self):
         """
         Vrni normiran vektor.
         """
         return self / abs(self)


 class Tocka:
     """
     Točka v ravnini.
     """

     def __init__(self, x, y):
         self.x = x
         self.y = y

     def __repr__(self):
         return 'Tocka({0}, {1})'.format(self.x, self.y)

     def __str__(self):
         return '({0}, {1})'.format(self.x, self.y)

     def __eq__(self, other):
         """
         Vrni True, če sta vektorja enaka.
         """
         return eq(self.x, other.x) and eq(self.y, other.y)

     def __sub__(self, other):
         """
         Vrni vektor od točke other do točke self.
         """
         return Vektor(self.x - other.x, self.y - other.y)

     def translacija(self, v):
         """
         Vrni translacijo točke self za vektor v.
         """
         return Tocka(self.x + v.x, self.y + v.y)

     def __add__(self, v):
         """
         Vrni translacijo točke self za vektor v.
         """
         return self.translacija(v)

     def razdalja_do(self, other):
         """
         Vrni razdaljo točke self do objekta other. Objekt other je lahko Tocka ali Premica.
         """
         if type(other) == Tocka:
             return abs(self - other)
         elif type(other) == Premica:
             return abs(other.predznacena_razdalja(self))
         else:
             raise TypeError("nepodprta operacija za tipa: '{0}' in '{1}'".format(type(self), type(other)))

 class Premica:
     """
     Premica v ravnini.

     Premico predstavimo s točko in normalo.
     """

     def __init__(self, tocka, normala):
         self.tocka = tocka
         self.normala = normala

     def __repr__(self):
         return 'Premica({0}, {1})'.format(self.tocka, self.normala)

     def enacba(self):
         """
         Vrne koeficiente a, b in c za enačbo premice :math:`a x + b y = c`.
         """
         a, b = self.normala.x, self.normala.y
         c = self.normala.skalarni_produkt(self.tocka)
         return a, b, c

     def __str__(self):
         a, b, c = self.enacba()
         return '{0} x + {1} y = {2}'.format(a, b, c)

     def smernik(self):
         """
         Vrni vektor, ki leži na premici self.
         """
         a, b = self.normala.x, self.normala.y
         return Vektor(-b, a)

     def pravokotnica(self, tocka=None):
         tocka = tocka or self.tocka
         # Prejšnja vrstica je ekvivalentna tejle: if tocka is None: tocka = self.tocka
         return Premica(tocka, self.smernik())

     def predznacena_razdalja(self, tocka):
         """
         Vrni predznačeno razdaljo od točke tocka do premice self.
         """
         return self.normala.normiran() * (tocka - self.tocka)

1. podnaloga

Razredu Vektor dodajte metodo __repr__(self). Zgled:

>>> v = Vektor(3, 2)
>>> v
Vektor(3, 2)

Opomba: Če v interaktivni konzoli pokličemo nek objekt, se izpiše niz, ki ga vrne klic metode __repr__ na tem objektu. Priporočilo je, da je niz, ki ga vrne metoda __repr__, veljavna programska koda v Pythonu, ki ustvari identično kopijo objekta.

Uradna rešitev

import math

# Konstante
EPS = 1e-12


# Pomožne funkcije
def eq(a, b, eps=EPS):
    return abs(a - b) < eps


class Vektor:
    """
    Vektor v ravnini.
    """

    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __eq__(self, other):
        """
        Vrni True, če sta vektorja enaka.
        """
        return eq(self.x, other.x) and eq(self.y, other.y)

    def __add__(self, other):
        """
        Vrni vsoto vektorjev self in other.
        """
        return Vektor(self.x + other.x, self.y + other.y)

    def skalarni_produkt(self, other):
        """
        Vrni standardni skalarni produkt vektorjev self in other.
        """
        return self.x * other.x + self.y * other.y

    def __mul__(self, other):
        """
        Vrni skalarni produkt vektorjev self in other, če je other tudi vektor.
        Sicer vrni produkt vektorja self s skalarjem other (če je other skalar).
        """
        if type(other) == Vektor:
            return self.skalarni_produkt(other)
        else:
            return Vektor(self.x * other, self.y * other)

    def normiran(self):
        """
        Vrni normiran vektor.
        """
        return self / abs(self)


class Tocka:
    """
    Točka v ravnini.
    """

    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __repr__(self):
        return 'Tocka({0}, {1})'.format(self.x, self.y)

    def __str__(self):
        return '({0}, {1})'.format(self.x, self.y)

    def __eq__(self, other):
        """
        Vrni True, če sta vektorja enaka.
        """
        return eq(self.x, other.x) and eq(self.y, other.y)

    def __sub__(self, other):
        """
        Vrni vektor od točke other do točke self.
        """
        return Vektor(self.x - other.x, self.y - other.y)

    def translacija(self, v):
        """
        Vrni translacijo točke self za vektor v.
        """
        return Tocka(self.x + v.x, self.y + v.y)

    def __add__(self, v):
        """
        Vrni translacijo točke self za vektor v.
        """
        return self.translacija(v)

    def razdalja_do(self, other):
        """
        Vrni razdaljo točke self do objekta other. Objekt other je lahko Tocka ali Premica.
        """
        if type(other) == Tocka:
            return abs(self - other)
        elif type(other) == Premica:
            return abs(other.predznacena_razdalja(self))
        else:
            raise TypeError("nepodprta operacija za tipa: '{0}' in '{1}'".format(type(self), type(other)))


class Premica:
    """
    Premica v ravnini.

    Premico predstavimo s točko in normalo.
    """

    def __init__(self, tocka, normala):
        self.tocka = tocka
        self.normala = normala

    def __repr__(self):
        return 'Premica({0}, {1})'.format(self.tocka, self.normala)

    def enacba(self):
        """
        Vrne koeficiente a, b in c za enačbo premice :math:`a x + b y = c`.
        """
        a, b = self.normala.x, self.normala.y
        c = self.normala.skalarni_produkt(self.tocka)
        return a, b, c

    def __str__(self):
        a, b, c = self.enacba()
        return '{0} x + {1} y = {2}'.format(a, b, c)

    def smernik(self):
        """
        Vrni vektor, ki leži na premici self.
        """
        a, b = self.normala.x, self.normala.y
        return Vektor(-b, a)

    def pravokotnica(self, tocka=None):
        tocka = tocka or self.tocka
        # Prejšnja vrstica je ekvivalentna tejle: if tocka is None: tocka = self.tocka
        return Premica(tocka, self.smernik())

    def predznacena_razdalja(self, tocka):
        """
        Vrni predznačeno razdaljo od točke tocka do premice self.
        """
        return self.normala.normiran() * (tocka - self.tocka)

class Vektor(Vektor):
    def __repr__(self):
        return 'Vektor({0}, {1})'.format(self.x, self.y)

2. podnaloga

Razredu Vektor dodajte metodo __str__(self). Zgled:

>>> v = Vektor(3, 2)
>>> print(v)
(3, 2)

Opomba: Funkcija print na svojem argumentu pokliče metodo __str__ in izpiše niz, ki ga ta metoda vrne. Metoda __str__ običajno vrne razumljiv opis objekta, ki naj bi ga razumeli tudi ne-programerji.

Uradna rešitev

class Vektor(Vektor):
    def __str__(self):
        return '({0}, {1})'.format(self.x, self.y)

3. podnaloga

Razredu Vektor dodajte metodo __abs__(self), ki naj vrne dolžino (normo) vektorja. Zgled:

>>> v = Vektor(1, 3)
>>> abs(v)
3.1622776601683795

Uradna rešitev

class Vektor(Vektor):
    def __abs__(self):
        """
        Vrni dolžino vektorja self.
        """
        return (self.x ** 2 + self.y ** 2) ** 0.5

4. podnaloga

Razredu Vektor dodajte metodo __sub__(self, other), ki vrne razliko vektorjev. Zgled:

>>> v = Vektor(-1, 3)
>>> u = Vektor(2, 1)
>>> u - v
Vektor(-3, 2)

Uradna rešitev

class Vektor(Vektor):
    def __sub__(self, other):
        """
        Vrni razliko vektorjev self in other.
        """
        return Vektor(self.x - other.x, self.y - other.y)

5. podnaloga

V razredu Vektor sestavite metodo __truediv__(self, skalar), ki vrne produkt vektorja self s skalarjem 1 / skalar. Zgled:

>>> Vektor(-1, 3) / 2
Vektor(-0.5, 1.5)

Uradna rešitev

class Vektor(Vektor):
    def __truediv__(self, skalar):
        """
        Vrni produkt vektorja self s skalarjem 1/skalar.
        """
        return self * (1 / skalar)

6. podnaloga

V razredu Vektor sestavite metodo sta_pravokotna(self, other), ki vrne True, če sta vektorja self in other pravokotna, in False sicer. Zgled:

>>> v = Vektor(-1, 3)
>>> u = Vektor(2, 1)
>>> v.sta_pravokotna(u)
False

Uradna rešitev

class Vektor(Vektor):
    def sta_pravokotna(self, other):
        """
        Vrni True, če sta vektorja self in other pravokotna, in False sicer.
        """
        return eq(self.skalarni_produkt(other), 0)

7. podnaloga

V razredu Vektor sestavite metodo rotacija(self, alpha), ki vrne rotacijo vektorja self za kot alpha (v radianih). Zgled:

>>> Vektor(1, 0).rotacija(math.pi/4)
Vektor(0.7071067811865476, 0.7071067811865475)

Uradna rešitev

class Vektor(Vektor):
    def rotacija(self, alpha):
        """
        Vrni rotacijo vektorja self za kot alpha (podan v radianih).
        """
        return Vektor(self.x * math.cos(alpha) - self.y * math.sin(alpha),
                      self.x * math.sin(alpha) + self.y * math.cos(alpha))

8. podnaloga

Razredu Premica dodajte metodo projekcija(self, tocka), ki vrne pravokotno projekcijo točke tocka na premico self. Zgled:

>>> p = Premica(Tocka(1, 1), Vektor(0, 1))
>>> p.projekcija(Tocka(3, 0))
Tocka(3, 1)

Uradna rešitev

class Premica(Premica):
    def projekcija(self, tocka):
        """
        Vrni pravokotno projekcijo točke tocka na premico self.
        """
        s = self.smernik()
        v = tocka - self.tocka
        w = s * (s * v) / (s * s)
        return self.tocka.translacija(w)

9. podnaloga

Razredu Premica dodajte metodo presek(self, other), ki vrne točko, ki je presek dveh premic. Zgled:

>>> p = Premica(Tocka(3, 4), Vektor(2, -1))
>>> q = Premica(Tocka(0, 1), Vektor(1, 2))
>>> p.presek(q)
Tocka(1.2, 0.4)

Uradna rešitev

class Premica(Premica):
    def presek(self, other):
        """
        Vrni presek premic self in other.
        """
        a1, b1 = self.normala.x, self.normala.y
        a2, b2 = other.normala.x, other.normala.y
        x1, y1 = self.tocka.x, self.tocka.y
        x2, y2 = other.tocka.x, other.tocka.y
        c = a1 * b2 - a2 * b1
        x = (b1 * b2 * (y1 - y2) + a1 * b2 * x1 - a2 * b1 * x2) / c
        y = (a1 * a2 * (x2 - x1) - a2 * b1 * y1 + a1 * b2 * y2) / c
        return Tocka(x, y)

10. podnaloga

Razredu Tocka dodajte metodo zrcali_cez_premico(self, premica), ki vrne zrcalno sliko točke self čez premico premica. Zgled:

>>> p = Premica(Tocka(1, 1), Vektor(0, 1))
>>> Tocka(3, 4).zrcali_cez_premico(p)
Tocka(3, -2)

Uradna rešitev

class Tocka(Tocka):
    def zrcali_cez_premico(self, premica):
        """
        Vrni točko, ki jo dobimo z zrcaljenjem točke self čez premico premica.
        """
        n = premica.normala
        v = self - premica.tocka
        w = n * (n * v) / (n * n)
        return self.translacija(w * -2)

Permutacija

Najbolj popularnih zapisov permutacije je po mnenju mnogih ciklični zapis (tj. permutacijo zapišemo kot produkt disjunktnih ciklov). Primer:

(1 5 2) (3 6) (4)

Ciklov dolžine 1 (fiksnih točk) po navadi ne pišemo, torej zgornjo permutacijo običajno pišemo kar takole:

(1 5 2) (3 6)

1. podnaloga

Sestavite razred Permutacija, s katerim predstavimo permutacijo. Najprej sestavite konstruktor __init__(self, cikli, stopnja=None), kjer je cikli seznam seznamov, ki predstavljajo cikle permutacije. Argument stopnja ima privzeto vrednost None – v tem primeru naj bo stopnja največje število, ki nastopa v katerem od ciklov. Razred naj ima atributa stopnja in cikli. Na prvem mestu v vsakem ciklu naj bo najmanjši element tega cikla (s cikličnim pomikom dobimo isti cikel). Tudi cikli naj bodo urejeni. Morebitne cikle dožine 1 naj konstruktor odstrani.

>>> p = Permutacija([[7, 3], [4], [5, 2, 1]], stopnja=7)
>>> p.stopnja
7
>>> p.cikli
[[1, 5, 2], [3, 7]]

Uradna rešitev

class Permutacija:
    def __init__(self, cikli, stopnja=None):
        if stopnja is None:
            self.stopnja = max(map(max, cikli))
        else:
            self.stopnja = stopnja
        self.cikli = [list(cikel) for cikel in cikli if len(cikel) > 1]
        for k in range(len(self.cikli)):
            cikel = self.cikli[k]
            # najdi najmanjsi element
            m = 0
            for i in range(len(cikel)):
                if cikel[i] < cikel[m]:
                    m = i
            self.cikli[k] = cikel[m:] + cikel[:m]
        self.cikli.sort()

2. podnaloga

V razredu Permutacija definirajte metodo inverz(self), ki sestavi in vrne inverz dane permutacije. Inverz permutacije dobimo tako, da v cikličnem zapisu obrnemo vse cikle (vsakega posebej).

>>> p = Permutacija([[7, 3], [4], [5, 2, 1]], stopnja=7)
>>> q = p.inverz()
>>> q.stopnja
7
>>> q.cikli
[[1, 2, 5], [3, 7]]

Uradna rešitev

class Permutacija(Permutacija):
    def inverz(self):
        return Permutacija([c[::-1] for c in self.cikli], stopnja=self.stopnja)

3. podnaloga

V razredu Permutacija sestavite metodo ciklicni_tip(self), ki vrne ciklični tip permutacije. To je nabor, ki ima toliko elementov, kot je dolžina najdaljšega cikla. Prvi element v tem naboru je število ciklov dolžine 1, drugi element je število ciklov dolžine 2, itn.

>>> p = Permutacija([[7, 3], [4], [5, 2, 1]], stopnja=7)
>>> p.ciklicni_tip()
(2, 1, 1)

Uradna rešitev

class Permutacija(Permutacija):
    def ciklicni_tip(self):
        dolzine = [len(c) for c in self.cikli]
        najdaljsi_cikel = max(dolzine)
        ctip = [dolzine.count(i) for i in range(1, najdaljsi_cikel + 1)]
        ctip[0] = self.stopnja - sum(dolzine)
        return tuple(ctip)

4. podnaloga

V razredu Permutacija sestavite metodo red(self), ki izračuna in vrne red permutacije. Naj bo $\pi$ permutacija. Red permutacije $\pi$ je najmanjše pozitivno število $k$, pri katerem je $\pi^k$ identiteta.

Namig 1: Red permutacije je najmanjši skupni večkratnik dolžin vseh ciklov.

Namig 2: Za poljubni dve naravni števili a in b velja, da je gcd(a, b) * lcm(a, b) == a * b. (Funkcija gcd računa največji skupni delitelj, funkcija lcm pa najmanjši skupni večkratnik.)

>>> p = Permutacija([[7, 3], [4], [5, 2, 1]], stopnja=7)
>>> p.red()
6

Uradna rešitev

def gcd(a, b):
    while b != 0:
        a, b = b, a % b
    return a

def lcm(a, b):
    return a * b // gcd(a, b)


class Permutacija(Permutacija):
    def red(self):
        r = 1
        for c in self.cikli:
            r = lcm(r, len(c))
        return r

Neusmerjen graf

Naj bo $V$ končna neprazna množica in $E$ družina dvoelementnih podmnožic množice $V$. Paru $G = (V, E)$ pravimo graf na množici vozlišč $V$ in z množico povezav $E$. Pri tej nalogi bomo sestavili razred NeusmerjenGraf za delo z enostavnimi (ni zank in ni vzporednih povezav) neusmerjenimi grafi.

1. podnaloga

Najprej sestavite konstruktor __init__(self, seznam_povezav), ki sprejme seznam parov seznam_povezav (vsak par predstavlja eno povezavo). Konstruktor naj objektu pripne atribut sosedje, ki naj bo slovar, katerega ključi so oznake vozlišč, vrednost ključa pa je množica sosedov te točke. Zgled:

>>> g = NeusmerjenGraf([(1, 2), (2, 3), (3, 1), (1, 4)])
>>> g.sosedje
{1: {2, 3, 4}, 2: {1, 3}, 3: {1, 2}, 4: {1}}

Uradna rešitev

class NeusmerjenGraf:
    def __init__(self, seznam_povezav):
        self.sosedje = dict()
        for u, v in seznam_povezav:
            if u not in self.sosedje:
                self.sosedje[u] = set()
            self.sosedje[u].add(v)
            if v not in self.sosedje:
                self.sosedje[v] = set()
            self.sosedje[v].add(u)

2. podnaloga

Sestavite metodo slovar_stopenj(self), ki vrne slovar, katerega ključi so oznake vozlišč, vrednosti pa so njihove stopnje. Zgled:

>>> g = NeusmerjenGraf([(1, 2), (2, 3), (3, 1), (1, 4)])
>>> g.slovar_stopenj()
{1: 3, 2: 2, 3: 2, 4: 1}

Uradna rešitev

class NeusmerjenGraf(NeusmerjenGraf):
    def slovar_stopenj(self):
        return {k: len(v) for k, v in self.sosedje.items()}

3. podnaloga

Sestavite metodo dodaj_vozlisce(self, u), ki v graf doda vozlišče z oznako u. Če vozlišče s to oznako že obstaja, naj metoda ne naredi ničesar. Zgled:

>>> g = NeusmerjenGraf([(1, 2), (2, 3), (3, 1)])
>>> g.dodaj_vozlisce(4)
>>> g.sosedje
{1: {2, 3}, 2: {1, 3}, 3: {1, 2}, 4: set()}}

Uradna rešitev

class NeusmerjenGraf(NeusmerjenGraf):
    def dodaj_vozlisce(self, u):
        if u not in self.sosedje:
            self.sosedje[u] = set()

4. podnaloga

Sestavite metodo odstrani_vozlisce(self, u), ki iz grafa odstrani vozlišče z oznako u in vse povezave, ki imajo u za krajišče. Ppredpostavi, da je vozlišča u zagotovo v grafu. Zgled:

>>> g = NeusmerjenGraf([(1, 2), (2, 3), (3, 1)])
>>> g.odstrani_vozlisce(3)
>>> g.sosedje
{1: {2}, 2: {1}}

Uradna rešitev

class NeusmerjenGraf(NeusmerjenGraf):
    def odstrani_vozlisce(self, u):
        del self.sosedje[u]
        for mn_sos in self.sosedje.values():
            mn_sos.discard(u)

5. podnaloga

Sestavite metodo dodaj_povezavo(self, e), ki v graf doda povezavo e (par vozlišč). Če ta povezava že obstaja, naj metoda ne naredi ničesar. V primeru, če katero od krajišč povezave manjka, še njega dodajte v graf (s klicem metode dodaj_vozlisce). Zgled:

>>> g = NeusmerjenGraf([(1, 2), (2, 3), (3, 1)])
>>> g.dodaj_povezavo((1, 4))
>>> g.sosedje
{1: {2, 3, 4}, 2: {1, 3}, 3: {1, 2}, 4: {1}}

Uradna rešitev

class NeusmerjenGraf(NeusmerjenGraf):
    def dodaj_povezavo(self, e):
        u, v = e
        self.dodaj_vozlisce(u)
        self.dodaj_vozlisce(v)
        self.sosedje[u].add(v)
        self.sosedje[v].add(u)

6. podnaloga

Sestavite še metodo odstrani_povezavo(self, e), ki iz grafa odstrani povezavo e (par vozlišč). Predpostavi, da je povezava e zagotovo v grafu. Zgled:

>>> g = NeusmerjenGraf([(1, 2), (2, 3), (3, 1)])
>>> g.odstrani_povezavo((1, 2))
>>> g.sosedje
{1: {3}, 2: {3}, 3: {1, 2}}

Uradna rešitev

class NeusmerjenGraf(NeusmerjenGraf):
    def odstrani_povezavo(self, e):
        u, v = e
        self.sosedje[u].remove(v)
        self.sosedje[v].remove(u)