Orbite
Tudi tokratno nalogo je navdihnil Advent of Code, konkretno naloga Universal Orbit Map. Dve izmed (lažjih) funkcij sta enaki tistim, ki jih je bilo potrebno sprogramirati tam.
Testi
Testi: testi-orbite.zip
Naloga
Planet COM ima samo eno luno; recimo ji B. Luna B pa ima, zanimivo, dve svoji luni; recimo jima G in C ter se ne ukvarjajmo s tem, ali imajo lune lahko svoje lune in kaj imajo o tem povedati astronomi. Okrog lune G kroži luna H, okrog lune B pa luna C. Ker to postaja zapleteno, raje pokažimo s sliko:
YOU
/
G - H J - K - L
/ /
COM - B - C - D - E - F
\
I - SAN
Ocena 6
Napiši funkcijo
preberi_orbite(ime_datoteke)
, ki kot argument prejme datoteko z lunami v takšni obliki:COM)B B)C C)D D)E E)F B)G G)H D)I E)J J)K K)L K)YOU I)SAN
vrniti pa mora slovar s temi podatki. Ključ slovarja je posamična luna, pripadajoča vrednost pa je luna (oz. planet), okrog katere ta luna kroži.
>>> preberi_orbite("example.txt") {'B': 'COM', 'C': 'B', 'D': 'C', 'E': 'D', 'F': 'E', 'G': 'B', 'H': 'G', 'I': 'D', 'J': 'E', 'K': 'J', 'L': 'K', 'SAN': 'I', 'YOU': 'K'}
Rešitev
Naloga zahteva, da znamo prebrati datoteko in da vemo za metodo
split
, ter to, kar vrne, pametno zložiti v slovar.def preberi_orbite(ime_datoteke): orbite = {} for vrstica in open(ime_datoteke): center, satelit = vrstica.strip().split(")") orbite[satelit] = center return orbite
Napiši funkcijo
lune(orbite)
, ki prejme slovar, kakršen je gornji in vrne nov slovar: njegovi ključi so lune oz. planet, vrednost pa množica lun, ki krožijo okrog nje oz. njega.>>> lune(orbite) {'B': {'C', 'G'}, 'C': {'D'}, 'COM': {'B'}, 'D': {'E', 'I'}, 'E': {'F', 'J'}, 'G': {'H'}, 'I': {'SAN'}, 'J': {'K'}, 'K': {'L', 'YOU'}}
Rešitev
Gremo čez par reči v slovarju (
orbite.items()
). Tisto, kar je bila prej vrednost, bo zdaj ključ. Tisto, kar so bili prej ključi,zlagamo v množico. Če uporabimodefaultdict
, je funkcija kar kratka.from collections import dict def lune(orbite): krozilci = defaultdict(set) for satelit, center in orbite.items(): krozilci[center].add(satelit) return krozilci
Tule je precej študentov delalo nekakšne dvojne zanke prek
orbite.items()
, zaradi česar je bila funkcija zelo počasna. Dokler testiramo zgolj to funkcijo, se ta počasnost še ni poznala. Ker kasnejše funkcije zelo velikokrat pokličejo to funkcijo, pa so nekatere (predvsem funkcijan_odvisnikov
) v tem primeru postale zelo počasne in se izvajala več minut namesto, da bi se končalo v trenutku.Napiši funkcijo
prestej_korake(odkod, kam, orbite)
, ki dobi začetno in končno luno ter slovar, kakršnega vrača funkcijapreberi_orbite
. Vrniti mora število korakov ododkod
dokam
, pri čemer je en korak skok z lune "dol" na luno oz. planet, okrog katere ta luna kroži (torej vedno samo z desne strani proti levi strani slike).Od
"J"
do"C"
trije koraki (J - E - D - C), zato>>> prestej_korake("J", "C", orbite) 3
Predpostaviti smeš, da funkcije nikoli ne bomo klicali z argumenti, pri katerih je pot po teh pravili nemogoča -- na primer od J do I ali od C do J.
Rešitev
Tudi to je le naloga iz uporabe slovarjev. Slovar
orbite
nam pove, okoli katere lune kroži posamezna luna. Vzamemo to luno. In potem luno, okoli kroži ta. In luno okoli katere kroži ta. To ponavljamo, dokler ne pridemo do cilja, vmes pa štejemo korake.def prestej_korake(odkod, kam, orbite): korakov = 0 while odkod != kam: odkod = orbite[odkod] korakov += 1 return korakov
Žalostno veliko študentov namesto
odkod = orbite[odkod]
še vedno pišefor kljuc, vrednost in orbite.items(): if kljuc == odkod: naslednja = vrednost odkod = naslednja
To je počasno. In grdo. In nevarno, ker se lahko pri tem zmotimo. In, predvsem, kaže, da avtor takšne rešitve ne razume točno, čemu služijo slovarji. :(
Napiši funkcijo
prestej_korake_r(odkod, kam, orbite)
, ki počne natančno isto, vendar je narejena rekurzivno.Rešitev
Če bi radi izračunali, koliko je od te lune do cilja (ki je nekje na levi), moramo le preveriti, koliko je od leve lune do cilja. Od te je pač en korak več.
Če smo že na cilju, pa je koravkov 0.
def prestej_korake_r(odkod, kam, orbite): if odkod == kam: return 0 return 1 + prestej_korake_r(orbite[odkod], kam, orbite)
Te naloge se ne da reševati takole:
def prestej_korake_r(odkod, kam, orbite): if odkod == kam: return 0 korakov = 0 prestej_korake_r(orbite[odkod], kam, orbite) korakov += 1 return korakov
Daljša razlaga podobno napačnega razmišljanja lahko preberete ob rešitvi naslednje funkcije.
Napiši funkcijo
n_odvisnikov(luna, orbite)
, ki prešteje, koliko lun kroži okrog luneluna
in okrog teh lun, in okrog lun, ki krožijo okrog teh lun in tako do konca. Z drugimi besedami, za podano luno vrne število vseh lun, ki so povezane z njo s povezavami proti desni.Okrog lune E in lun, ki krožijo okrog nje (in tako naprej) krožijo F, J, K, L in YOU, zato
>>> n_odvisnikov("E", orbite) 5
Okrog COM pa posredno ali neposredno kroži vseh 13 lun, zato
>>> n_odvisnikov("COM", orbite) 13
Rešitev
Odvisnikov je toliko, kolikor lun kroži okrog te lune, k temu pa moramo prešteti še odvisnike vseh sinov.
def n_odvisnikov(luna, orbite): odvisniki = lune(orbite)[luna] return len(odvisniki) + sum( n_odvisnikov(luna, orbite) for luna in odvisniki)
Ali, enako le malo dajše, tako:
def n_odvisnikov(luna, orbite): odvisniki = lune(orbite)[luna] odvisnikov = len(odvisniki) for odvisnik in odvisniki: odvisnikov += n_odvisnikov(odvisnik, orbite) return odvisnikov
Tule je prav, da opazimo, da funkcija kliče
lune(orbite)
in da se ta klic dogaja vedno ponovno in ponovno in ponovno, ob vsakem rekurzivnem klicu. Slabo napisana funkcijalune
za 1000 lun ne bo porabila 1000 korakov, temveč 1000 * 1000. Ker funkcijan_odvisnikov
za 1000 lun 1000-krat pokličelune
, bo vse skupaj zahtevalo 1000 * 1000 * 1000 = 1 milijardo korakov.Če je funkcija
lune
napisana solidno, to še ne bo problem. Če nas to skrbi, pa se rešimo tako:def n_odvisnikov(luna, orbite): obrnjene = lune(orbite) return n_odvisnikov0(luna, obrnjene) def n_odvisnikov0(luna, obrnjene): odvisniki = obrnjene[luna] return len(odvisniki) + sum( n_odvisnikov0(luna, obrnjene) for luna in odvisniki)
Zdaj funkcija
n_odvisnikov
obrnjene slovar lun v pravo obliko in pokliče rekurzivno funkcijon_odvisnikov0
, ki prejme seznam lun v takšni obliki, tako da ga ni potrebno pretvarjati ob vsakem rekurzivnem klicu znova.Tudi veliko naslednjih funkcij bi potrebovalo seznam v takšni obliki in so zaradi pretvarjanja nekoliko počasnejše. A še počasnejše bi bile, če pretvarjanja ne bi bilo. Naloge so bile podane v tej obliki prav zato, da bi bili študenti pri vsaki prisiljeni sami presoditi, kakšen slovar jim bo prišel najbolj prav.
S to nalogo so imeli študenti žalostno hude probleme. To pomeni, da ne berejo zapiskov predavanj. :( Naloga je natančno enaka nalogi, v kateri smo šteli potomce v rodbini Novakovih.
Tule je tipičen, a poučen primer napačnega razmišljanja.
def n_odvisnikov(luna, orbite): odvisnikov = 0 odvisniki = lune(orbite)[luna] for x in odvisniki: n_odvisnikov(x, orbite) odvisnikov += len(odvisniki) return odvisnikov
Avtorji takšne rešitve (precej sem jih videl v tem tednu) najbrž razmišljajo takole. Pripravim si spremenljivko
n_odvisnikov
, v katero bom sešteval odvisnike. Torejodvisnikov = 0
. K temu potem prištejem število odvisnikov te lune. To potem ponovim za vsako luno, ki je pripeta na to luno (for x in podlune: n_odvisnikov(x, orbite):
).Napaka v razmišljanju je v tem, da si predstavljate, da bo obstajala samo ena spremenljivka
odvisnikov
in vse funkcije bodo prištevale vanjo (odvisnikov += len(podlune)
). Vendar si istočasno predstavljate, da se bo vrsticaodvisnikov = 0
zgodila le enkrat, v prvem klicu funkcije, v nadaljnjih klicih pa se bo skrivnostno preskočila.A oboje očitno ne more biti res. Ali je spremenljivka
odvisnikov
le ena -- vendar se potem vedno znova postavlja na 0, ali pa imamo ob vsakem klicu funkcije novo spremenljivko. Res je seveda drugo. Ko funkcija pokliče samo sebe, ima ta, nova "inkarnacija" funkcije svojo spremenljivkoodvisnikov
, ki z ono, prvo, nima zveze.Funkcijo lahko malo preobrnemo -- premešamo vrstice, katerih vrstni red ni pomemben.
def n_odvisnikov(luna, orbite): odvisniki = lune(orbite)[luna] for x in odvisniki: n_odvisnikov(x, orbite) odvisnikov = 0 odvisnikov += len(odvisniki) return odvisnikov
Zdaj pa pogledamo zadnje tri vrstice in uvidimo, da funkcija pravzaprav počne to:
def n_odvisnikov(luna, orbite): odvisniki = lune(orbite)[luna] for x in odvisniki: n_odvisnikov(x, orbite) return len(odvisniki)
Funkcija
n_odvisnikov
ne spreminja "sveta" okrog sebe. Klic te funkcija nima "stranskih učinkov". Ko pokličemon_odvisnikov(x, orbite)
in zavržemo rezultat tega klica, je to isto, kot če bi funkcije sploh ne klicali. Gornja funkcija je torej enaka kotdef n_odvisnikov(luna, orbite): odvisniki = lune(orbite)[luna] return len(odvisniki)
Kako to popraviti, sem nakazal zgoraj. Funkcijo
n_odvisnikov(x, orbite)
kličemo zato, da bi izračunala število odvisnikov lunex
. Ta rezultat moramo uporabiti, ne zavreči.Prva rekurzivna funkcija, ki smo jo programirali, je bila
vsota
. Smo jo morda sprogramirali tako?def vsota(s): v = 0 vsota(s[1:]) v += s[0] return v
Ocena 7
Napiši funkcijo
pot_do(odkod, kam, orbite)
, ki je podobna funkcijiprestej_korake
, vendar ne vrne števila korakov temveč seznam lun na poti, vključno zodkod
inkam
. Funkcija naj ne bo rekurzivna. Rekurzivno različico boš napisal za oceno 8.>>> pot("J", "C", orbite) ["J", "E", "D", "C"]
Spet smeš predpostaviti, da pot, po kateri sprašuje funkcija, obstaja.
Rešitev
Tole je čisto isto kot štetje korakov, le da zlagamo lune v seznam, ki ga na koncu vrnemo.
def pot_do(odkod, kam, orbite): pot = [odkod] while odkod != kam: odkod = orbite[odkod] pot.append(odkod) return pot
Napiši funkcijo
pot_v_niz(pot)
, ki prejme seznam, kakršnega vrača prejšnja funkcija in vrne niz, v katerem so lune ločene z znakom->
.>>> pot_v_niz(["J", "E", "D", "C"]) "J -> E -> D -> C"
Rešitev
Iz te naloge lahko doktoriramo, lahko pa vemo, da imajo nizi metodo
join
.def pot_v_niz(pot): return " -> ".join(pot)
Če vemo o Pythonu kaj več, pa funkcijo kar priredimo. Celotna rešitev naloge je lahko kar.
pot_v_niz = " -> ".join
Napiši funkcijo
navodila(pot, ime_datoteke)
, ki prejme pot, kakršno vrne prejšnja funkcija in ime datoteke. V datoteko shrani zapis navodil v obliki, ki jo kaže spodnji primer.Klic
navodila("F -> E -> D -> C -> B", "pot.txt")
v datoteko pot.txt zapiše naslednje besediloIz F pojdite na E. Potem zavijte na D. Potem zavijte na C. Potem zavijte na B. Vaš cilj, B, bo pod vami.
V prvi vrstici torej vedno piše "Iz ... pojdite na ...", v naslednji(h) pa "Potem zavijte na ...". Na koncu naj napiše, da bo cilj pod vami.
Rešitev
To nalogo je bilo potrebno dodati za preprosto vajo iz oblikovanja nizov in pisanja datotek.
def navodila(pot, ime_datoteke): dat = open(ime_datoteke, "wt") lune = pot.split(" -> ") if len(lune) >= 2: dat.write(f"Iz {lune[0]} pojdite na {lune[1]}.\n") for luna in lune[2:]: dat.write(f"Potem zavijte na {luna}.\n") dat.write(f"Vaš cilj, {lune[-1]}, bo pod vami.")
Ocena 8
Napiši funkcijo
pot_do_r(odkod, kam, orbite)
, ki počne isto kot funkcijapot_do
, le da je napisana rekurzivno.Rešitev
Pot od te lune do cilja je enaka poti od naslednje lune do cilja, le še korak do naslednje lune moramo dodati.
Razmišljanje je torej čisto enako kot pri rekurzivnem štetju korakov, le da rezultat ni število temveč seznam.
def pot_do_r(odkod, kam, orbite): if odkod == kam: return [odkod] return [odkod] + pot_do_r(orbite[odkod], kam, orbite)
Napiši funkcijo
odvisniki(luna, orbite)
, ki je podobna funkcijin_odvisnikov
, vendar ne vrne števila povezanih lun na desni temveč množico odvisnih lun.>>> odvisniki("E", orbite) {'F', 'J', 'YOU', 'L', 'K'}
Rešitev
Vrniti moramo množico direktnih odvisnikov podane lune, ki ji potem dodamo še vse odvisnike ter direktnih odvisnikov.
def odvisniki(luna, orbite): moji = lune(orbite)[luna] vsi = set(moji) for luna in moji: vsi |= odvisniki(luna, orbite) return vsi
Nalogo je možno rešiti tudi brez rekurzije.
V seznam odvisnikov najprej dodamo kar samo luno - odstranili jo bomo na koncu. Nato gremo z zanko čez seznam odvisnikov in za vsakega odvisnika dodamo v seznam njegove odvisnike. To je eden od primerov, ko gremo z zanko čez seznam, ki ga medtem spreminjamo in to celo pravilno deluje. Ko pridemo do konca, smo dodali vse odvisnike vseh odvisnikov. Ker naloga zahteva, da vrnemo množico, vrnemo množico, v kateri so vsi odvisniki razen prvega, to je, podane lune same.
def odvisniki(luna, orbite): orbite_nazaj = lune(orbite) vsi = [luna] for odvisnik in vsi: vsi += orbite_nazaj[odvisnik] return set(vsi[1:])
Zakaj nismo že od vsega začetka uporabljali množice? Ker množica nima vrstnega reda, z zanko
for
ne moremo iti prek množice, ki se medtem spreminja. To ne le ne bi delovalo, temveč Python tega v resnici ne pusti.
Ocena 9
Napiši funkcijo
pot_med(odkod, kam, orbite)
, ki vrne (najkrajšo) pot med dvema lunama. Tokrat je dovoljeno iti proti levi in potem nazaj proti desni, torej po vseh povezavah na sliki.>>> pot_med("SAN", "K") ["SAN", "I", "D", "E", "J", "K"]
Rešitev
Nalogo ste reševali na različne zvite načine. Najbolj preprost pa je tale: poiščemo pod od obeh lun do začetne. Ob poti bosta imeli nekaj enakih skupnih končnih elementov. Te odbijemo. Zapomnimo pa si zadnjega odbitega - tam bo potrebno zaviti. Rešitev je enaka poti do zadnjega odbitega in poti od njega do cilja -- kar je ravno okrog obrnjena pot od cilja do zadnjega odbitega.
def pot_med(luna1, luna2, orbite): pot1 = pot_do(luna1, "COM", orbite) pot2 = pot_do(luna2, "COM", orbite) while pot1 and pot2 and pot1[-1] == pot2[-1]: pot1.pop() skupni = pot2.pop() return pot1 + [skupni] + pot2[::-1]
Tu smo kar predpostavili, da se prvi planet imenuje COM. Če si tega ne bi upali, pač ne bi mogli uporabiti funkcije
pot_do
, temveč bi iskali pot do začetka.def do_zacetka(luna, orbite): pot = [luna] while pot[-1] in orbite: pot.append(orbite[pot[-1]]) return pot def pot_med(luna1, luna2, orbite): pot1 = do_zacetka(luna1, orbite) pot2 = do_zacetka(luna2, orbite) while pot1 and pot2 and pot1[-1] == pot2[-1]: pot1.pop() skupni = pot2.pop() return pot1 + [skupni] + pot2[::-1]
Na eno od lun bodo postavili bazo. Baza oskrbuje lune, ki krožijo do tri korake naprej (na sliki: proti desni). Luna D lahko oskrbuje I, SAN, E, F, J, K, ne more pa oskrbovati L in YOU, ker sta predaleč.
Napiši funkcijo
baza(lune, orbite)
, ki prejme seznam poseljenih lun (lune
) in, seveda, orbite. Funkcija pove, na katero luno je potrebno postaviti bazo, da bo oskrbovala čim več lun.>>> baza(["I", "E", "J", "K"], orbite) "D"
Baza na D lahko oskrbuje vse štiri lune, baza na C ali E bi oskrbovala tri, baza na B ali J bi oskrbovala 2, baza na I ali K pa eno.
Če je možnih več enako dobrih izbir, naj vrne poljubno od njih.
Rešitev
Da rešitev ne bo predolga, bomo uporabili razred
Counter
. Spoznali smo ga na predavanjih o slovarjih; v modulucollections
dela družbodefaultdict
-u.Predstavljajmo si, da lune glasujejo, kje bo baza. Gremo čez vse lune. Vsaka glasuje zase, poleg tega pa se pri vsaki zapeljemo še za tri korake na levo in "oddajamo" glasove za te, leve lune. Tista, ki prejme največ glasov, je zmagala. To luno dobimo z
most_common
.most_common(1)
vrne seznam najpogostejših 1 lun (se pravi najpogostejšo luno). Vzamemo prvi element tega seznama. Ta je par: ime lune in število glasov. Zanima nas element lune, torej prvi element. Zato vrnemoglasov.most_common(1)[0][0]
.def baza(lune, orbite): glasov = Counter() for luna in lune: glasov[luna] += 1 for korak in range(3): luna = orbite[luna] glasov[luna] += 1 if luna not in orbite: break return glasov.most_common(1)[0][0]
Ne spreglejmo še
if luna == "COM": break
Ta je potreben zato, ker lahko ob teh treh korakih nazaj že prej trčimo na začetek.
Ocena 10
Napiši funkcijo
sirina_orbite(luna, razdalja, orbite)
, ki vrne število lun, ki krožijo na podani razdalji od podane lune. Na razdalji 2 od lune D krožijo 3 lune: J, F in SAN.Rešitev
Predstavljajmo si rodbino. Znali bi izračunati število sinov. Znali bi izračunati tudi število vnukov -- vsakega sina vprašamo, koliko sinov ima. Znamo izračunati tudi število pravnukov -- vsakega sina vprašamo, koliko vnukov ima.
V splošnem je potomcev na nivoju n toliko, kolikor imajo vsi sinovi skupan potomcev na nivoju n - 1.
def sirina_orbite(odkod, n, orbite): if n == 0: return 1 sirina = 0 for luna in lune(orbite)[odkod]: sirina += sirina_orbite(luna, n - 1, orbite) return sirina
Zdaj pa poglejmo nek drug sistem lun.
Skiciral ga je nek začetniški astronom - poimenoval je lune in zabeležil relacije med njimi. Potreboval je par dni. Ali noči. In lune so se medtem premikale.
Kaj opaziš? Da sta luni D in F pravzaprav najbrž ista luna - verjetno ju je popisal v dveh različnih nočeh! Vse, kar jima sledi, je namreč enako. Seveda imajo lune drugačna imena (izmislil si jih je on), pa na sliki tudi ni vse enako obrnjeno - G je "zgornja" luna D-ja, K pa je "srednja" luna F-ja, ampak to je vendar isto. Celotna struktura je enaka.
Prav tako bi bili lahko isti, recimo P in M.
Zdaj pa si predstavljajmo, da na sliki ne bi bilo lune J. V tem primeru D in F ne bi bila več (najbrž) isti, pač pa bi bili lahko še vedno isti P in M. Ali pa celo K in G.
Če ne bi bilo lune O, bi bili lahko isti kvečjemu P in M...
Napiši funkcijo enaka_struktura(luna1, luna2, orbite)
, ki vrne True
, če sta strukturi pod lunama luna1
in luna2
enaki in False
če nista.
Funkcija mora delovati tudi, če okrog česa kroži več, recimo 5 lun. Lahko pa predpostaviš, da jih ni bistveno več kot 5.
Nasvet: ker so lune, ki krožijo okrog določene lune, navedene v poljubnem vrstnem redu, bo potrebno pod vsako luno najbrž poskusiti vse možne vrstne rede -- ko primerjaš dve luni, pustiš lune, ki so okrog ene, v originalnem vrstnem redu, vrstni red lun okrog druge pa mešaš. Pri tem lahko uporabiš funkcijo permutations
, ki jo najdeš v modulu itertools
. Če boš tej funkciji slučajno podal(a) množico, ne bo srečna; pretvori jo v seznam.
Če se dobro lotiš, bo funkcija kar elegantna in imela približno deset vrstic.
#### Rešitev
Poiskati moramo takšno permutacijo potomcev teh dveh lun, da se ujemajo vse strukture pod njimi. Če jo najdemo, vrnemo `True`. Če je ni, vrnemo `False`.
```python
from itertools import permutations
def enaka_struktura(luna1, luna2, orbite):
lune1 = lune(orbite)[luna1]
lune2 = lune(orbite)[luna2]
if len(lune1) != len(lune2):
return False
for p in permutations(list(lune2)):
if all(enaka_struktura(pod1, pod2, orbite)
for pod1, pod2 in zip(lune1, p)):
return True
return False
```
Za κῦδος
Potrebno bo popraviti napako astronoma - začetnika. Ugotoviti je potrebno, katera luna ustreza kateri.
Napiši funkcijo
pari_lun(luna1, luna2, orbite)
, ki počne podobno kot prejšnja, le da vrača drugačen rezultat. Če sta strukturi podluna1
inluna2
različni, vrneNone
. Sicer pa vrne pare lun, ki so v bistvu ista luna.Pari so shranjeni kot terke. Vsaka terka je urejena po abecedi, celoten seznam pa je urejen po abecedi terk.
>>> pari_lun("D", "F", orbite) [('D', 'F'), ('G', 'K'), ('H', 'J'), ('I', 'L'), ('M', 'P'), ('N', 'O'), ('R', 'T'), ('S', 'U'), ('V', 'Z')]
Luni D ustreza F, luni G ustreza K, luni H ustreza J, luni I ustreza L, luni M ustreza P ...
Odgovor bi lahko bil tudi:
[('D', 'F'), ('G', 'K'), ('H', 'L'), ('I', 'J'), ('M', 'P'), ('N', 'O'), ('R', 'T'), ('S', 'U'), ('V', 'Z')]
Namesto parov H-J in I-L imamo namreč lahko tudi H-L in I-J.
Napiši pravo, lepo funkcijo, ne takšne, ki teka po vsem slovarju gor in dol in poskuša najti neke pare, ki bi ustrezali.
Funkcija ne bo bistveno daljša od prejšnje.
Rešitev
Tole je precej podobno preverjanju strukture.
Funkcija rekurzivno kliče sebe. Če najde ustrezno permutacijo, vrne pare, sicer ne vrne ničesar (in torej vrne
None
). Kadar kaj vrne, pa doda te pare k preostalim uspešno najdenim parom.def pari_lun(luna1, luna2, orbite): lune1 = lune(orbite)[luna1] lune2 = lune(orbite)[luna2] if len(lune1) == len(lune2): for p in permutations(list(lune2)): pari = [tuple(sorted([luna1, luna2]))] for pod1, pod2 in zip(lune1, p): podpari = pari_lun(pod1, pod2, orbite) if podpari is None: break pari += podpari else: return sorted(pari)