Practicum Datastructuren Opgave 9: Backtrack–algoritmen 1

advertisement
Practicum Datastructuren
– voorjaar 2006 –
Opgave 9: Backtrack–algoritmen
1
Achtergrond
Veel problemen zijn niet analytisch op te lossen. We kunnen zon probleem soms met een computer
oplossen door van alle denkbare megelijkheden na te gaan of ze inderdaad werkelijke oplossingen
zijn. Een te volgen aanpak kan dan zijn: zolang niet alle denkbare mogelijkheden geprobeerd
doe: pak een volgende mogelijkheid en controleer of die een oplossing is. Afhankelijk van het
specifieke probleem moet deze aanpak bijgeschaafd en verder uitgewerkt worden. Vaak kan zon
trial–and–error –aanpak elegant op een recursieve wijze geformuleerd worden. Een simpele aanpak
hierbij gaat op de brute force manier (alle mogelijke combinaties proberen). Een intelligentere
maar toch relatief eenvoudige aanpak, is die met een ‘terugkrabbel’ backtrack-algoritme.
2
Leerdoelen
Na afloop van deze opdracht ben je in staat om:
• aan te geven wat de basisprincipes zijn van backtrack -algoritmen;
• een situatie voor toepassing van een backtrack -aanpak te herkennen;
• voor eenvoudige problemen een correcte backtrack -oplossing in de vorm van een algoritme te
bedenken.
3
Instructie
Bestudeer allereerst de onderdelen over backtracking uit het Algoritmiek-dictaat (het [eerste deel
van] hoofdstuk 12) en de sheets over dit onderwerp zoals die gebruikt zijn op het college (bekijk
beslist ook de programma-voorbeelden) en uiteraard je op het hoorcollege gemaakte eigen aantekeningen en begin pas daarna aan de volgende deelopdrachten.
4
Probleemschets 1: Aantal combinaties
Beschouw de volgende situatie met een rijtje getallen, die allemaal groter dan 1 zijn, bijvoorbeeld:
const int Rijlengte
= 10;
int rij [ Rijlengte ] = { 2 , 3 , 5 , 7 , 8 , 11 , 13 , 17 , 19 , 23 , 29 } ;
We kunnen ons nu afvragen op hoeveel manieren een gegeven getal als product van de getallen in
de rij te schrijven is. De getallen in de rij mogen hierbij zo vaak gebruikt worden als je maar wilt.
Het getal 20 is bijvoorbeeld te schrijven als 2 ∗ 2 ∗ 5, of 2 ∗ 5 ∗ 2, of 5 ∗ 2 ∗ 2, op 3 manieren dus. Er
blijkt geen enkele manier te zijn om het getal 31 als product van getallen in deze rij te schrijven.
Het moet mogelijk zijn om via het toetsenbord de waarde van het getal in te voeren, waarvoor
bepaald moet worden óf (hoe vaak etc.) het als product van die getallen geschreven kan worden.
Deelopdracht 1
Ontwerp en implementeer de functie
int bepaalAantalCombinaties ( int doelwaarde, int product)
waarmee backtrackend bepaald wordt hoe vaak het getal doelwaarde gemaakt kan worden uit de
gegeven globale rij van getallen. De parameter product wordt gebruikt om het product van de
getallen die op het moment van de aanroep uit de gegeven rij zijn gekozen. De beginwaarde bij de
hoofdaanroep (bijvoorbeeld vanuit je main-functie) is 1. Waarschijnlijk kan je backtrack-aanpak
impliciet zijn.
Deelopdracht 2
Hetzelfde als bij 1 waarbij echter oplossingen waarvoor precies dezelfde reeks cijfers is gebruikt
maar één keer meetellen. Dus 2 ∗ 2 ∗ 5, 2 ∗ 5 ∗ 2 en 5 ∗ 2 ∗ 2 tellen als één oplossing voor het doelgetal
20. Bovendien moet(en) de oplossing(en) getoond worden. Test je programma uit en neem de
uitvoer op als commentaarregel(s) aan het einde van je programmacode.
5
Probleemschets 2: Som van kwadraten
Voor sommige gehele getallen is het mogelijk hun kwadraat te schrijven als de som van de kwadraten
van andere gehele getallen. Zo kunnen we bijvoorbeeld 5 schrijven als 52 = 32 + 42 . We beperken
ons tot positieve gehele getallen en tot situaties waarbij in de kwadratenserie elk getal slechts
éénmaal mag voorkomen (niet toegestaan is dus bijvoorbeeld: 22 = 12 +12 +12 +12 ). Voor sommige
getallen is het niet mogelijk om het kwadraat als de som van andere kwadraten te schrijven, voor
andere getallen kan het wel, soms zelfs op meerdere manieren (bijvoorbeeld 92 = 82 + 42 + 12 =
62 + 52 + 42 + 22 ).
Deelopdracht 3: Bepaal alle mogelijke kwadratenseries
Stel nu een backtrack procedure
void bepaalKwadratenSeries(int getal)
op (plus eventueel benodigde hulpprocedures), die alle mogelijke manieren afdrukt om het kwadraat
van opgegeven getal als een som van kwadraten te schrijven. Hint: je zult hier een hulprij en zeer
waarschijnlijk expliciet backtracken bij nodig hebben. Bij de hoofdaanroep:
bepaalKwadratenSeries( 9 ) ;
krijg je als mogelijke uitvoer (je mag kiezen of je de kwadraten van laag naar hoog of van hoog
naar laag afdrukt):
Gevonden kwadratenserie(s) voor het getal 9 :
9∗9 = 1∗1 + 4∗4 + 8∗8
9∗9 = 2∗2 + 4∗4 + 5∗5 + 6∗6
Test het programma uit en neem de uitvoer op als commentaarregel(s) op het einde van je
programma-code.
Deelopdracht 4: Bepaal kortste kwadratenserie
Breid je vorige procedure(s) nu zodanig uit, dat via een aanroep als bepaalKortsteKwadratenSeries(9);
je bij de uitvoer alleen de kortste kwadratenserie te zien krijgt.
6
Producten
Als producten moet je (uitgeteste!) C++-code hebben, die ervoor zorgt dat je programma voldoet
aan de gevraagde specificaties van de diverse deelopdrachten en waarbij je uitwerking voldoet aan
de kwaliteitscriteria zoals die gesteld worden (zie bij punt 7. Zelfreflectie). Geef bovendien de
gevraagde uitvoer van deelopdracht 2 en 3.
7
Zelfreflectie
Via de cursuswebpagina kom je uit op een aantal criteria die we ook bij deze cursus gebruiken voor
de kwaliteit van je ingeleverde werk. Ga voor jezelf na [voor zover dat nu al van toepassing is]
in hoeverre je uitwerkingen voldoen aan deze criteria. Je uitwerking van deze zelfreflectie moet je
echter niet inleveren.
8
Inleveren van je producten
Vóór maandag 24 april, 9.00 uur, en wel door jullie programma één keer via e-mail met de tekst
PI2 opgave9 in de Subject:-regel naar [email protected] te sturen. Vergeet
niet om in de uitwerking duidelijk jullie namen te vermelden.
Download