5.2. Steuerung der Reihenfolge der Problemvariablen
Um die Reihenfolge der Problemvariablen zu steuern, wird die Liste der Problemvariablen
nach den gewünschten Kriterien sortiert. Zum Beispiel könnte es sinnvoll sein,
beim labeling zuerst die Veranstaltungen in den Stundenplan zu legen, welche die
grössten Teilnehmerzahlen haben, da diese Veranstaltungen in der Regel leichter
Konflikte (und somit Backtracking beim labeling) auslösen, als Veranstaltungen mit
geringeren Teilnehmerzahlen.
Wenn man davon ausgehen kann, dass alle Kriterien nach denen sortiert werden soll, durch einen
gültigen Prologausdruck beschreiben lassen (so lässt sich das Kriterium Teilnehmeranzahl
durch einen numerischen Wert ausdrücken), dann besteht die Sortierung der Liste der Problemvariablen aus vier Schritten:
-
Genereierung eines Tupels für jede Veranstaltung, das aus den, durch Prologausdrücke
ausgedrückten, Sortierkriterien besteht und aus der VeranstaltungsID.
(kriterium1, kriterium2, ..., kriteriumn, VeranstaltungsID)
Diese Tupel können dann in einer Liste verwaltet werden.
-
Die Liste aus Tupeln wird mit dem vorgegebenen Prädikat sort sortiert.
Da die VeranstaltungsIDs in den Tupeln in letzter Position stehen, spielen
sie für die Sortierug keine Rolle. Stattdessen wird die Liste nach den Ausdrücken
sortiert, welche die Sortierkriterien abbilden.
-
Anhand der Reihenfolge, in der die VeranstaltungsIDs in der SortierListe
jetzt vorliegen, kann die Liste der Problemvariablen, in deren Tupeln ja ebenfalls die
VeranstaltungsIDs enthalten sind, sortiert werden.
-
Die sortierte Liste der Problemvariablen wird nun an das labeling Prädikat
übergeben
Beispiel:
Sortierung der Problemvariablen nach Teilnehmeranzahl und zur Verfügung stehenden
Räume:
main :-
...
findall([TeilnehmerAnz0, RaumAnz, VeranstaltungsID],
(veranstaltung(VeranstaltungsID, Teilnehmer, BenRessourcen),
zaehleTeilnehmer(Teilnehmer, TeilnehmerAnz),
TeilnehmerAnz0 is TeilnehmerAnz * (-1),
findall(RaumID, raum(RaumID, _, _, _), RaumListe),
holeGueltigeRaeume(RaumListe, VeranstaltungsID, GueltigeRaeume),
length(GueltigeRaeume, RaumAnz)
),
SortKriteriumsListe
),
sort(SortKriteriumsListe, SortListe),
mkVerIDListe(SortListe, SortVerIDListe),
sortiere(ProblemVars, SortVerIDListe, SortiertePVars),
labeling(SortiertePVars),
...
mkVerIDListe([], []).
mkVerIDListe([SortElement|SortRest], [VerID|VerIDRest]) :-
letztes(VerID, SortElement)
mkVerIDListe(SortRest, VerIDRest).
letztes(LetztesEle, [LetztesEle]).
letztes(LetztesEle, [_|Rest]) :- letztes(LetztesEle, Rest).
sortiere(_, [], []).
sortiere(PVars, [VerID|VIDRest], [Element|ErgebnisListe]) :-
suchen(PVars, VerID, Element),
sortiere(PVars, VIDRest, ErgebnisListe).
suchen([raumTagZeit(Raum, Tag, Zeit, VerID)|PRest], VerID, raumTagZeit(Raum, Tag, Zeit, VerID)).
suchen([PVar|PRest], VerID, Element) :- suchen(PRest, VerID, Element).
Die Wirkung dieses Algorithmusses soll an einem Beispiel deutlich gemacht werden:
Reihenfolge der Problemvariablen:
[raumTagZeit(Raum, Tag, Zeit, bwl1), raumTagZeit(Raum, Tag, Zeit, fourier_laplace),
raumTagZeit(Raum, Tag, Zeit, analysis1_1)]
SortKriteriumsListe nach findall:
[[-60, 5, bwl1], [30, 5, fourier_laplace], [-60, 3, analysis1_1]]
SortListe nach dem sort:
[[-60, 3, analysis1_1], [-60, 5, bwl1], [30, 5, fourier_laplace]]
SortVerIDListe nach dem mkVerIDListe:
[analysis1_1, bwl1, fourier_laplace]
Reihenfolge der Problemvariablen nach dem sortiere:
[raumTagZeit(Raum, Tag, Zeit, analysis1_1), raumTagZeit(Raum, Tag, Zeit, bwl1),
raumTagZeit(Raum, Tag, Zeit, fourier_laplace)]
Was diesen Algorithmus auszeichnet ist, dass zum Wechseln der Heuristik nur der findall-Ausdruck ausgewechselt
werden muss, da die Sortierprädikate heuristikunabhängig arbeiten. Aus diesem Grund
wird das Tupel aus Sortierkriterien auch nicht als Struct, sondern als Liste abgebildet.
5.3. Steuerung der Reihenfolge der Wertebelegung
Die Steuerung der Reihenfolge, in welcher den Domainvariablen Raum, Tag und Zeit Werte aus
ihren Wertebereichen zugewiesen werden, lässt sich durch Überschreiben des in der
FiniteDomain Library vorgegebenen Prädikats indomain realisieren. Das vorgegebene
Prädikat indomain/1 macht nichts anderes, als die Werte aus der Domain einer Problemvariablen
in der Reihenfolge auszuprobieren, in der sie in der Domain vorliegen und das ist bei Domains aus
numerischen Werten die numerische Reihenfolge.
Beispiel:
Tag::1..5
Reihenfolge beim Zuweisen mit indomain/1: 1, 2, 3, 4, 5
In einem selbstdefinierten indomain Prädikat kann man den Wertebereich der
Domainvariablen umsortieren, um den Wertebereich in einer dem Problem mehr
angepassten Ordnung, als der der numerischen Grösse der Werte, auszuprobieren. So kann es zum
Beispiel bei der Auswahl von Werten für die Domainvariable Raum sinnvoll sein,
zunächst Räume zu wählen, bei denen die Differenz zwischen dem angebotenen Platz
und dem Platz den die Teilnehmer der entsprechenden Veranstaltung belegen, möglichst gering ist.
Dadurch wird die Wertebelegung dieser Variable von Anfang an in Richtung einer ökonomischen Ressourcennutzung
gesteuert.
Dazu wird mit dem von der FiniteDomain Library vorgegebenen Prädikat dom/2 der Wertebereich einer
Domainvariablen auf eine Liste abgebildet. Für jeden Wert dieser Liste werden ein oder mehrere Bewertungswerte,
in Abhängigkeit des/der Kriterums/en nach denen die Auswahl der Werte erfolgen soll, generiert.
Nach diesen Kriteriumsbewertungwerten kann dann die Liste der Werte des Wertebereichs der
Domainvariablen sortiert werden.
Durch das vorgegebene Prädikat member, wird dann die Domainvariable mit dem
erstmöglichen Wert dieser umsortierten Liste instanziiert.
Beispiel:
Veranstaltungen sollen bei der Suche im Suchraum immer zunächst in Räume gelegt werden, bei denen die Differenz zwischen
dem Platz, den sie zur Verfügung stellen, und dem Platz, den die Teilnehmer der entsprechenden Veranstaltung
belegen, möglichst gering ist.
labeling([ProblemVar|Rest]) :-
ProblemVar = raumTagZeit(Raum, _, _, VeranstaltungsID),
veranstaltung(VeranstaltungsID, Teilnehmer, _),
zaehleTeilnehmer(Teilnehmer, TeilnehmerAnzahl),
dom(Raum, RaumListe),
mkRaumKriteriumsListe(RaumListe, TeilnehmerAnzahl, KriteriumsListe),
indomain(Raum, KriteriumsListe),
indomain(Tag),
indomain(Zeit),
labeling(Rest).
mkRaumKriteriumsListe([], _, []).
mkRaumKriteriumsListe([RaumID|RRest], TeilnehmerAnzahl, [Kriterium|KRest]) :-
raum(RaumID, Platz, _, _),
KriteriumsInhalt is Platz - TeilnehmerAnzahl,
Kriterium = [KriteriumsInhalt],
mkRaumKriteriumsListe(RRest, TeilnehmerAnzahl, KRest).
indomain(DomainVar, _) :-
nonvar(DomainVar).
indomain(DomainVar, KriteriumsListe) :-
var(DomainVar),
dom(DomainVar, Wertebereichsliste),
sortiereWerte(Wertebereichsliste, KriteriumsListe, SortierteWertebereichsListe),
member(DomainVar, SortierteWertebereichsListe).
sortiereWerte(Wertebereichsliste, KriteriumsListe, SortierteWertebereichsListe) :-
mkKombiListe(Wertebereichsliste, KriteriumsListe, KombiListe),
sort(KombiListe, SortierteKombiListe),
mkWerteListe(SortierteKombiListe, SortierteWertebereichsListe).
mkKombiListe([], [], []).
mkKombiListe([Wert|WRest], [Kriterium|KRest], [KombiEle|KombiRest]) :-
append([Wert], Kriterium, KombiElement),
mkKombiListe(WRest, KRest, KombiRest).
mkWerteListe([], []).
mkWerteListe([Kriterium|SKRest], [Wert|SWRest]) :-
letztes(Wert, Kriterium),
mkWerteListe(SKReste, SWRest).
Die Wirkung dieses Algorithmusses soll an einem Beispiel deutlich gemacht werden:
Die Domain der Domainvariablen Raum sei für die Veranstaltung analysis1_1:
Raum :: 1..5
Die TeilnehmerAnzahl sei: 100
Die Liste RaumListe nach dem dom im labeling ist:
[1, 2, 3, 4, 5]
Die KriteriumsListe nach mkRaumKriteriumsListe:
[0, 100, 50, 50, 150]
das heisst nach der benutzten Wissensbasis hat Raum 1 genau 100 Plätze,
Raum 2 200 Plätze, Raum 3 und 4 150 Plätze und Raum 5 250 Plätze.
Die Wertebereichsliste nach dem dom im indomain:
[1, 2, 3, 4, 5]
Die SortierteWertebereichsListe nach dem sortiereWerte im indomain:
[1, 3, 4, 2, 5]
Auch dieser Algorithmus erlaubt es, die verwendete Heuristik sehr einfach zu ändern.
Das überschriebene indomain Prädikat funktioniert für alle möglichen
Heuristiken, nur im labeling-Prädikat muss entsprechend geändert werden.