Op Boxing Day1 belooft de manager van een 10-koppige band om een dikke eindejaarspremie uit te keren als de muzikanten slagen in de volgende opdracht.
De manager heeft de 10 verschillende instrumenten van de muzikanten in 10 afgesloten dozen gestopt, die hij in een geluidsdichte kamer heeft geplaatst. Hij heeft de dozen ook gelabeld met de 10 verschillende instrumenten, maar de inhouden van de dozen komen niet noodzakelijk overeen met hun labels. De muzikanten moeten om de beurt de kamer binnengaan om hun eigen instrument te zoeken. Daarbij mogen ze maximaal 5 dozen openen. Als ze hun eigen instrument gevonden hebben dan moeten ze de geopende dozen terug afsluiten en de kamer langs een andere deur verlaten. Ze mogen op geen enkele manier met elkaar communiceren. De muzikanten slagen enkel in de opdracht als ze allemaal hun eigen instrument gevonden hebben.
Als elke muzikant willekeurig 5 dozen opent dan heeft hij 50% kans om zijn eigen instrument te vinden. De kans dat alle muzikanten op deze manier hun eigen instrument vinden is \[\left(\frac{1}{2}\right)^{10} = \frac{1}{1024}\] Dat is dus minder dan 0.1%.
De band heeft echter een strategie bedacht waarmee ze 35% kans hebben om de opdracht te doen slagen. Elke muzikant gaat de kamer binnen en opent de doos die gelabeld is met zijn instrument. Als de doos zijn eigen instrument bevat, dan is hij geslaagd in zijn deel van de opdracht. Anders kijkt hij welk instrument er in de doos zit en opent daarna de doos die gelabeld is met dat instrument.
Hij blijft dit herhalen totdat hij ofwel 5 dozen geopend heeft zonder zijn eigen instrument te vinden (waardoor de opdracht mislukt) of totdat hij zijn eigen instrument gevonden heeft (waardoor hij slaagt in zijn deel van de opdracht).
Deze strategie werkt omdat ze elke muzikant laat zoeken in een gelinkte reeks dozen die uiteindelijk zal uitkomen bij de doos die het instrument van de muzikant bevat. Deze doos linkt terug naar de doos waar de muzikant zijn zoektocht gestart is, waardoor de gelinkte reeks dozen een lus vormt. Een dergelijke lus wordt in de grafentheorie2 een cykel genoemd.
Deze strategie werkt veel beter dan het willekeurig uitkiezen van dozen. Voor een even aantal dozen $$m = 2n$$ kan aangetoond worden dat de kans dat de langste cykel kleiner of gelijk is aan $$n$$ kan berekend worden als \[ 1 - \left( \frac{1}{n+1} + \frac{1}{n+2} + \frac{1}{n+3} + \cdots + \frac{1}{2n} \right)\] Voor $$m = 10$$ wordt deze kans ongeveer 35%. Voor grote $$m$$ nadert deze kans naar 30%. Het is dus zeker niet gegarandeerd dat de muzikanten in hun opdracht zullen slagen, maar met een klein beetje geluk kunnen ze toch hun eindejaarspremie opstrijken.
Er zijn $$m$$ verschillende items die elk worden beschreven door een unieke string die geen komma's bevat. Deze items werden in $$m$$ dozen gestopt. De dozen werden ook gelabeld met de beschrijvingen van de $$m$$ items, maar de labels van de dozen komt niet noodzakelijk overeen met hun inhouden.
De labels en de inhouden van de dozen worden beschreven in een tekstbestand. Elke regel van het tekstbestand beschrijft een doos aan de hand van twee beschrijvingen van items, die van elkaar gescheiden worden door een komma (,). De eerste beschrijving correspondeert met het label van de doos en de tweede met de inhoud van de doos. Zo'n tekstbestand kan er dan bijvoorbeeld als volgt uitzien:
guitar,conga saxophone,banjo conga,maracas drum set,saxophone trumpet,guitar bass guitar,trombone synthesizer,trumpet banjo,drum set trombone,bass guitar maracas,synthesizer
Onderstaande figuur toont een grafische voorstelling van de labels en de inhouden van de tien dozen, zoals beschreven in dit tekstbestand.
Gevraagd wordt:
Schrijf een functie lees_dozen waaraan de locatie van een tekstbestand (str) moet doorgegeven worden dat de labels en de inhouden van een aantal dozen beschrijft. De functie moet een dictionary (dict) teruggeven die het label op elke doos afbeeldt op de inhoud van die doos. Labels en inhouden worden hierbij beschreven aan de hand van dezelfde stringvoorstelling als in het gegeven tekstbestand. Als het tekstbestand geen geldige omschrijving van dozen bevat dan moet een AssertionError opgeworpen worden met de boodschap ongeldige dozen. Dat is het geval als hetzelfde label meerdere keren voorkomt, als dezelfde inhoud meerdere keren voorkomt, of als de labels op hun volgorde na niet gelijk zijn aan de inhouden.
Schrijf een functie cykel waaraan twee argumenten moeten doorgegeven worden: i) de beschrijving van een item (str) en ii) een dictionary (dict) zoals die wordt teruggegeven door de functie lees_dozen en die dus de labels en de inhouden van een aantal dozen vastlegt. Als het gegeven item niet in de gegeven dozen gestopt werd dan moet een AssertionError opgeworpen worden met de boodschap ongeldig item. Anders moet de functie een tuple (tuple) teruggegeven dat de cykel van dozen omschrijft die begint bij het gegeven item.
Schrijf een functie langste_cykel waaraan een dictionary (dict) moet doorgegeven worden zoals die wordt teruggegeven door de functie lees_dozen en die dus de labels en de inhouden van een aantal dozen vastlegt. De functie moet de lengte (int) van de langste cykel van dozen teruggeven, waarbij elk item in de gegeven dozen als startpunt van een cykel beschouwd wordt.
Bij onderstaande voorbeeldsessie gaan we ervan uit dat de tekstbestanden dozen01.txt3, dozen02.txt4, dozen03.txt5 en dozen04.txt6 zich in de huidige directory bevinden.
>>> dozen = lees_dozen('dozen01.txt7')
>>> dozen
{'guitar': 'conga', 'saxophone': 'banjo', 'conga': 'maracas', 'drum set': 'saxophone', 'trumpet': 'guitar', 'bass guitar': 'trombone', 'synthesizer': 'trumpet', 'banjo': 'drum set', 'trombone': 'bass guitar', 'maracas': 'synthesizer'}
>>> cykel('guitar', dozen)
('guitar', 'conga', 'maracas', 'synthesizer', 'trumpet')
>>> cykel('saxophone', dozen)
('saxophone', 'banjo', 'drum set')
>>> cykel('bass guitar', dozen)
('bass guitar', 'trombone')
>>> cykel('clarinet', dozen)
Traceback (most recent call last):
AssertionError: ongeldig item
>>> langste_cykel(dozen)
5
>>> lees_dozen('dozen02.txt8') # 2x zelfde label
Traceback (most recent call last):
AssertionError: ongeldige dozen
>>> lees_dozen('dozen03.txt9') # 2x zelfde inhoud
Traceback (most recent call last):
AssertionError: ongeldige dozen
>>> lees_dozen('dozen04.txt10') # labels verschillen van inhouden
Traceback (most recent call last):
AssertionError: ongeldige dozen
Gál A, Miltersen PB (2003). The cell probe complexity of succinct data structures. In International Colloquium on Automata, Languages, and Programming, Springer, Berlin, Heidelberg, 332–344. 11
Gál A, Miltersen PB (2007). The cell probe complexity of succinct data structures. Theoretical Computer Science 379(3), 405–417. 12
Winkler P (2006). Seven puzzles you think you must no have heard correctly. Seventh Gathering for Gardner (in honour of Martin Gardner).13