a

Chevrons Auslosungsalgorithmus

Ansichten
Aus Conquepedia
(Unterschied zwischen Versionen)
Wechseln zu: Navigation, Suche
(Die Seite wurde neu angelegt: „Hier mal die Beschreibung meines Vorschlags für den Masters-Auslosungsalgorithmus. Ich versuche, ziemlich ausführlich zu schreiben, damit es keine Probleme beim…“)
 
Zeile 1: Zeile 1:
 
Hier mal die Beschreibung meines Vorschlags für den Masters-Auslosungsalgorithmus.
 
Hier mal die Beschreibung meines Vorschlags für den Masters-Auslosungsalgorithmus.
Ich versuche, ziemlich ausführlich zu schreiben, damit es keine Probleme beim Implementieren gibt. Im Fall von Unklarheiten einfach fragen smile
+
Ich versuche, ziemlich ausführlich zu schreiben, damit es keine Probleme beim Implementieren gibt. Im Fall von Unklarheiten einfach fragen
  
 
Grundsätzlich teilt sich das Problem auf in
 
Grundsätzlich teilt sich das Problem auf in
Zeile 144: Zeile 144:
 
Zum einen fehlten noch die Vergleichgsmöglichkeiten für die verschiedenen Losungen bzw. Losungspaare/-profile, zum anderen eine Funktion, die eine einzelne Losung "innerlich" optimiert, d.h., die aus 4 bzw. 6 bzw. 8 Spielern zwei möglichst faire Teams macht. Fangen wir mal mit Letzterem an, dann haben wir's hinter uns.
 
Zum einen fehlten noch die Vergleichgsmöglichkeiten für die verschiedenen Losungen bzw. Losungspaare/-profile, zum anderen eine Funktion, die eine einzelne Losung "innerlich" optimiert, d.h., die aus 4 bzw. 6 bzw. 8 Spielern zwei möglichst faire Teams macht. Fangen wir mal mit Letzterem an, dann haben wir's hinter uns.
  
== A. Optimierungsalgorithmus für Einzellosung ==
+
=== A. Optimierungsalgorithmus für Einzellosung ===
  
 
INPUT:
 
INPUT:
eine nach Ratings sortierte Liste von Spielern (4, 6 odercool mit ihren Ratings (1D-Array)
+
eine '''nach Ratings sortierte''' Liste von Spielern (4, 6 oder mit ihren Ratings (1D-Array)
  
 
OUTPUT:
 
OUTPUT:
Zeile 162: Zeile 162:
 
Dafür brauchen wir eine Bewertungsfunktion, ein Maß für Fairness. Natürlich sollen die Teams möglichst ebenbürtig sein, deshalb wird hier einfach die minimale Differenz zwischen den Teams gesucht.
 
Dafür brauchen wir eine Bewertungsfunktion, ein Maß für Fairness. Natürlich sollen die Teams möglichst ebenbürtig sein, deshalb wird hier einfach die minimale Differenz zwischen den Teams gesucht.
 
WICHTIG: Es kommt hier sehr darauf an, dass alles schnell geht. Es wird deshalb immer nur das erste Team betrachtet und dessen einzelne Ratings aufaddiert. Zusätzlich wird einmalig die Summe aller Ratings in der aktuellen Losung bestimmt. Die Differenz aus Gesamtsumme und Summe des ersten Teams ergibt natürlich die Summe der Ratings des zweiten Teams. Die Gesamtsumme lässt sich für alle Verteilungen wiederverwenden, und wir benutzen die Zielfunktion
 
WICHTIG: Es kommt hier sehr darauf an, dass alles schnell geht. Es wird deshalb immer nur das erste Team betrachtet und dessen einzelne Ratings aufaddiert. Zusätzlich wird einmalig die Summe aller Ratings in der aktuellen Losung bestimmt. Die Differenz aus Gesamtsumme und Summe des ersten Teams ergibt natürlich die Summe der Ratings des zweiten Teams. Die Gesamtsumme lässt sich für alle Verteilungen wiederverwenden, und wir benutzen die Zielfunktion
Betrag( [Summe Team 1] x 2 - [Gesamtsumme] ),
+
'''Betrag( [Summe Team 1] x 2 - [Gesamtsumme] )''',
 
die nichts anderes ist als die Differenz zwischen den beiden Teams. Die Multiplikation sollte, falls irgendwie möglich, per Linksshift geschehen. Falls sich das in php nicht realisieren lässt, ist es wahrscheinlich schneller, die Summe von Team 1 zu sich selbst zu addieren, statt sie mit 2 zu multiplizieren.
 
die nichts anderes ist als die Differenz zwischen den beiden Teams. Die Multiplikation sollte, falls irgendwie möglich, per Linksshift geschehen. Falls sich das in php nicht realisieren lässt, ist es wahrscheinlich schneller, die Summe von Team 1 zu sich selbst zu addieren, statt sie mit 2 zu multiplizieren.
 
Je kleiner der Wert der Zielfunktion, desto fairer sind die Teams.
 
Je kleiner der Wert der Zielfunktion, desto fairer sind die Teams.
Zeile 179: Zeile 179:
 
Bei 6 Spielern gibt es 10 Möglichkeiten (2 aus 5), Teams zu bilden. Wendet man die obige Überlegung an, bleiben folgende Verteilungen übrig:
 
Bei 6 Spielern gibt es 10 Möglichkeiten (2 aus 5), Teams zu bilden. Wendet man die obige Überlegung an, bleiben folgende Verteilungen übrig:
 
126, 136, 145, 146, 156.
 
126, 136, 145, 146, 156.
Diese fünf Verteilungen werden verglichen, und fertig smile
+
Diese fünf Verteilungen werden verglichen, und fertig
  
 
-----
 
-----
Zeile 227: Zeile 227:
 
Die Vorzeichen von a, b und c lassen sich z.B. zusammen in einem Byte speichern (als Bits codiert), wodurch die acht Fälle dann den Zahlen von 0 bis 7 entsprechen.
 
Die Vorzeichen von a, b und c lassen sich z.B. zusammen in einem Byte speichern (als Bits codiert), wodurch die acht Fälle dann den Zahlen von 0 bis 7 entsprechen.
  
Damit haben wir den unübersichtlichen Teil auch schon geschafft smile
+
Damit haben wir den unübersichtlichen Teil auch schon geschafft  
 
Zusammenfassend nochmal die einzelnen Schritte:
 
Zusammenfassend nochmal die einzelnen Schritte:
  
Zeile 238: Zeile 238:
 
-> Nachdem alle relevanten Verteilungen geprüft wurden, wird die optimale Verteilung zurückgegeben, evtl. auch der Zielfunktionswert, falls er für den Vergleich mit anderen Losungen herangezogen werden soll.
 
-> Nachdem alle relevanten Verteilungen geprüft wurden, wird die optimale Verteilung zurückgegeben, evtl. auch der Zielfunktionswert, falls er für den Vergleich mit anderen Losungen herangezogen werden soll.
  
== B.1 Bewertungsfunktionen für Einzellosungen ==
+
=== B.1 Bewertungsfunktionen für Einzellosungen ===
  
 
INPUT:
 
INPUT:
Zeile 268: Zeile 268:
 
empfehlen. Aber evtl. lässt sich der Algorithmus ja so schreiben, dass [alpha] und [beta] nachträglich noch verändert werden können bzw. dem Algorithmus beim Aufruf als Argumente übergeben werden (mit bestimmten Default-Werten).
 
empfehlen. Aber evtl. lässt sich der Algorithmus ja so schreiben, dass [alpha] und [beta] nachträglich noch verändert werden können bzw. dem Algorithmus beim Aufruf als Argumente übergeben werden (mit bestimmten Default-Werten).
  
== B.2 Bewertungsfunktion für Losungspaare/-profile ==
+
=== B.2 Bewertungsfunktion für Losungspaare/-profile ===
  
 
INPUT:
 
INPUT:

Version vom 1. September 2009, 23:02 Uhr

Hier mal die Beschreibung meines Vorschlags für den Masters-Auslosungsalgorithmus. Ich versuche, ziemlich ausführlich zu schreiben, damit es keine Probleme beim Implementieren gibt. Im Fall von Unklarheiten einfach fragen

Grundsätzlich teilt sich das Problem auf in a) die Bestimmung des besten Losungsprofils (z.B. ein 4v4, darunter ein 3v3, darunter ein 3v3) und b) die optimale Aufteilung der einzelnen Spieler auf die verschiedenen Losungen.

Da nicht alle theoretisch möglichen Auslosungen verglichen werden, wird an manchen Stellen auch heuristisch vorgegangen. Insgesamt ist es aber ein ziemlich gründlicher Algorithmus.

Ich beschreibe zuerst kurz die einzelnen Schritte und danach die Details.

1. Die möglichen Losungsprofile werden bestimmt. Dabei wird dem 4v4 grundsätzlich Vorrang gegeben, d.h., es werden nur solche Profile aufgenommen, die die maximale Anzahl an 4v4-Losungen beinhalten. Z.B. werden bei 24 Spielern drei 4v4-Losungen gelost und nicht vier 3v3-Losungen. 2v2-Losungen werden nur benutzt, falls es nicht anders geht (d.h. bei 4 oder 10 Spielern). Bei mehr als 32 Spielern wird zunächst ein 4v4 an die oberste Stelle gesetzt, und das iterativ, bis 32 oder weniger Spieler übrig sind. So bleibt die Anzahl der Losungsprofile im einstelligen Bereich.

2. Für jedes Losungsprofil werden nun die folgenden Schritte durchgeführt:

2.1 Falls nur noch zwei oder weniger Losungen zu besetzen sind, wird zu Schritt 2.5 gesprungen.

2.2 Die oberste noch zu besetzende Losung wird in Angriff genommen. Ist es ein 3v3, dann werden die beiden höchstgerateten noch freien Spieler für diese Losung ausgewählt. Ist es ein 4v4, dann werden stattdessen die vier höchstgerateten noch freien Spieler für diese Losung ausgewählt. In beiden Fällen bleiben vier freie Spots in der aktuellen Losung übrig.

2.3 Für jede beliebige Wahl von 4 aus den nächsthöchsten 12 noch freien Spielern wird die optimale Losung bestimmt (zusammen mit den 2 bzw. 4 Spielern aus Schritt 2.2). Das beste Ergebnis wird gespeichert. Damit liegt nun eine volle 3v3- bzw. 4v4-Losung vor.

2.4 Es wird getestet, ob es möglich ist, einen Spieler aus dem stärkeren Team der Losung aus Schritt 2.3 durch einen noch freien Spieler zu ersetzen und dadurch die Losung zu verbessern. Ist das möglich, so wird der Spieler ersetzt und Schritt 2.4 noch bis zu viermal neu gestartet. Ist keine derartige Verbesserung möglich, oder wurde Schritt 2.4 schon zum dritten Mal gestartet, dann wird die aktuelle Losung akzeptiert, die zugehörigen Spieler aus der Liste der noch freien Spieler gestrichen und mit Schritt 2.1 fortgesetzt.

2.5 Dieser Schritt wird durchgeführt, sobald nur noch zwei Losungen zu besetzen sind. Ist die obere Losung von 3v3, dann werden die beiden höchstgerateten noch freien Spieler für diese Losung ausgewählt. Ist sie ein 4v4, dann werden stattdessen die vier höchstgerateten noch freien Spieler für diese Losung ausgewählt. Danach werden die restlichen Spieler für die obere Losung per Brute Force ermittelt (maximal 495 Möglichkeiten, für jede dieser Möglichkeiten müssen allerdings die obere und die untere Losung jeweils noch ausgewertet werden).

3. Die Losungsprofile werden verglichen und das optimale Profil zusammen mit den für dieses Profil optimalen Losungen ausgegeben.


So viel zum Grundkonzept. Was du jetzt noch brauchst, sind die Details zu den einzelnen Schritten sowie die Auswertungsfunktion, mit der aus gegebenen vier, sechs oder acht Spielern die fairste Losung zusammengebaut sowie dieser Losung ein Wert zugeordnet wird, um sie mit anderen Losungen vergleichn zu können.

Ich werde zuerst die einzelnen Schritte genauer erklären.

Inhaltsverzeichnis

Bestimmung der möglichen Losungsprofile

INPUT: die Anzahl der Spieler

OUTPUT: die Anzahl m der Losungsprofile die Anzahl n der Losungen (ist für eine gegebene Spieleranzahl unabhängig vom Losungsprofil) die Codierung der Losungsprofile (2D-Array der Dimensionen m und n)

Zur Codierung: Ich benutze eine 4 als Zeichen für ein 2v2, eine 6 als Zeichen für ein 3v3 und eine 8 als Zeichen für ein 4v4. Diese Zeichen werden hintereinandergereiht. Für eine gegebene Spieleranzahl unterscheiden sich die Profile nur in der Reihenfolge der einzelnen Losungen.

-> n wird mit 0 initialisiert.

-> Schleife: Solange die Anzahl der Spieler größer als 32 ist, wird n jeweils um eins erhöht, eine zusätzliche 8 vorgemerkt, die an die erste Stelle jeder Profil-Codierung geschrieben wird, und die Anzahl der Spieler um 8 reduziert.

-> Die möglichen Losungen werden nach folgender Tabelle gespeichert:

Spielerzahl: m; n; Codierungen. 04: 1; 1; 4. 06: 1; 1; 6. 08: 1; 1; 8. 10: 2; 2; 64, 46. 12: 1; 1; 66. 14: 2; 2; 68, 86. 16: 1; 2; 88, 88. 18: 1; 3; 666. 20: 3; 3; 866, 686, 668. 22: 3; 3; 886, 868, 688. 24: 1; 3; 888. 26: 4; 4; 8666, 6866, 6686, 6668. 28: 6; 4; 8866, 8686, 8668, 6886, 6868, 6688. 30: 4, 4; 8886, 8868, 8688, 6888. 32: 1; 4; 8888.

Dabei muss natürlich die vorangegangene Schleife mit eingebaut werden, bei der evtl. n erhöht und 8er-Losungen vorangestellt wurden.


Vorgehen für jedes Losungsprofil

Abfrage der noch zu besetzenden Losungen und ggf. Sprung

Hierzu gibt es eigentlich nichts weiter zu sagen, außer dass die Anzahl er noch offenen (f.h. zu besetzenden) Losungen sinnvollerweise irgendwo gespeichert werden sollte.


Auswahl der 2 bzw. 4 stärksten verbleibenden Spieler

Keine Erläuterung nötig, stattdessen eine Bemerkung: Hier liegt natürlich eine gewisse Heuristik vor. Es ist ja nicht gesagt, dass diese Spieler optimalerweise zusammen in eine Losung kommen sollten. Es handelt sich hier um ein Zugeständnis, zum einen an die Tradition, im Masters einigermaßen ähnlich geratete Spieler zusammen zu losen, und andererseits an die schiere Menge an Berechnungen, die nötig wären, um alle Möglichkeiten zu berücksichtigen. Mit Schritt 2.4 hat man aber eine gewisse Chance, diese Heuristik zu kompensieren.


Auswahl der übrigen vier Spieler

INPUT: die Größe der aktuellen Losung die Ratings der zwei bzw. vier in Schritt 2.2 festgesegten Spieler (1D-Array) die Liste der nächsthöchsten 12 noch freien Spieler (1D-Array)

OUTPUT: die Indizes der 6 bzw. 8 Spieler, aus denen sich die aktuelle Losung zusammensetzen soll

-> Es werden aus den 12 Spielern vier ausgewählt und die entstehende Losung ausgewertet (zur Auswertung schreibe ich, wie gesagt, später mehr). Dies muss für jede mögliche Wahl vierer Spieler getan werden. Das ergibt 495 Möglichkeiten (4 aus 12). Die optimale Auswahl wird gespeichert.

-> Die gelosten Spieler werden markiert, sie sind nicht mehr frei.


Test auf Verbesserung durch Ersetzen

INPUT: die Losung aus Schritt 2.3 (1D-Array) die Liste der übrigen freien Spieler mit ihren Ratings (1D-Array)

OUTPUT: die (möglicherweise veränderte) Losung (1D-Array) die (möglicherweise veränderte) Liste der übrigen freien Spieler mit ihren Ratings (1D-Array)

-> Es wird das optimale Rating eines (hypothetischen) Spielers ermittelt, den man mit dem stärksten Spieler des stärkeren Teams der Losung austauschen müsste, damit die Losung perfekt wird, d.h., dass beide Teams gleich stark sind. Dazu zieht man die addierten Ratings der übrigen drei Spieler des stärkeren Teams von den addierten Ratings aller Spieler des schwächeren Teams ab.

-> Wahrscheinlich gibt es keinen Spieler, der genau dieses hypothetische Rating hat. Wenn es aber unter den tatsächlich vorhandenen noch freien Spielern einen optimalen Tauschkandidat gibt, dann ist es entweder der Spieler, der gerade als nächster über diesem Rating liegt, oder der, der gerade als nächster unter diesem Rating liegt. Nur diese beiden Spieler müssen getestet werden. Sie werden also jeweils gedanklich in das stärkere Team "eingewechselt", und es wird geschaut, ob sich die Losung dadurch verbessert.

-> Die beiden vorangegangenen Punkte werden auch für die anderen Spieler des stärkeren Teams durchgeführt und die Ergebnisse verglichen: Der Austausch, bei dem sich die stärkste Verbesserung ergibt, wird durchgeführt. Ist keine Verbesserung möglich, wird natürlich nicht getauscht.

-> Wurde ein Tausch durchgeführt, dann wird Schritt 2.4 mit der nun veränderten Losung neu gestartet. Insgesamt wird Schritt 2.4 maximal fünfmal gestartet (die Anzahl ist willkürlich gewählt und kann gerne diskutiert werden).

-> Die fertige Losung wird gespeichert und die Liste der übrigen freien Spieler aktualisiert.


Besetzung der letzten beiden Losungen

Hier muss etwas mehr Aufwand betrieben werden, da man nicht einfach die vorletzte Losung optimieren und dann hoffen kann, dass die letzte auch noch einigermaßen hinhaut. Das würde sonst wahrscheinlich zu Unmut im Keller führen Augenzwinkern

INPUT: das Losungsprofil, genauer: die Größen der beiden letzten Losungen die Liste der übrigen freien Spieler mit ihren Ratings (1D-Array)

OUTPUT die vorletzte Losung (1D-Array) die letzte Losung (1D-Array)


-> Ist die vorletzte Losung ein 3v3, so werden die beiden höchstgerateten noch freien Spieler für diese Losung ausgewählt. Ist sie ein 4v4, dann werden stattdessen die vier höchstgerateten noch freien Spieler für die vorletzte Losung ausgewählt. Im Fall eines 2v2 ist keine Vorauswahl nötig.

-> Aus den übrigen (maximal 12) Spielern werden jetzt wieder alle möglichen Kombinationen aus 4er-Gruppen gebildet (maximal 495 Möglichkeiten) und die jeweils entstehenden Losungspaare (vorletzte und letzte Losung) ausgewertet. Das optimale Losungspaar wird gespeichert. Damit ist das aktuelle Losungsprofil ausgewertet.

Bestimmung des optimalen Losungsprofils

Der Vergleich der Losungsprofile besteht lediglich darin, dass die optimalen Losungen (die ja schon bekannt sind) mit den optimalen Losungen der anderen Losungsprofile verglichen werden.


Ein paar Themen hatte ich gestern ja noch ausgespart, hier jetzt der Nachschlag. Zum einen fehlten noch die Vergleichgsmöglichkeiten für die verschiedenen Losungen bzw. Losungspaare/-profile, zum anderen eine Funktion, die eine einzelne Losung "innerlich" optimiert, d.h., die aus 4 bzw. 6 bzw. 8 Spielern zwei möglichst faire Teams macht. Fangen wir mal mit Letzterem an, dann haben wir's hinter uns.

A. Optimierungsalgorithmus für Einzellosung

INPUT: eine nach Ratings sortierte Liste von Spielern (4, 6 oder mit ihren Ratings (1D-Array)

OUTPUT: ein optimales Team (das andere Team ergibt sich zwangsläufig, kann aber natürlich auch mit ausgegeben werden) (1D-Array) evtl. eine Bewertungszahl der Losung, die angibt, wie "gut" sie ist (wird erst in Teil B behandelt und vorerst ausgespart)


[1] bezeichnet das Rating von Spieler 1, [2] das Rating von Spieler 2 usw.

Die Spieler sollen auf zwei Teams verteilt werden. Für diese Verteilung ist es egal, welches Team dann links und welches rechts in der Masterszone steht. Aber natürlich wollen wir spiegelsymmetrische Verteilungen nicht beide berechnen. Deshalb wird Spieler 1 (also der mit dem höchsten Rating) pauschal in das erste Team gesteckt. Jetzt kommt es nur noch darauf an, mit welchen Spielern er zusammengesteckt wird, und dadurch ist die Verteilung auch schon eindeutig bestimmt.


Dafür brauchen wir eine Bewertungsfunktion, ein Maß für Fairness. Natürlich sollen die Teams möglichst ebenbürtig sein, deshalb wird hier einfach die minimale Differenz zwischen den Teams gesucht. WICHTIG: Es kommt hier sehr darauf an, dass alles schnell geht. Es wird deshalb immer nur das erste Team betrachtet und dessen einzelne Ratings aufaddiert. Zusätzlich wird einmalig die Summe aller Ratings in der aktuellen Losung bestimmt. Die Differenz aus Gesamtsumme und Summe des ersten Teams ergibt natürlich die Summe der Ratings des zweiten Teams. Die Gesamtsumme lässt sich für alle Verteilungen wiederverwenden, und wir benutzen die Zielfunktion Betrag( [Summe Team 1] x 2 - [Gesamtsumme] ), die nichts anderes ist als die Differenz zwischen den beiden Teams. Die Multiplikation sollte, falls irgendwie möglich, per Linksshift geschehen. Falls sich das in php nicht realisieren lässt, ist es wahrscheinlich schneller, die Summe von Team 1 zu sich selbst zu addieren, statt sie mit 2 zu multiplizieren. Je kleiner der Wert der Zielfunktion, desto fairer sind die Teams.


So weit, so gut. Nehmen wir den einfachsten Fall (4 Spieler). Es gibt drei Möglichkeiten, mit wem Spieler 1 zusammen spielen kann: 12 (Spieler 1 spielt mit Spieler 2 im Team), 13 (Spieler 1 spielt mit Spieler 3 im Team,; ich behalte diese Notation ab jetzt bei) und 14. Betrachten wir die Verteilung 13, so stellen wir fest, dass Spieler 1 mindestens so stark ist wie Spieler 2 und Spieler 3 mindestens so stark wie Spieler 4. Die Differenz beider Teams ist also [1]+[3]-[2]-[4] = ([1]-[2]) + ([3]-[4]) und damit die Summe zweier positiver Zahlen (naja, genaugenommen könnten die Zahlen auch Null werden, wenn mehrere Spieler das gleiche Rating haben, aber negativ werden sie nie). Vertauscht man Spieler 3 und 4, so erhält man also eine Teamverteilung, die mindestens genau so gut ist (haben Spieler 3 und 4 unterschiedliche Ratings, ist sie sogar besser).

Kurz gesagt, die Verteilung 14 ist immer die beste. Die anderen Verteilungen sind allerhöchstens genauso gut, und das auch nur dann, wenn mehrere Spieler das gleiche Rating haben. Wir brauchen also nur die Verteilung 14 zu betrachten, und die ist dann natürlich auch optimal. Das war dir wahrscheinlich eh schon klar, aber beim 3v3 und 4v4 kann man analog vorgehen und so die Rechenzeit deutlich verkürzen.


Bei 6 Spielern gibt es 10 Möglichkeiten (2 aus 5), Teams zu bilden. Wendet man die obige Überlegung an, bleiben folgende Verteilungen übrig: 126, 136, 145, 146, 156. Diese fünf Verteilungen werden verglichen, und fertig


Bei 8 Spielern gibt es bereits 35 Möglichkeiten (3 aus 7); analog zu oben kann man das auf 21 reduzieren. Das ist immer noch Einiges, aber mit etwas zusätzlichen Infos lässt sich das Ganze noch etwas herunterkochen.

Wir definieren folgende drei Werte: a = [1]+[8]-[4]-[5] b = [1]+[4]-[2]-[3] c = [5]+[8]-[6]-[7] Je nachdem, welche Werte a, b und c annehmen, fallen wieder Verteilungen weg. Beispiel: Die Verteilung 1238 kann nicht von vornherein ausgeschlossen werden, da Spieler 8 ja sehr niedrig geratet sein könnte. Ist a>=0, so sind Spieler 1 und 8 zusammen aber mindestens so stark wie Spieler 4 und 5 zusammen, und natürlich sind auch Spieler 2 und 3 zusammen mindestens so stark wie Spieler 6 und 7. Man hat also für die Differenz der Teams [1]+[2]+[3]+[8]-[4]-[5]-[6]-[7] = a + ([2]+[3]-[6]-[7]) wieder eine Summe zweier nichtnegativer Zahlen, kann Spieler 2 und 3 mit den Spielern 6 und 7 tauschen und erhält eine mindestens so gute Verteilung (sogar eine bessere, wenn die Spieler unterschiedliche Ratings besitzen).

Die Verteilungen 1278 und 1368 sind immer zu testen, sie fallen in keinem Fall heraus.

Die übrigen, zu testenden Verteilungen sind je nach Fall:

a>=0, b>=0, c>=0: 1367, 1378, 1456, 1467, 1468, 1478, 1567, 1568, 1578, 1678.

a>=0, b>=0, c<0: 1348, 1358, 1378, 1458, 1468, 1478, 1567, 1568, 1578, 1678.

a>=0, b<0, c>=0: 1367, 1378, 1456, 1457, 1458.

a>=0, b<0, c<0: 1348, 1358, 1378, 1456, 1457, 1467, 1567.

a<0, b>=0, c>=0: 1267, 1268, 1367, 1467, 1468, 1567, 1568.

a<0, b>=0, c<0: 1238, 1248, 1258, 1268, 1348, 1358, 1458, 1468, 1567, 1568.

a<0, b<0, c>=0: 1267, 1268, 1367, 1457, 1458.

a<0, b<0, c<0: 1238, 1248, 1258, 1268, 1348, 1358, 1457, 1458, 1467, 1567.


Im ungünstigsten Fall sind das 12 Verteilungen, die man vergleichen muss. Ich bin mir zwar ziemlich sicher, dass die einzelnen Verteilungen stimmen und habe auch nochmal nach Tippfehlern gesucht, trotzdem wäre es evtl. eine gute Idee, die Verteilungen mal zu überprüfen, z.B. durch Gegenüberstellung mit einem BruteForce-Algorithmus, der einfach alle 35 Verteilungen checkt. (beachte, dass es mehr als eine optimale Verteilung geben kann; lexikographische Ordnung hilft aber)

Die Vorzeichen von a, b und c lassen sich z.B. zusammen in einem Byte speichern (als Bits codiert), wodurch die acht Fälle dann den Zahlen von 0 bis 7 entsprechen.

Damit haben wir den unübersichtlichen Teil auch schon geschafft Zusammenfassend nochmal die einzelnen Schritte:

-> In Abhängigkeit von der Größe der Losung werden die relevanten Verteilungen bestimmt (und ggf. vorher die Indikatoren a, b und c).

-> Der Wert der Zielfunktion Betrag( [Summe Team 1] x 2 - [Gesamtsumme] ) wird für jede relevante Verteilung berechnet und in einer Variablen gespeichert, sofern es sich entweder um die erste Verteilung handelt oder der Zielfunktionswert besser (d.h. niedriger) ist als der aktuelle Wert der Variablen. Es muss ggf. dann natürlich auch die Verteilung selbst gespeichert werden.

-> Nachdem alle relevanten Verteilungen geprüft wurden, wird die optimale Verteilung zurückgegeben, evtl. auch der Zielfunktionswert, falls er für den Vergleich mit anderen Losungen herangezogen werden soll.

B.1 Bewertungsfunktionen für Einzellosungen

INPUT: die optimale Verteilung zu einer bestimmten Losung (1D-Array)

OUTPUT: ein Zahlenwert als Maß für die "Güte" der Losung

Oft ist es nötig, zwei Losungen miteinander zu vergleichen. Das tut man, indem man deren jeweils optimale Verteilungen vergleicht. Dazu braucht man ein Kriterium. Natürlich kann man wieder die oben verwendete Zielfunktion benutzen, zusätzlich kann aber auch die Streuung der Spieler mit einfließen, d.h. die Differenz zwischen höchstem und niedrigstem Rating innerhalb der Losung. Dazu braucht man dann eine Gewichtung. Aus den einzelnen Zielfunktionen

F1 = Betrag( [Summe Team 1] x 2 - [Gesamtsumme] ) und F2 = [höchstes Rating] - [niedrigstes Rating]

kann man dann eine neue Funktion zusammenbasteln:

F = [alpha] * (F1)^2 + [beta] * (F2)^2

mit positiven, ganzzahligen Gewichtungsfaktoren [alpha] und [beta]. Es ist von Vorteil, immer ganzzahlig zu rechnen. Möchte man zum Beispiel F1 mit 1 gewichten und F2 mit 3/8, so gewichtet man stattdessen lieber F1 mit 8 und F2 mit 3, was aufs Gleiche rauskommt, aber nur ganze Zahlen verwendet.

Die Quadrate hinter F1 und F2 sind nur eine Empfehlung, sie fangen besonders hohe Werte von F1 oder F2 ab. Man kann die Quadrate auch weglassen (nicht empfohlen).

Die konkreten Werte für [alpha] und [beta] sind natürlich Geschmackssache. Es ist klar, dass [alpha] deutlich größer sein sollte als [beta], für den Anfang würde ich z.B.

[alpha] = 10 und [beta] = 1

empfehlen. Aber evtl. lässt sich der Algorithmus ja so schreiben, dass [alpha] und [beta] nachträglich noch verändert werden können bzw. dem Algorithmus beim Aufruf als Argumente übergeben werden (mit bestimmten Default-Werten).

B.2 Bewertungsfunktion für Losungspaare/-profile

INPUT: die Werte der Bewertungsfunktion für die optimalen Losungen eines bestimmten Losungspaares/-profils (1D-Array)

OUTPUT: ein Zahlenwert als Maß für die "Güte" des Losungspaares/-profils

Wesentlich seltener als der Vergleich zweier Losungen ist der Vergleich zweier Losungsprofile (bzw. Losungspaare, aber das kann genauso behandelt werden, da Losungsprofile ja auch Losungspaare als Spezialfall mit einschließen, z.B. bei 14 Spielern). Für jedes Losungsprofil müssen dazu die optimalen Losungen und Verteilung bekannt sein. Jeder Losung L1, L2, ... lässt sich wie oben erklärt ein Funktionswert F zuordnen. Die Funktionswerte bezeichne ich mit [L1], [L2], ...

Nun gibt es auch hier mehrere Möglichkeiten:

(I) Einfache Addition: F(Profil) = [L1] + [L2] + ... Diese Version hat den Vorteil, dass sie schnell zu berechnen ist. Das ist allerdings nicht so wichtig, da Losungsprofile nur selten vergleichen werden. Der Nachteil ist, dass z.B. ein Losungsprofil mit drei sehr fairen und einer absolut unfairen Losung den Vorzug bekäme vor einem anderen Losungsprofil mit vier fairen Losungen.

(II) Maximum: F(Profil) = max ([L1], [L2], ...) Diese Version ist sehr fair, da die "schlechteste" Losung als Maß genommen wird. Sehr schnell ist sie auch noch. Leider hat auch sie einen Nachteil, sie vernichtet Informationen, und schon ein winziger Unterschied in der schlechtesten Losung wird stärker bewertet als noch so heftige Unterschiede in den anderen Losungen, die werden nämlich einfach ignoriert (jedenfalls so lange, bis eine dieser anderen Losungen die schlechteste wird).

(III) Summe gleicher Potenzen: F(Profil) = [L1]^k + [L2]^k + ... mit einer positiven natürlichen Zahl k. Diese Version stellt sozusagen einen Kompromiss dar. Für k=1 erhält man gerade Version (I), für k gegen unendlich nähert man sich, bis auf einen Faktor, Version (II).

Ich empfehle Version (III) mit k=4, was bereits relativ nahe an Version (II) kommt, da sich für noch höheres k der Funktionswert nicht mehr wesentlich ändert (der Aufwand dagegen schon ^^). Allerdings (du ahnst, was kommt) gibt es auch bei dieser Version einen Nachteil, die entstehenden Zahlen sind nämlich sehr groß (32 Bit reichen evtl. nicht mehr), und aufgrund der numerischen Auslöschung kommen Gleitkommazahlen wahrscheinlich nicht in Frage (Double Precision könnte funktionieren). Oder wird das ganze eh eine 64bit-Anwendung? Notfalls müsste man sich irgendwie eine 64bit-Integerzahl zusammenbasteln...