Autor Beitrag
Horst_H
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1653
Erhaltene Danke: 243

WIN10,PuppyLinux
FreePascal,Lazarus
BeitragVerfasst: So 11.10.09 13:28 
Hallo,

es geht um RoboRally Programmierwettbewerb
Man hat 8 Karten aus denen man 5 auswählen kann.
=> Binomialkoeffizient
5 aus 8 => 8 über 5 => 8 über(8-5) => 8 über 3 = (8*7*6)/(1*2*3)= 56
Bei diesen 5 ist die Reihenfolge wichtig.Also 5!=120 Möglichkeiten
Insgesamt 56*120= 6720 Möglichkeiten.

Nun gibt nur 7 verschiedene Karten.Das heisst, eine Karte ist mindestens doppelt eher 2 Doppelte.
Momantan erstelle ich alle Kombinationen 5 aus 8, sortiere diese und entferne anschliessend Mehrfache ( bleiben 41 bei einem Pärchen )
Nun zu jeder Kombination alle Permutationen, sortieren, mehrfache entfernen.( Blöcke zu 120 , wenn ein Paar in der Kombination bleiben 60 = 5!/2! übrig )
{ Nun alle Permutatin zusammenfassen und nochmals sortieren, aber das ist einen andere Sache, da 60-er Blöcke ja schon immer sortiert sind... }

Es bleibt das Problem, der Permutation mit einer/mehrere Merhrfache drin.
Hier Beispiel 112
Ich möchte nicht alle Permutationen einer 5er-Kombination erzeugen, sortieren und dann Doppelte entfernen, da ja auch noch mehr Paare, Tripel.. auftreten können.

Gruß Horst
Thorsten83
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 191
Erhaltene Danke: 1



BeitragVerfasst: So 11.10.09 22:56 
Öhm was spricht dagegen, einen Kartentyp doppelt zu benutzen?
Horst_H Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1653
Erhaltene Danke: 243

WIN10,PuppyLinux
FreePascal,Lazarus
BeitragVerfasst: Mo 12.10.09 00:40 
Hallo,

es spricht nichts dagegen, aber ich will nicht doppelt testen.
Wenn man die "1" in "1234" hineinpermutiert :-) entstehen statt 5! =120 nur 60 eindeutige Möglichkeiten, "11" in "123" bleiben von 120 nur noch 5!/3! = 20 eindeutige Möglichkeiten also 100 Tests wären unnötig.
Und da die Tests, also das Verschieben des Robots auf dem Brett wesentlich mehr Zeit kostet, hoffte ich auf eine clevere Lösung.
Bis jetzt habe ich nur festgestellt, das es einfacher ist, stur alle 6720 Kombinationen zu erzeugen , diese zu sortieren, um dann die Mehrfachen verschwinden zu lassen. Es ist sogar minimal schneller.
Das es nur eine beschränkte Anzahl ist, wo mindestens eine Karte doppelt ist, habe ich jetzt ein konstantes Feld mit den schon aufsteigend sortierten 3720 Möglichkeiten erzeugt.
Das Sortieren dauert doppelt solange wie das Erzeugen der Permutationen.

Gruß Horst
Apuch
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 27

Kubuntu 9.04, Win 2000
Delphi 7
BeitragVerfasst: Mo 12.10.09 01:39 
Morgen,

ganz naive Idee: nehm eine HashMap (Eine Art assoziatives Array, die Schlüssel sind aber nicht zwangsweise Strings). Den Schlüssel baut man nun so, das für gleiche Kombinationen der Gleiche Schlüssel entsteht. Für dein Problem kann man ja sowas ansetzen:
ausblenden Quelltext
1:
schluessel := 9^0 * Anzahl_Karten_Typ1 + 9^1 * Anzahl_Karten_Typ2 + 9^2 * Anzahl_Karten_Typ3 + ...					

Kann mich auch verrechnet haben, ich komme so aber auf kanpp 5 Millionen verschiedene Schlüssel.. passt also in ein int. Im Ablauf iteriertst du über deine Kombinationen und fügst sie in die Hashmap ein. Bei einer Kollision (gleiche Schlüssel) wird der letzte Wert überschrieben. Nach der ganzen Aktion iterierst du über alle Einträge der Hashmap und hast nur einmalige Kombinationen.

Unter Python ist das eine der schnellsten Umsetzungen. Nur etwas schneller ist die Umsetzung mit den sog. Sets (genau das selbe nur ohne expliziten Schlüssel). Auch C++ mit der STL müsste damit weit vorne liegen. Unter Delphi mangelts anscheinend leider an den Hashamps. Bei der Delphi Container Library findest du wohl was passendes. Für Wettbewerbe kommen aber kaum externe Bibliotheken in Frage. Vielleicht findest du ja so zumindest einen Anreiz.

Eine direkte Lösung für dein Problem kenne ich jetzt so nicht. Über eine Permutation zu iterieren ist schon schwierig genug, wenn man nun noch auf die Wiederhohlungen achten will (5 aus 8 aus 7), wirds echt kompliziert :? Hoffe, konnt dir trotzdem helfen.

MfG
Horst_H Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1653
Erhaltene Danke: 243

WIN10,PuppyLinux
FreePascal,Lazarus
BeitragVerfasst: Mo 12.10.09 08:47 
Hallo,

ein gutes Tipp mit den set's !
Wie ein Brett vorm Kopf . Set's gehen nur bis 256 Werten, aber TBits sind da etwas freier.
Ich habe eine Kombination in einem Word gespeichert.
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
1..8 => 0..7
Die "größte" Kombination: 8/7/6/5/4 ist dann 7/6/5/4/3 =32099
wenn es nur 7 Karten gibt 6/6/5/4/3 = 29027 => 3829 Byte, aber 4096  sind wohl auch zu ertragen ;-)
//Eintüten
j := 0;
For i := 4 downto 0 do
  j := j*8+Perm[i];//j shl 3+ 
PermFeld[permCount]:= j;
...
//Auspacken
j:= PermFeld[permpos];
For i := 0 to 4 do
  Perm:= j AND 7;
  j := j div 8;//shr 3
  end;

Jetzt kann ich ja testen, wie schnell ich alle Werte wieder auzs TBits auslesen kann, da nur weniger als 10% genutzt werden.
Das erinnert mich an ein Problem von BenBe, bei der suche nach dem nächsten Eintrag, der das irgendwie in drei Ebenen aufgeteilt hatte, aber dise Felder waren auch dünner besetzt.

Ich schau mal

Gruß Horst
Thorsten83
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 191
Erhaltene Danke: 1



BeitragVerfasst: Mo 12.10.09 09:39 
Hey,

mit deiner Lösung erzeugst du aber möglicherweise haufenweise Permutationen die total schwachsinnig sind, weil z.B. der Roboter nach dem ersten Zug in ein Loch gefallen ist, oder?

Ich hätte eher sowas ausprobiert:

ausblenden Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
testeZuege(Verfuegbare Züge)
wenn (roboter tot)
       // breche ab
sonst wenn #Verfügbare Zuge = 3
      // Berechne heuristischen Wert der erreichten Stellung, wenn > max. bisher gefundener Wert:
      // Speichere Permutation, die hierher geführt hat, und zugehörigen Wert
sonst
       für jeden verfügbaren Zug
              hänge diesen Zug an aktuelle Lösung an, entferne ihn aus "Verfügbare Züge"
              rufe dich rekursiv auf
              hänge den Zug wieder ein
Horst_H Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1653
Erhaltene Danke: 243

WIN10,PuppyLinux
FreePascal,Lazarus
BeitragVerfasst: Mo 12.10.09 12:19 
Hallo,

ich habe das falsch angefasst:
die 6720 Permutationen zu erzeugen geht ja eigentlich rasend schnell. (

Durch das Sortieren entsteht soetwas, was ich jetzt als Konstante gespeichert ( ~13.5 KB ) habe:
01234,01235,01236,01237,01243,01245,01246,01247....
Dadurch kann ich bei einem Abbruch einfach die Permutation suchen, die sich als erste in der Abbruchstelle unterscheidet.
Ausserdem brauche ich den Robot nicht bis zur Ausgangsposition zurückbewegen, da der vorherige Weg ähnlich war.
Ohne Berücksichtigung von Doppelten sind es immer 4-er Gruppen, die die ersten 4 Schritte gemein haben und 4x5 = 20 er Gruppen, die die ersten 3 Schritte gemein haben etc. .
Das ist doch tolle daran, wenn man es denn mit brutaler Gewalt probieren will.
Damit könnte man bei einem Abbruch sofort die nächste Stelle in dem Feld gesammelter Permutationen berechnen. Wenn ich die Doppelten vorher rausschmeisse, geht das nicht mehr, mhhhh , könnte also ein Fehler sein, denn gleiche Nachbarn zu bestimmen ist wesentlich schneller als eine Suche, wo es denn nun weiter geht.

Irgendwie will ich ja noch mittels A-Stern/ A* den Weg zum Ziel finden und es soll ja dann die Kombination an Karten genommen werden, die den Abstand zum Ziel möglichst klein macht. Bei den Förderbändern und Schieber ist das ja nicht so simpel A* richtig zu implementieren, besser gesagt, ich lass es ;-) ich nehme nur die Wände als Hindernis, wenn ich in ein Loch/ oder Schrottpresse treffen wird eben der Wert für dieses Feld geändert, man kann ja auch darüber weggehen.
Wahrscheinlich ist es einfacher im ersten Durchgang A* für das komplette Feld zu berechnen und abzuspeichern, statt es bei jedem Durchgang zu berechnen.

Gruß Horst
Thorsten83
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 191
Erhaltene Danke: 1



BeitragVerfasst: Mo 12.10.09 16:50 
Warum sind eigentlich alle immer so scharf auf den A*? :D
Der setzt aber auch eine Metrik voraus, oder?


Zum Spiel: Ich habe das richtig verstanden dass man vor einer Runde 8 Karten bekommt und antworten muss, welche 5 davon man in welcher Reihenfolge spielen will, oder?
Und eine Interaktion mit dem Gegner findet nicht statt?

Ich möchte mal behaupten dass man die 40.320 Kombinationen in ein paar Milisekunden durchtesten kann...
Horst_H Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1653
Erhaltene Danke: 243

WIN10,PuppyLinux
FreePascal,Lazarus
BeitragVerfasst: Mo 12.10.09 18:13 
Hallo,

ja irgendwie muss man ja den Weg zum Ziel finden, und der ist nicht immer geradeaus. Einfach nur die Manhattan oder Mannheim Distanz zu minimieren lässt einen hinter einer Mauer auf das Ziel zusteuern.

Du würdest 8! testen, also auch das acht mal die selbe Karte kommt?
Ich habe jetzt mal die Referenz gestoppt und die braucht 4,8 ms nur zum laden und 5 karten zurückschreiben.
Mein freepascal Elaborat braucht mit einlesen des Boards/Karten und rekursiven Erzeugen der Permutation und zurückschreiben von irgendwelkchen 5 Karten 4,3 ms.
Äusserst merkwürdig.

Der Ablauf ist ja derart , das die KI jedesmal von dem Auswertungsprogramm neu gestartet wird und in den Dateien robot die Position und Richtung vorfindet und in cards.txt die besagten acht Karten, aus den man fünf ( geschickt ) auswählen muss.Deshalb gab es schon Einwä¤nde von Java-Nutzern...
Dummerweise braucht dieses Auswertungsprogramm etwa 55 ms um die 5 Schritte zu testen und die neuen Karten und Roboterstellung auszugeben. ( C++ mit -O3 und hast Du nicht gesehen kompiliert, müßte doch schnell sein)
Bei einem Zeitlimit von 6 Sekunden und geratenen 24 Zügen bleiben noch 0,19 Sekunden für eigene Berechnungen.
Mit dem Auswertungsprogramm sind das 3.5 Züge !! Das ist ja gar nichst.

Anbei mein 5aus8.pas irgendwie habe ich einen Fehler bei der Version mit vorherigen aussieben eingebaut( es hat mal funktioniert :-( zuviel hin und her kopiert ) .
Aber etwa 1,4 ms mit sortieren (das braucht allein schon 0.9 ms )

Gruß Horst

Eine andere Version von 5aus8, die eben funktioniert, während die andere genau einmal tut was sie soll.

Edit 3.te Version etwas anschaulicher ?
Erheblich schneller. in freepascal als Release kompiliert 325 Takte pro Permutation, heute morgen waren es noch 770 ...
Mit Turbo-Delphi sind es 251 Takte. :-)
Einloggen, um Attachments anzusehen!


Zuletzt bearbeitet von Horst_H am Do 22.10.09 22:34, insgesamt 2-mal bearbeitet
Thorsten83
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 191
Erhaltene Danke: 1



BeitragVerfasst: Mo 12.10.09 19:23 
Hey,

ich würde's zumindest erstmal ausprobieren, gucken wie schnell das ist, und mir dann Gedanken machen,
wäre ja auch peinlich, wenn eine Karte doppelt vorkommt, auch gut 2x benutzt werden kann, man das aber schon gleich verbietet.

Als Beispiel:
ausblenden Quelltext
1:
2:
3:
x
---
o

Wobei "o" die aktuelle Position sein soll, "-" eine Mauer und "x" das Ziel :)
Dann bräuchtest du ja 2 Drehungen...

Hab vorhin übrigens nicht richtig nachgedacht, testen müsste man ja nur die 5-elementigen Anordnungen, also schlimmstensfalls 6720 (?)

Ein weitere Problem ist ja auch, dass du nicht weißt, welches Feld für dich vorteilhaft ist, weil du nicht weißt, welche Karten du beim nächsten mal bekommen wirst, oder?

Wenn man die Stellungsbewertung schnell hinkriegt sehe ich kein Problem...
Horst_H Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1653
Erhaltene Danke: 243

WIN10,PuppyLinux
FreePascal,Lazarus
BeitragVerfasst: Mo 12.10.09 21:51 
Hallo,

Das Spielfeld ist ja statisch. Also ein Loch bleibt ein Loch und wandert nicht.Auch die Wände bleiben stehen.
Nur nach jedem auspielen einer Karte setzen sich erst die Förderbänder in Bewegung, dann die Stössel/Pressen und anschliessend die Drehscheiben.
Ein Förderband/Stössel in Richtung Ziel ist also günstig.

Mit den fünf Karten programmiert man den Robot.
Der wendet diese also blind eine Karte nach der anderen Karte an, deshalb ist Reihenfolge wichtig.
Bei Mitspielern können die einen blockieren/abdrängen/den Weg versperren und schon passieren die merkwürdigsten Dinge...
Meine nue alte Version von 5aus8 tut es.
Jetzt werde ich nur eine natural merge Sortierung von Delphi-Laie einbauen, da ich eine sortierte Mindestblocklänge von 60/30/20 (1/2/3 Paare) habe.

Ich werde jetzt mal die Bewegung auf dem Spielbrett einbauen.

Gruß Horst
Thorsten83
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 191
Erhaltene Danke: 1



BeitragVerfasst: Di 13.10.09 13:08 
Zitat:

Aufgrund der zeitlichen Begrenzung des Wettbewerbs wurde aber auf die Interaktion mit anderen Robotern und auf einige Spielelemente verzichtet. Auf diese Art soll die zu programmierende Künstliche Intelligenz (KI), d.h. das Programm, welches den Roboter steuert, nicht zu komplex werden.


Das hatte ich so interpretiert, dass die Roboter sich nicht wegschubsen?
Horst_H Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1653
Erhaltene Danke: 243

WIN10,PuppyLinux
FreePascal,Lazarus
BeitragVerfasst: Di 13.10.09 13:51 
Hallo,

Die KI tritt alleine gegen niemanden an, das ist richtig.
Nur bei dem Originalspiel ist das anders.

Gruß Horst
Thorsten83
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 191
Erhaltene Danke: 1



BeitragVerfasst: Di 13.10.09 17:46 
Hey,

du erstellst dir jetzt alle 5-elementigen Teilmengen der Karten 1-8, und suchst dir dann diejenigen raus, die mit den zur Verfügung stehenden Karten möglich sind?

Mal ein Wort zu der Bewertung: Ich würde bevor die möglichen Züge ausprobiert werden jedem möglichen Feld eine Bewertung zuweisen, dann musst du um eine Zugfolge zu bewerten nur nach der schon erstellten Bewertung gucken, und nicht jedes mal groß rechnen.
Problem dabei: Wie weit ist ein Feld genau neben dem Zielfeld von diesem entfernt? (Die Mauerproblematik mal außen vor gelassen...)
Wenn du in die richtige Richtung guckst 1 Zug, wenn du 90° falsch gedreht stehst 2 Züge (drehen + bewegen), wenn dich das Feld nicht doppelt so weit dreht, sonst kannst du das Zielfeld von da aus gar nicht direkt erreichen. Kann man alles sehr schön über einen Graphen modellieren, aber dafür reicht es halt nicht aus, ein Feld auch nur als einen Knoten zu betrachten ;)

Was man dann noch reinnehmen kann (oder sollte?) ist, wie wahrscheinlich es ist, die notwendigen Karten zu bekommen...
Horst_H Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1653
Erhaltene Danke: 243

WIN10,PuppyLinux
FreePascal,Lazarus
BeitragVerfasst: Di 13.10.09 22:02 
Hallo,

bei brute force nimmt man eben alle Möglichkeiten und schmeisst möglichst früh unsinnige und deren Nachfolger raus. Deshalb mein Ansatz mit den sortierten Abfolgelisten/5er Gruppen.
Ich habe diese als Konstante vorliegen mit Umwandelung in eine 5er-Kartenfolge in 130 Takten ein, also rund 5e5 Takte für alle, da eine Doppelte gewiss ist.
Bei 0.1 Sekunden habe bei mir 2.3e8 Takte Zeit also ist erstmal unwesentlich.
Natürlich muss vor dem Test die Bewertung der Felder machen, zumindest die von A*, um sicher ins Ziel zu kommen , dann müsste ich aber auch immer auf einem Feld landen, was A* auf dem Weg zum Ziel schon bewertet hat.
Wenn es merkwürdig läuft könnte ich 12 Schritte und eine Drehung zwischendrin machen und nicht nur 5 oder nur Drehungen.

Die Wegsuche ist doch insofern etwas schwierig, weil ein Förderband dich dem Ziel nähert oder entfernt, Drehteller deine Richtung ändert und Du nicht nur MF-1 Karten hast, genau 5 Karten nutzen musst....

Ich bewerte erst ein Feld komplett leer nur mit den Wänden mit A* mit Einer-Schritten.
Das kann ich einmal vorab machen und dann speichern.
Dann spiele ich die Kombinationen aus und dann schaue ich weiter :-)
Ich sehe das große Problem, das durch 1er-Schritte A* falsch bewertet. Wenn ich in ein Loch trete, was ich eigentlich überspringen kann, wird A* das dahinter nie berechnen.
Man muss es dann so modifizieren, dass A* neben solchen Löchern/Schrottpressen mit einem um 2 erhöhten Wegkostenwert weiter das Ziel sucht.
Die Wahrscheinlichkeit ist gut und schön.Manche Karten kommen ja mit 3/14 = 21.4% Wahrscheinlichkeit vor, also mit (1-0.214)hoch 8 = 14.56 % Wahrscheinlichkeit !nicht vor, also mit 85.54% mindestens einmal.Das gilt für LR/RR/MF_1.MF_2 kommt mit 29% und MF_2/RU/MB mit 55% nicht vor. MF_2 ist also in 2 von 3 Kartenstapel dabei.
OK ein Stapel ohne zurücklegen, aber wer will jetzt alle Karten benutzen, um ins Ziel zu gelangen.

Gruß Horst
Thorsten83
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 191
Erhaltene Danke: 1



BeitragVerfasst: Do 15.10.09 14:24 
Joa, wenn man das sauber machen will wird's tricky...

Man könnte ja z.B. ein Feld als 4 Knoten beschreiben, die im Falle eines "normalen" Feldes so verbunden sind:


ausblenden Quelltext
1:
2:
3:
4:
5:
  o
 / \
o   o
 \ /
  o


Du kannst das Feld also z.B. auf dem linken Knoten betreten.
Ist das rechte Nachbarfeld betretbar, wird der linke Knoten mit dem linken Knoten des Nachbarfeldes verbunden.
Eine Drehung um 90° entspräche dann dem Übergang von einem Knoten zu einem anderen, der aber zu dem selben Feld gehört.
Horst_H Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1653
Erhaltene Danke: 243

WIN10,PuppyLinux
FreePascal,Lazarus
BeitragVerfasst: So 01.11.09 23:35 
Hallo,

ich habe jetzt eine Lösung umgesetzt die hoffentlich Ronny Polley in
users.informatik.uni...nd-kombinatorik.html
gemeint hat :?

ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
144:
145:
146:
147:
148:
149:
150:
151:
152:
153:
154:
155:
156:
157:
158:
159:
160:
161:
162:
163:
164:
165:
166:
167:
168:
169:
170:
171:
172:
173:
174:
175:
176:
177:
178:
179:
180:
181:
182:
183:
184:
185:
186:
187:
188:
189:
190:
191:
192:
193:
194:
195:
196:
197:
198:
199:
200:
201:
202:
203:
204:
205:
206:
207:
208:
209:
210:
211:
212:
213:
214:
215:
216:
217:
218:
219:
220:
221:
222:
223:
224:
225:
226:
227:
228:
229:
230:
231:
232:
233:
234:
235:
236:
237:
238:
239:
240:
241:
242:
243:
244:
245:
246:
247:
248:
249:
250:
251:
252:
253:
254:
255:
256:
257:
258:
259:
260:
261:
262:
263:
264:
265:
266:
267:
268:
269:
270:
271:
272:
273:
274:
275:
276:
277:
278:
279:
280:
281:
282:
283:
284:
285:
286:
287:
288:
289:
290:
291:
292:
293:
294:
295:
296:
297:
298:
299:
300:
301:
302:
303:
304:
305:
306:
307:
308:
309:
310:
311:
312:
313:
314:
315:
316:
317:
318:
319:
320:
321:
322:
323:
324:
325:
326:
327:
328:
329:
330:
331:
332:
333:
334:
335:
336:
337:
338:
339:
340:
341:
342:
343:
344:
345:
346:
347:
348:
349:
350:
351:
352:
353:
354:
355:
356:
357:
program Perm5aus8;
{$IFDEF FPC}
  {$mode Delphi}
  {$DEBUGINFO-}
  {$R- $V- $O-}
  {$Optimization ON}
  {$Optimization regvar}
  {$Optimization PEEPHOLE}
  {$Optimization CSE}
  {$Optimization ASMCSE}
{$ELSE}
  {$APPTYPE CONSOLE}
{$ENDIF}

const
  cMaxCardsOnDeck = 8;
  CMaxCardsUsed   = 5;
type
  tDeckIndex = 0..cMaxCardsOnDeck-1;
  tSequenceIndex = 0..CMaxCardsUsed-1;

  tDiffCardCount = 0..6;

  tMengenElem     = record
                      Elemcount : tDeckIndex;
                      Elem  : tDiffCardCount;
                    end;

  tMenge          = record
                      maxElem   : word;//= SizeOf(tMengenElem) -1..High(tDeckIndex);
                      RestMenge : array [low(tDiffCardCount)..High(tDiffCardCount)] of tMengenElem;
                    end;

  tRestmengen     = array [low(tSequenceIndex)..High(tSequenceIndex)+1of tMenge;

  tAuswahlStapel  = array [low(tDeckIndex)..High(tDeckIndex)] of tDiffCardCount;
  tCardSequence   = array [low(tSequenceIndex)..High(tSequenceIndex)] of tDiffCardCount;
  tPermRec        = packed record 
                      permTiefe : byte;// Stelle der Änderung gegenüber Vorgängerpermutation
                      permCardSequence :tCardSequence ;
                    end;   
  tPermArray      = array of tPermRec;

  tPropArray      = array of tDiffCardCount;
//Erzeugen aller moeglichen Ziehungen ohne Beachtung der Reihenfolge
  tCardStr = String[4];
const
  //alphabetisch geordnet, aber unwichtig
  cCardText : array[tDiffCardCount] of tCardStr =
               ('MB','MF 1','MF 2','MF 3','RL','RR','RU');
{ Karten
Titel   Anzahl   Wahrscheinlichkeit
Bewegung 3 Felder vorwärts   0.071 (1/14)
Bewegung 2 F. vorwärts     0.143     (2/14)
Bewegung 1 F. vorwärts      0.214     (3/14)
Bewegung 1 F. rückwärts   0.071
Nach links drehen (90 Grad)   0.214
Nach rechts drehen (90 Grad)   0.214
Umdrehen (180 Grad)   0.071               }

  cCardprop : array[tDiffCardCount] of integer = (1,3,2,1,3,3,1);

var
  PermCount  : integer;

 // globale Variable, um Stelle der Änderung gegenüber der
 // Vorgänger permutation festzuhalten
  mindepth   : integer;

  AusgangsMenge : tMenge;
  Restmengen: tRestmengen;
  PropArray : tPropArray;
  PermFeld  : tPermarray;

  CardSequence :tCardSequence;

function GetCPU_Time: int64;
type
  TCpu = record
            HiCpu,
            LoCpu : Dword;
         end;
var
  Cput : TCpu;
begin
  asm
  RDTSC;
  MOV Dword Ptr [CpuT.LoCpu],EAX
  MOV Dword Ptr [CpuT.HiCpu],EDX
  end;
  with Cput do
    result := int64(HiCPU) shl 32 + LoCpu;
  end;

//*****************************************************************************
function Fakultaet(i:integer): integer;
begin
  result := 1;
  while i > 1 do
    begin
    result := result * i;
    dec(i);
    end;
end;

function NueberK(n,k:byte):longint;
var
  i : longint;
begin
  {n ueber k ist = n ueber (n-k) , also kuerzere Version waehlen}
  If k > n div 2 then
    k := n-k;
  result := 1;
  If k<= n then
    For i := 1 to k do
      result := result*(n-i+1div i;{geht immer  ohne Rest }
end;

procedure CardSequenceAusgeben(const inCards:tCardSequence);
var
  i : integer;
begin
  For i := low(tCardSequence) to High(tCardSequence) do
    write(cCardText[inCards[i]],' ');
  writeln;
end;

procedure Wahrscheinlichkeiten;
var
  i,j,
  maxcnt: integer;
begin
  maxCnt := 0;
  For i := low(cCardprop) to High(cCardprop) do
    inc(maxCnt,cCardprop[i]);
  setlength(PropArray,maxcnt);
  For i := low(cCardprop) to High(cCardprop) do
    For j := 1 to cCardprop[i] do
      begin
      dec(maxcnt);
      PropArray[maxCnt] := i;
      end;
end;

procedure MengeInit(var ioMenge:tMenge);
var
  i : integer;
begin
  with ioMenge do
    begin
    maxElem := High(tDiffCardCount);
    For i := Low(tDiffCardCount) to High(tDiffCardCount) do
      with RestMenge[i] do
        begin
        ElemCount := 0;
        Elem      := i;
        end;
    end;
end;

procedure MengeAusgeben(const inMenge:tMenge);
var
  i,j : integer;
begin
  with inMenge do
    begin
    write(MaxElem:3,'..');
    For i := 0 to MaxElem-1 do
      with RestMenge[i] do
        For j := 0 to ElemCount-1 do
          write(cCardText[Elem],',');
    end;
  //letztes Komma entfernen
  writeln(#8,' ');
end;

procedure MengeAufsteigendSortieren(var Ausgangsmenge:tMenge);
var
  Startmenge : tMenge;
  i,j,k : integer;
begin
Startmenge := Ausgangsmenge;

Ausgangsmenge.maxElem := 0;
repeat
  //Suche die maximale Mehrfachheit eines Elementes
  k := 0;
  with Startmenge do
    For i := low(tDiffCardCount) to high(tDiffCardCount) do
      with Restmenge[i] do
        if k< ElemCount then
          begin
          k := ElemCount;
          j := Elem;
          end;
  //Keins mehr vorhanden. Und tschuesz
  if k = 0 then
    break;
  // Eintragen in Ausgangsmenge
  with Ausgangsmenge do
    begin
    with Restmenge[maxElem] do
      begin
      ElemCount:= k;
      Elem := j;
      end;
    inc(MaxElem);
    end;
  //Entfernen aus Startmenge
  Startmenge.Restmenge[j].ElemCount :=0;
until false;

end;

procedure ZufallsMenge(var ioMenge:Tmenge);
var
  c,
  i : integer;
begin
  MengeInit(ioMenge);
  c := length(PropArray);
  For i := Low(tDeckIndex) to High(tDeckIndex) do
    inc(ioMenge.RestMenge[PropArray[random(c)]].ElemCount);
  MengeAufsteigendSortieren(ioMenge);
end;

procedure Permute(depth:integer);
var
  i : integer;
  pMengeElem : ^tMengenElem;
begin
  i := 0;
  pMengeElem := @RestMengen[depth].RestMenge[i];
  repeat
    if  pMengeElem^.Elemcount <> 0 then begin
        //Element entnehmen
        CardSequence[depth] := pMengeElem^.Elem;
        dec(pMengeElem^.ElemCount);
        IF depth = CMaxCardsUsed-1 then begin
          
         {write(RestMengen[depth].MaxElem:3,mindepth:2,'  ');
          CardSequenceAusgeben(Cardsequence);}

          
          with PermFeld[permCount] do begin
            permTiefe := mindepth;
            permCardSequence := CardSequence;
            end;
          inc(permCount);
          mindepth := depth;
          end
        else begin
          RestMengen[depth+1]:= RestMengen[depth];
          Permute(depth+1);
          //Element wieder einfuegen
          inc(pMengeElem^.ElemCount);
          dec(minDepth);
          end;
        end;
    inc(pMengeElem);
    inc(i);
  until i >=RestMengen[depth].MaxElem;
end;

procedure  Testlauf(const inMenge:tMenge);
var
  T1,T0 : int64;
begin
  PermCount := 0;
  Restmengen[0] := inMenge;
  MengeAusgeben(inMenge);

  T0 := GetCPU_Time;
  Permute(0);
  T1 := GetCPU_Time;
  
  writeln(permCount:8,' moegliche verschiedene Anordnungen.');
  Writeln((T1-T0):0,' Cpu-Takte gesamt.');
  Writeln((T1-T0)/permCount:0:0,' Cpu-Takte pro Anordnung.');
  writeln;  
end;

const
  Runden = 2000000;
var
  T1,T0 : int64;
  i,cnt :integer;
BEGIN
randomize;
Wahrscheinlichkeiten;

cnt := 0;
T0 := GetCPU_Time;
For i := 1 to Runden do
 begin
 ZufallsMenge(Ausgangsmenge);
 inc(cnt,Ausgangsmenge.maxElem);
 end;
T1 := GetCPU_Time;
writeln(cnt/Runden:0:5,' mittlere Anzahl an verschiedenen Karten');
Writeln((T1-T0)/Runden:0:0,' Cpu-Takte pro zufaelligem Kartendeck');

i := Fakultaet(cMaxCardsUsed)*NueberK(cMaxCardsOnDeck,cMaxCardsUsed);
writeln(i,' maximale Anzahl Perms');
writeln;
setlength(PermFeld,i);

// Kartendecks mit 7,6,5,4,3,2,1 veschiedenen Karten testen
For i := High(tDiffCardCount)+1 Downto 1 do
  begin
  repeat
    ZufallsMenge(Ausgangsmenge);
  until Ausgangsmenge.Maxelem = i;
  Testlauf(Ausgangsmenge);
  end;
//Aufraeumen
setlength(PermFeld,0);
readln;
END.

{freepascal 2.4.2
4.61482 mittlere Anzahl an verschiedenen Karten
1025 Cpu-Takte pro zufaelligem Kartendeck
6720 maximale Anzahl Perms

  7..RR,RR,MB,MF 1,MF 2,MF 3,RL,RU
    3720 moegliche verschiedene Anordnungen.
250529 Cpu-Takte gesamt.
67 Cpu-Takte pro Anordnung.

  6..RR,RR,RR,MF 1,MF 2,MF 3,RL,RU
    1520 moegliche verschiedene Anordnungen.
106279 Cpu-Takte gesamt.
70 Cpu-Takte pro Anordnung.

  5..MF 1,MF 1,MF 1,RR,RR,MB,MF 3,RL
     820 moegliche verschiedene Anordnungen.
54101 Cpu-Takte gesamt.
66 Cpu-Takte pro Anordnung.

  4..RL,RL,RL,MF 1,MF 1,RR,RR,MF 3
     440 moegliche verschiedene Anordnungen.
27337 Cpu-Takte gesamt.
62 Cpu-Takte pro Anordnung.

  3..RR,RR,RR,RR,RR,MF 1,MF 1,RL
      71 moegliche verschiedene Anordnungen.
6686 Cpu-Takte gesamt.
94 Cpu-Takte pro Anordnung.

  2..RR,RR,RR,RR,RR,RR,RR,MF 1
       6 moegliche verschiedene Anordnungen.
3096 Cpu-Takte gesamt.
516 Cpu-Takte pro Anordnung.

  1..RL,RL,RL,RL,RL,RL,RL,RL
       1 moegliche verschiedene Anordnungen.
533 Cpu-Takte gesamt.
533 Cpu-Takte pro Anordnung.}


Es ist jetzt erheblich schneller und direkt in meinem Sinne aufsteigend sortiert :-)

Gruß Horst

EDIT:
Mit TurboDelphi kompiliert sind es nur 54 Takte, also fast fünf mal schneller selbst ein Kartendeck wird in 664 Takten erstellt.

EDIT2:
ich hatte noch permTiefe eingeführt, welches die Position des ersten Unterschiedes zur Vorgängeranordnung angibt.
Das bedeutet, wenn man die vorherigen Schritte alle abgespeichert hat, den neuen Weg ab der Position vor permtiefe fortsetzen kann.
Oder auch, wenn ein Spielzug bei pos 3 scheitert, alle folgenden Permutationen, die sich erst nach permtiefe>3 ändern, kann ich verwerfen.