Naloge - algoritmične (težje)

Štetje cifer

Napiši funkcijo count(n), ki sešteje vse cifre v številu n. Postopek ponavljaj dokler ima rezultat več kot eno cifro.

>>> count(12345)  # 1 + 2 + 3 + 4 + 5 = 15, 1 + 5 = 6
6
>>> count(999999999)  # 9 + 9 + 9 + 9 + 9 + 9 + 9 + 9 + 9 = 81, 8 + 1 = 9
9
>>> count(213413512)
4
>>> count(2147483647)
1

Rešitev

Rešitev z while zanko.

def count(n):
    while n > 9:
        sum = 0
        for i in str(n):
            sum += int(i)
        n = sum
    return n

Malo bolj napredna rešitev z while zanko.

def count(n):
    while n > 9:
        n = sum(map(int, str(n)))
    return n

Poštnina

Po pošti bi radi poslal n steklenih kock dimenzij 1 x 1 x 1 tako, da jih zapakiramo v škatlo velikosti a x b x c. Cena poštnine je a + b + c. Ker so kocke steklene jih moramo zapakirati tako, da v škatli ni praznih prostorov, saj bi se sicer kocke lahko premikale in razbile. Napiši funkcijo postnina(n), ki vrne ceno najcenejše poštnine.

>>> postnina(1)  # 1 x 1 x 1
3
>>> postnina(6)  # 2 x 3 x 1
6
>>> postnina(7)  # 7 x 1 x 1
9
>>> postnina(200)  # 5 x 5 x 8
18

Rešitev

Najbolj preprosta rešitev pregleda vse možne a x b x c (1 <= a, b, c <= n).

def postnina(n):
    cene = []
    for a in range(1, n + 1):
        for b in range(1, n + 1):
            for c in range(1, n + 1):
                if a * b * c == n:
                    cene.append(a + b + c)
    return min(cene)

V resnici je dovolj, če napišemo zanki le za a in b. Izraz x // y vrne celi del pri deljenju x z y.

def postnina(n):
    cene = []
    for a in range(1, n + 1):
        for b in range(1, n + 1):
            c = n // (a * b)
            if a * b * c == n:
                cene.append(a + b + c)
    return min(cene)

Program lahko pohitrimo tako da upoštevamo, da so a, b in c delitelji števila n.

def postnina(n):
    delitelji = []
    for i in range(1, n + 1):
        if n % i == 0:
            delitelji.append(i)
    cene = []
    for a in delitelji:
        for b in delitelji:
            c = n // (a * b)
            if a * b * c == n:
                cene.append(a + b + c)
    return min(cene)

01

Seznam xs lahko vsebuje le cifri 0 in 1. Spremenimo ga lahko tako, da katerikoli element 0 zamenjamo z 1 in obratno. Na koncu želimo, da so vsi elementi 0 na levi strani seznama, vsi elementi 1 pa na desni. Seznam [0, 0, 1, 0, 1] lahko z eno spremembo pretvorimo v seznam [0, 0, 1, 1, 1], seznam [0, 1, 0, 1, 0] pa z dvema spremembama postane [0, 1, 1, 1, 1] ali [0, 0, 0, 0, 0]. Napiši funkcijo nic_ena(xs), ki vrne najmanjše število potrebnih sprememb.

>>> nic_ena([0, 0, 1, 0, 1])
1
>>> nic_ena([0, 0, 1, 1, 1])
0
>>> nic_ena([0, 1, 0, 1, 0])
2
>>> nic_ena([1, 1, 1, 1, 0, 0, 0])
3
>>> nic_ena([1, 0, 0, 0, 1, 1, 1, 0])
2
>>> nic_ena([0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0])
9

Rešitev

V končnem seznamu bo na nekem mestu meja med ničlami in enicami. Število potrebnih sprememb lahko za določeno mejo hitro izračunamo tako, da seštejemo število enic levo in število ničel desno od meje. Postopek ponovimo za vsako mejo.

def nic_ena(xs):
    ones_left = 0
    zeros_right = xs.count(0)
    count = [zeros_right]   
    for x in xs:
        if x == 0:
            zeros_right -= 1
        else:
            ones_left += 1
        count.append(ones_left + zeros_right)
    return min(count)

Mask

Napiši funkcijo mask(n), ki vrne vseh $2^n$ različnih seznamov ničel in enic dolžine n.

>>> mask(0)
[[]]
>>> mask(1)
[[0], [1]]
>>> mask(2)
[[0, 0], [0, 1], [1, 0], [1, 1]]
>>> mask(3)
[[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 1, 1], [1, 0, 0], [1, 0, 1], [1, 1, 0], [1, 1, 1]]
>>> len(mask(10))
1024

Rešitev

Iterativna rešitev.

def mask(n):
    s = [[]]
    for i in range(n):
        t = []
        for x in s:
            t.append(x + [0])
            t.append(x + [1])
        s = t
    return s

Rekurzivna rešitev.

def mask(n):
    if n == 0:
        return [[]]
    xss = []
    for xs in mask(n - 1):
        xss.append(xs + [0])
        xss.append(xs + [1])
    return xss

Potenčna množica

Napiši funkcijo potencna(xs), ki vrne seznam vseh podseznamov seznama xs. Uporabi funkcijo mask iz prejšnje naloge.

>>> potencna([])
[[]]
>>> potencna([1, 5])
[[], [1], [5], [1, 5]]
>>> potencna([1, 5, 7])
[[], [1], [5], [1, 5], [7], [1, 7], [5, 7], [1, 5, 7]]
>>> len(potencna([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))
1024

Rešitev

Uporabimo funkcijo mask, ki nam pove, kateri element vključimo in katerega ne.

def potencna(xs):
    rezultat = []
    for mask_bits in mask(len(xs)):
        xs_masked = []
        for mask_bit, x in zip(mask_bits, xs):
            if mask_bit:
                xs_masked.append(x)
        rezultat.append(xs_masked)
    return rezultat

Lahko pa dopolnimo rešitev iz prejšnje naloge.

def potencna(xs):
    if not xs:
        return [[]]
    yss = []
    for ys in potencna(xs[1:]):
        yss.append(ys)
        yss.append(xs[:1] + ys)
    return yss

Knjige

Franc in Anton izdelujeta seminarsko nalogo, za katero je treba prebrati veliko knjig. Odločita se, da si bosta knjige razdelila pošteno. Napiši funkcijo knjige(xs), ki sprejme seznam knjig in vrne True, če si Franc in Anton knjige lahko razdelita tako, da bosta oba prebrala enako število strani. Seznam xs vsebuje število strani posamezne knjige.

>>> knjige([10, 20, 30])  # Anton dobi prvo in drugo knjigo, Franc pa tretjo.
True
>>> knjige([10, 20, 30, 40])  # Anton dobi drugo in tretjo knjigo, Franc pa prvo in zadnjo.
True
>>> knjige([10, 20, 30, 40, 50])
False
>>> knjige([23, 51, 51, 153, 25, 51, 59, 39, 35, 91])
False
>>> knjige([23, 51, 51, 153, 20, 25, 51, 59, 39, 35, 91])
True

Rešitev

def knjige(xs):
    cilj = sum(xs) / 2
    for ys in potencna(xs):
        if sum(ys) == cilj:
            return True
    return False

Zadnja sprememba: petek, 9. oktober 2020, 20.59