Autor |
Beitrag |
Luzzifus
      
Beiträge: 200
Win2K
D6 Prof
|
Verfasst: Do 27.10.05 16:00
Hallo,
ich brauche einen Algorithmus der mir zu einer bestimmten Ordnungszahl die entsprechende Permutation einer Ziffernfolge ausrechnet.
Das einzige was mir bisher eingefallen ist: den rekursiven Standard-Algorithmus für Permutationen in Verbindung mit einem Zähler laufen zu lassen und dann abzubrechen wenn der Zähler meine gegebene Ordnungszahl erreicht hat.
Das sieht dann in etwa so aus:
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:
| procedure Perm(ex: Integer); var i : Byte; anz : Integer;
procedure Switch(x,y: Byte); var tmp : Byte; begin tmp := permAr[x]; permAr[x] := permAr[y]; permAr[y] := tmp; end;
procedure permint(n: Byte); var k : Byte; begin if n=0 then begin inc(anz); if anz >= ex then exit; end else for k:=n downto 1 do begin Switch(k,n); permint(n-1); if anz >= ex then exit; Switch(k,n); end; end;
begin anz := 0; for i:=1 to 9 do permAr[i] := i; permint(9); end; |
Wie man sieht, permutiere ich einen neunstellige Ziffernfolge der Ziffern 1..9. Da 9! ziemlich viel ist dauert das entsprechend lange, für eine hohe Ordnungszahl ca. 70ms @ 2GHz. Das brauch ich schneller. :>
Vielleicht gibt's auch ganz andere Möglichkeiten als diesen Permutationsalgorithmus, das Ziel ist jedenfalls eine eineindeutige (!!) Abbildung der natürlichen Zahlen im Intervall [1...9!] auf alle Permutationen der Ziffern 1..9. Sprich ich gebe dem Algorithmus eine bestimmte Zahl und bekomme eine bestimmte Permutation zurück, die bei keiner anderen Zahl auftritt (das ist wichtig, es müssen alle Permutationen erfasst werden).
Hat jemand eine Idee wie das schneller gehen könnte? (falls das überhaupt anders geht  )
so long,
luzzi.
Zuletzt bearbeitet von Luzzifus am Fr 28.10.05 10:27, insgesamt 1-mal bearbeitet
|
|
BattleFrog
      
Beiträge: 53
WIN 2000
Delphi 7 Ent.
|
Verfasst: Do 27.10.05 16:23
Dafür gibts natürlich superkomplexe Rangehensweisen, aber das erste was mir einfiel, ist:
Speichere alle möglichen Permutationen in einem Array. Das hat einen eindeutigen Index und du kannst auf alle Permutationen zugreifen ohne jedesmal neu zu rechnen. (Nur einmal beim Programmstart das Array füllen)
(Verschiebung: Mehr Speicherbedarf, Weniger CPU-Zeit)
|
|
Luzzifus 
      
Beiträge: 200
Win2K
D6 Prof
|
Verfasst: Do 27.10.05 16:30
BattleFrog hat folgendes geschrieben: | Dafür gibts natürlich superkomplexe Rangehensweisen, aber das erste was mir einfiel, ist:
Speichere alle möglichen Permutationen in einem Array. Das hat einen eindeutigen Index und du kannst auf alle Permutationen zugreifen ohne jedesmal neu zu rechnen. (Nur einmal beim Programmstart das Array füllen)
(Verschiebung: Mehr Speicherbedarf, Weniger CPU-Zeit) |
Hallo,
danke für die schnelle Idee, aber:
9! = 362880.
Für jede Permutation bräuchte ich 9 Byte (mit ein bisserl mehr Aufwand 9*5 Bit) Speicherplatz. Ein Array dieser Länge würde über 3MB Speicher belegen..
Das wäre zwar eine Alternative, gefällt mir aber fast genausowenig wie meine bisherige Lösung. Also von mir aus kannste ruhig eine dieser "superkomplexe Rangehensweisen" rauskramen.
so long,
luzzi.
|
|
Horst_H
      
Beiträge: 1654
Erhaltene Danke: 244
WIN10,PuppyLinux
FreePascal,Lazarus
|
Verfasst: Do 27.10.05 17:29
Hallo,
von Harry M. Verfasst am: Sa 15.10.05 14:37 in
www.delphi-forum.de/...r=asc&highlight=
daraus
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12:
| function BruteForce(cNb: Cardinal; Const sCh: String): String; begin Result := ''; while cNb > Length(sCh) do begin dec(cNb); Result := sCh[cNb mod Length(sCh) + 1] + Result; cNb := cNb div Length(sCh); end;
if cNb > 0 then Result := sCh[cNb] + Result; end; |
Es erzeugt einen String der nur Zeichen aus sCh enthaelt und auch so lang ist, was aber nicht immer stoert.
cNB gibt Deine gewuenschte Nummer , Ordnungszahl an.
mit sCH ='123456789' und cNb im Bereich 0.. 9!-1 erhaelst Du Deinen gesuchten String.
Gruss Horst
|
|
Luzzifus 
      
Beiträge: 200
Win2K
D6 Prof
|
Verfasst: Do 27.10.05 18:40
Der von dir genannte Algorithmus hat mehrere entscheidende Nachteile:
1. Er gibt mir immer nur maximal 6-stellige Ziffernfolgen zurück, was ich nicht beheben kann da ich nicht ganz nachvollziehen kann was der A. macht.
2. Wenn die Ordnungszahl zu klein wird kriege ich teilweise nur einen einelementigen Ergebnisstring.
3. Die Ziffern können mehrfach im Ergebnisstring vorkommen, wodurch keine Permutation entsteht. Wiegesagt brauche ich die Eigenschaften von Permutationen, sprich:
- Länge des Ergebnisstrings genau 9.
- Alle Ziffern von 1..9 kommen in jedem Ergebnis jeweils genau einmal vor.
- Ergebnis muss reproduzierbar sein (also kein nichtdeterministischer Anteil).
- Die Abbildung ist eineindeutig, sprich zu jeder Ordnungszahl gibt es genau eine Permutation und umgekehrt. Das kann ich hier zwar nicht sofort sehen aber da mit modulo gearbeitet wird gehe ich davon aus dass die Abbildung dieses Algorithmus maximal in eine Richtung eindeutig ist.
(Die Reihenfolge der Permutationen ist mir dabei allerdings egal.)
|
|
alzaimar
      
Beiträge: 2889
Erhaltene Danke: 13
W2000, XP
D6E, BDS2006A, DevExpress
|
Verfasst: Do 27.10.05 19:23
Hier, der hier geht:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19:
| Function NthPermutation (aString : String; aCount : Integer) : String; Var d : Array Of Integer; g, i, n : Integer;
Begin n := Length (aString); setlength (d, n); d[1] := 1; For i := 2 to n - 1 do d[i] := i * d[i-1]; Result := ''; for i := n-1 downto 1 do begin g := (aCount div d[i]) + 1; Result := Result + aString[g]; delete (aString, g, 1); aCount := aCount mod d[i]; End; Result := Result + aString; End; |
Herleitung siehe Beitrag von mir (ziemlich weit unten auf der Seite):
www.delphi-forum.de/...ighlight=permutation
Es gibt übrigens hier im Forum eine Suchfunktion. "Permutation" und ein bisserl Klicken hätte gereicht 
_________________ Na denn, dann. Bis dann, denn.
|
|
Luzzifus 
      
Beiträge: 200
Win2K
D6 Prof
|
Verfasst: Do 27.10.05 19:32
Danke dir herzlichst, das ist genau das was ich suche.
Du magst es nicht glauben aber ich kenne durchaus den Sinn einer Suchfunktion. Ich habe nach "Permutation" gesucht und nur seitenweise Themen gefunden die vom Titel her keinen so offensichtlichen Zusammenhang mit meinem Problem hatten, um's mal vorsichtig auszudrücken. Und dass ich dann keine Lust habe 20 oder mehr Themen komplett durchzulesen kannst du bestimmt verstehen.
Auf jedenfall danke ich dir, mein Problem ist somit gelöst.
so long,
luzzi.
|
|
alzaimar
      
Beiträge: 2889
Erhaltene Danke: 13
W2000, XP
D6E, BDS2006A, DevExpress
|
Verfasst: Do 27.10.05 19:36
Ich dachte mir, das Du suchen kannst. Aber Suchen <> Finden. Suchen tut der Compi, finden musst du selbst.
Egal, Hauptsache sie wurden geholfen. 
_________________ Na denn, dann. Bis dann, denn.
|
|
BattleFrog
      
Beiträge: 53
WIN 2000
Delphi 7 Ent.
|
Verfasst: Fr 28.10.05 08:34
Ist diese Funktion denn jetzt überhaupt schneller als deine bisherige?
|
|
Luzzifus 
      
Beiträge: 200
Win2K
D6 Prof
|
Verfasst: Fr 28.10.05 09:40
BattleFrog hat folgendes geschrieben: | Ist diese Funktion denn jetzt überhaupt schneller als deine bisherige? |
Diese Funktion hat nur eine einfache for-Schleife die in meinem Fall exakt 8 Mal durchlaufen muss (Permutation der Ziffern 1..9). Die Laufzeit ist also grob gesagt O(n), bei mir < 1ms. Mein alter Algorithmus hat alle Permutationen nacheinander erzeugt, im Worst-Case also alle 362880. Ohne weitere Ausführungen hat dieser Algorithmus eine Komplexität von O(n!) (#Permutationen = n!, mehrere Vertausch-Operationen pro Permutation). Das entspricht bei mir im Worst-Case bis zu 70ms Berechnungszeit.
Fazit: Ja, dieser Algorithmus hier ist schneller, da ich ja nur eine einzige Permutation brauche und nicht alle. 
Zuletzt bearbeitet von Luzzifus am Fr 28.10.05 10:24, insgesamt 1-mal bearbeitet
|
|
Horst_H
      
Beiträge: 1654
Erhaltene Danke: 244
WIN10,PuppyLinux
FreePascal,Lazarus
|
Verfasst: Fr 28.10.05 09:44
Hallo,
mein Beitrag oben war wohl ziemlicher Schrott  .
Nun gut.
Die function perm(ex:integer) macht ex Aufrufe von switch.
Ich habe die Funktion von alzaimer etwas umgestrickt, um diese merkwuerdigen delete's (ist ja ein move) und das unnoetige Berechnen von n! einzusparen.
Es wird eine eindeutige Kombination aus acount mod n! bestimmt.
Es werden genau n-1 Tauschungen vorgenommen.
Also muesste das hier schneller sein.
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21:
| Function NthPermutation (const aString : AnsiString; aCount : Integer) : AnsiString; Var pos, i, n : Integer; chTemp : char; Begin n := Length(aString); result := aString;
for i := n downto 2 do begin n := acount DIV i; pos := acount-n*i +1; acount := n; chTemp := result[i]; result[i] := result[Pos]; result[Pos] := chTemp;
acount := acount div i; End; End; |
Gruss Horst
P.S.:
Bei mit braucht das Programm:
NthPermutation('123456789',i) mit i =0..362879(9!-1)
org alzaimer: 2,1362164744 s
opt alzaimer: 0,3425047038 s ( ca. 1e-6 sek pro Aufruf)
EDIT oben MOD und DIV mit einem DIV und einer Multiplikation ersetzt.
Minimal schnellere Version dank DivMod.
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17:
| Function NthPermutation (const aString : AnsiString; aCount : NativeInt) : AnsiString; Var pos, i, n : NativeInt; chTemp : char; Begin result := aString; n := Length(aString); for i := n downto 2 do begin DivMod(acount,i,acount,pos); chTemp := result[i]; result[i] := result[Pos+1]; result[Pos] := chTemp; End; End; |
Zuletzt bearbeitet von Horst_H am So 01.10.17 18:16, insgesamt 2-mal bearbeitet
|
|
Luzzifus 
      
Beiträge: 200
Win2K
D6 Prof
|
Verfasst: Fr 28.10.05 09:58
Horst_H hat folgendes geschrieben: | Ich habe die Funktion von alzaimer etwas umgestrickt, um diese merkwuerdigen delete's (ist ja ein move) und das unnoetige Berechnen von n! einzusparen.
Es wird eine eindeutige Kombination aus acount mod n! bestimmt.
Es werden genau n-1 Tauschungen vorgenommen.
Also muesste das hier schneller sein.
Gruss Horst |
Bei exakter Implementation ist deine Variante etwa viermal so schnell (~530ms für alle 9! Permutationen, der von alzaimar ~2050ms).
**Edit:
Komisch dass deine Variante bei mir fast doppelt so lange braucht wie bei dir (die Zeit für alzaimer-org ist ja sehr ähnlich).
|
|
alzaimar
      
Beiträge: 2889
Erhaltene Danke: 13
W2000, XP
D6E, BDS2006A, DevExpress
|
Verfasst: Fr 28.10.05 10:40
Horst_H hat folgendes geschrieben: |
Ich habe die Funktion von alzaimer etwas umgestrickt, um diese merkwuerdigen delete's (ist ja ein move) und das unnoetige Berechnen von n! einzusparen.
|
Wenn Du die Herleitung gelesen hättest, und/oder wüsstest, was eine Inverse und ein Graycode ist, dann hättest du das 'delete' und die Berechnungen von n! nicht als "merkürdig" und "unnötig" bezeichnet (Wie kann man so fies zu einem 'delete' sein  ).
Aber Deine Lösung gefällt mir sowieso noch viel besser, hat aber nichts mehr mit meiner Herleitung zu tun. Sie müsste dann auch 'HorstPermutation' heissen, oder 'NthHorstation' oder so. Ehre, wem Ehre gebührt!
_________________ Na denn, dann. Bis dann, denn.
|
|
Horst_H
      
Beiträge: 1654
Erhaltene Danke: 244
WIN10,PuppyLinux
FreePascal,Lazarus
|
Verfasst: Fr 28.10.05 11:50
Hallo,
zuviel der Ehre, fuer etwas , dass es sicher schon mal gab.
Es ist nur eine Vereinfachung von mischen, wobei statt Pos = Zufallszahl(n) eben Pos = acount mod n genutzt wird.
www.webplain.de/fore...hp?1,298,311#msg-311
Als Abwandlung koennt man jetzt mit nur einer Zufallszahl mischen.
Also 52 Karten und dann Zufallszahl aus 52! -> nthPermutation -> gemischt.
Aber 52! = 8.0658E67 also nicht mehr ganz in Int64
Gruss Horst
|
|
alzaimar
      
Beiträge: 2889
Erhaltene Danke: 13
W2000, XP
D6E, BDS2006A, DevExpress
|
Verfasst: Fr 28.10.05 11:55
Horst_H hat folgendes geschrieben: | zuviel der Ehre, fuer etwas , dass es sicher schon mal gab. |
Meinst Du, Graycodes und Inverse sind von mir? Obwohl ich kenn keine Herleitung für Permutationen, die darauf basieren. Aber das will nix heissen. Wenn Ihr aber schon meine Funktion als 'Org Alz' bezeichnet, dann doch auch den 'Org Horst' in Zukunft. Ach egal.
_________________ Na denn, dann. Bis dann, denn.
|
|
Horst_H
      
Beiträge: 1654
Erhaltene Danke: 244
WIN10,PuppyLinux
FreePascal,Lazarus
|
Verfasst: Fr 28.10.05 12:38
Hallo,
@Luzzifus Zitat: | **Edit:
Komisch dass deine Variante bei mir fast doppelt so lange braucht wie bei dir (die Zeit für alzaimer-org ist ja sehr ähnlich). |
Also ich benutze jetzt D7 AMD64 3000+ bei 1800Mhz
{CrystalCpuId /hide /resi /cq
9x 1.225V
7x 1.075V
5x 0.875V }
eigentlich muessten Ahtlon XP und Pentium schneller sein.
dennishomepage.gugs-....dk/PosChallenge.htm
Der Opteron 240(1,4 GHz) ist ja nur auf Hoehe des Pentium III mit 1400 Mhz, ganz schoen traurig, aber leider wahr.
Quelltext 1: 2:
| P3 1400 C PM 1500 C P4 1600 A P4 2800 E XP 2500+ Opteron 240 517 384 488 291 409 525 ms |
Der Duron 1800 kommt schon an die Zeiten des XP 2500+(1.833 Ghz)
Also wer sich viel mit Zeichenketten rumaergern muss, braucht die Mhz.
Gruss Horst
P.S.:
Der Athlon 64 hat bei manchen Benchmark's vom Fastcode Project, die 10 mal einen Test laufen lassen, die Eigenschaft, ab dem dritten Durchlauf schneller zu werden.
Hier ist Subbench1 von 200 auf 182 ms gesunken????
PosShaAsm3_a 0 183 241 423
PosShaAsm3_a 0 183 241 423
PosShaAsm3_a 0 181 242 423
PosShaAsm3_a 0 181 242 423
PosShaAsm3_a 0 183 242 425
PosShaAsm3_a 0 200 241 441
PosShaAsm3_a 0 200 242 442
|
|
|