Het algoritme
In een programma dat ik ooit gemaakt heb om Japanse puzzels (griddlers, nonogrammen) op te lossen heb ik een algoritme geschreven dat alle mogelijke combinaties van een rij of kolom kan maken.
Stel dat je een rij hebt van 8 tekens lang, met 2 blokken van resp. 1 en 3 als grootte, dan zijn er deze combinaties:
Hierbij zijn de X'en gevulde hokjes, en de punten lege plekken. Om deze combinaties te maken heb ik een recursief algoritme verzonnen:
vals: de lengtes van de blokken (in het voorbeeld 1 en 3)
size: het aantal blokken (in het voorbeeld 2)
len: de lengte van de rij of kolom (in het voorbeeld 8 )
pos: de huidige positie van de blokken, 0-based
id: het blok dat één positie naar rechts moet worden verschoven, 0-based
De waarde van id is bij de eerste aanroep van deze functie het nummer van het laatste (meest rechtse) blok. De functie ngmPos geeft de waarde 'true' terug als het gelukt is een nieuwe combinatie te maken, en de waarde 'false' als dat niet gelukt is (en alle blokken zo ver mogelijk naar rechts zijn gezet).
Het probleem
Na een profiling van m'n programma blijkt dat veruit de meeste CPU-tijd in deze functie gaat zitten. Voor elke rij en kolom moeten namelijk één keer alle combinaties worden verzonnen, daarna wordt alles gecached. Nu vroeg ik me af of jullie me kunnen vertellen hoe ik het beter kan aanpakken (misschien zie ik iets over het hoofd en slaat m'n hele verzinsel nergens op), of hoe ik simpelweg deze functie wat sneller kan laten werken.
In een programma dat ik ooit gemaakt heb om Japanse puzzels (griddlers, nonogrammen) op te lossen heb ik een algoritme geschreven dat alle mogelijke combinaties van een rij of kolom kan maken.
Stel dat je een rij hebt van 8 tekens lang, met 2 blokken van resp. 1 en 3 als grootte, dan zijn er deze combinaties:
code:
1
2
3
4
5
6
7
8
9
10
| X.XXX... X..XXX.. X...XXX. X....XXX .X.XXX.. .X..XXX. .X...XXX ..X.XXX. ..X..XXX ...X.XXX |
Hierbij zijn de X'en gevulde hokjes, en de punten lege plekken. Om deze combinaties te maken heb ik een recursief algoritme verzonnen:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| bool ngmPos(unsigned char *vals, unsigned int size, unsigned int len, unsigned int *pos, unsigned int id) { if (((id == size - 1) && (len - pos[id] > vals[id])) || ((id < size - 1) && (pos[id + 1] - pos[id] - 1 > vals[id]))) { pos[id]++; } else { if (id == 0) return false; if (!ngmPos(vals, size, len, pos, id - 1)) return false; pos[id] = pos[id - 1] + vals[id - 1] + 1; } return true; } |
vals: de lengtes van de blokken (in het voorbeeld 1 en 3)
size: het aantal blokken (in het voorbeeld 2)
len: de lengte van de rij of kolom (in het voorbeeld 8 )
pos: de huidige positie van de blokken, 0-based
id: het blok dat één positie naar rechts moet worden verschoven, 0-based
De waarde van id is bij de eerste aanroep van deze functie het nummer van het laatste (meest rechtse) blok. De functie ngmPos geeft de waarde 'true' terug als het gelukt is een nieuwe combinatie te maken, en de waarde 'false' als dat niet gelukt is (en alle blokken zo ver mogelijk naar rechts zijn gezet).
Het probleem
Na een profiling van m'n programma blijkt dat veruit de meeste CPU-tijd in deze functie gaat zitten. Voor elke rij en kolom moeten namelijk één keer alle combinaties worden verzonnen, daarna wordt alles gecached. Nu vroeg ik me af of jullie me kunnen vertellen hoe ik het beter kan aanpakken (misschien zie ik iets over het hoofd en slaat m'n hele verzinsel nergens op), of hoe ik simpelweg deze functie wat sneller kan laten werken.