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:

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 = maxtevciImena.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 = maxtevciImena.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:

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