| 
| Autor | Beitrag |  
| Delphi-Laie 
          Beiträge: 1600
 Erhaltene Danke: 232
 
 
 Delphi 2 - RAD-Studio 10.1 Berlin
 
 | 
Verfasst: Do 24.09.09 19:59 
 
Nachdem die eingeworfene iterative Lösung dank BenBE deutlich verbessert wurde, möchte ich noch einmal auf die ursprüngliche Aussage:
 	  |  Horst_H hat folgendes geschrieben  : |  	  | Was ich noch nicht gefunden habe ist eine nextpermutation, mit jeweils nur einer Tauschung. | 
 zurückkommen.
 Anscheinend gibt es eine rekursive  Lösung schon wesentlich länger: In [url=www.cs.princeton.edu/~rs/talks/perms.pdf]dieser PDF-Datei [/url] scheint die Methode 4 "Heap's Algorithm" genau das zu erfüllen:
 - Ein Elementetausch (logischerweise nur zweier Elemente) zur Erzeugung einer jeden neuen Permutation
 - jede Permutation wird genau einmal erzeugt, also eine - wenn auch nicht lexikographische) Enumeration
 während Methode 1 "Backtracking" jeweils zwei Vertauschungen vornimmt und nach meiner Recherche Methode 2 "Plain changes" nicht sauber enumeriert: Es gibt Dopplungen, so daß andererseits, weil die Anzahl der Erzeugungen (n!) stimmt, Permustionen entfallen müssen. Heaps Algorithmus gibt es seit 1963! Spartanischer Pascal-Code dafür, in dem der beispielhafte Vektor a[1..n] (könnte genausogut z.B. ein String sein) erschöpfend permutiert wird, dazu:
 		                       Delphi-Quelltext 
 									| 1:2:
 3:
 4:
 5:
 6:
 7:
 8:
 9:
 10:
 11:
 12:
 13:
 14:
 15:
 16:
 17:
 18:
 19:
 
 | procedure generate(n:word);var c,t:byte;
 
 begin
 
 if n=1 then
 begin
 end;
 
 for c:=1 to n do
 begin
 if odd(n) then
 begin t:=a[c];a[c]:=a[n];a[n]:=t end
 else begin t:=a[1];a[1]:=a[n];a[n]:=t
 end
 end
 
 end;
 |  , aufzurufen über die Anzahl der zu permutierenden Elemente: generate(Elementeanzahl), bei einem Vektor (Array) also generate(succ(high(a))), bei einem String generate(length(a)). |  |  |  
| Fiete 
          Beiträge: 617
 Erhaltene Danke: 364
 
 W7
 Delphi 6 pro
 
 | 
Verfasst: Sa 26.09.09 16:45 
 
Moin an alle,
 zwei Bemerkungen seien gestattet:
 Das Permutationsproblem ist äquivalent zum Turmproblem, dieses wiederum kann aus dem [url=de.wikipedia.org/wiki/Damenproblem]Damenproblem [/url] abgeleitet werden.
 												| 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:
 
 | unit NDamen2009;
 interface
 
 uses
 Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
 StdCtrls, Spin, Grids, ExtCtrls, DBCtrls;
 
 type
 TBrett=Array of Integer;
 TNDamen = class(TForm)
 Anzahl: TSpinEdit;
 StaticText1: TStaticText;
 Start: TButton;
 StellungsAusgabe: TListBox;
 Zeigen: TCheckBox;
 StaticTextN: TStaticText;
 LabelN: TLabel;
 StaticTextTime: TStaticText;
 FeldAusgabe: TStringGrid;
 LabelZeit: TLabel;
 procedure StartClick(Sender: TObject);
 procedure StellungsAusgabeClick(Sender: TObject);
 private
 Brett:TBrett;
 BrettGroesse:Integer;
 Stellungen:Integer;
 function TimeSekunden:extended;
 procedure Ausgabe(NR:Integer;Brett:TBrett);
 function Erlaubt(x:Integer):Boolean;
 procedure Setz(x:Integer);
 procedure Fuellen(Nr:Integer);
 
 public
 
 end;
 
 var NDamen: TNDamen;
 
 implementation
 
 {$R *.DFM}
 {$R+,Q+}
 
 function TNDamen.TimeSekunden:extended;
 var H,M,S,MS:Word;
 begin
 DecodeTime(Now,H,M,S,MS);
 TimeSekunden:=3600.0*H+60.0*M+S+MS/1000
 end;
 
 procedure TNDamen.Ausgabe(NR:Integer;Brett:TBrett);
 var x,y:Integer;
 Zeile:String;
 begin
 IF NDamen.Zeigen.Checked then
 begin
 Zeile:='';
 StellungsAusgabe.Items.Add(IntToStr(Nr)+'-te Stellung');
 for x:=1 to BrettGroesse do
 for y:=1 to BrettGroesse do
 IF Brett[x]=y then Zeile:=Zeile+char(x+64)+IntToStr(y)+' ';
 StellungsAusgabe.Items.Add(Zeile);
 StellungsAusgabe.Items.Add('');
 end
 end;
 
 function TNDamen.Erlaubt(x:Integer):Boolean;
 var K:Integer;
 begin
 Erlaubt:=True;
 for K:=1 to x-1 do
 if Brett[x]=Brett[K] then begin Erlaubt:=False;exit end;
 [b]   for K:=1 to x-1 do
 if Abs(Brett[x]-Brett[K])=x-K then begin Erlaubt:=False;exit end;
 end;
 
 procedure TNDamen.Setz(x:Integer);
 var y:Integer;
 begin
 for y:=1 to BrettGroesse do
 begin
 Brett[x]:=y;
 if Erlaubt(x) then
 if x<BrettGroesse then Setz(x+1)
 else begin Inc(Stellungen);Ausgabe(Stellungen,Brett) end;
 end
 end;
 
 procedure TNDamen.Fuellen(nr:Integer);
 var x,y:Integer;
 Teil,Zeile:String;
 begin
 for x:=1 to BrettGroesse do
 FeldAusgabe.Cells[x,0]:=char(64+x);
 for y:=1 to BrettGroesse do
 FeldAusgabe.Cells[0,BrettGroesse+1-y]:=IntToStr(y);
 for y:=1 to BrettGroesse do
 for x:=1 to BrettGroesse do
 FeldAusgabe.Cells[x,y]:=' ';
 Zeile:=StellungsAusgabe.Items[Nr];x:=0;
 while Zeile<>'' do
 begin
 Teil:=Copy(Zeile,1,Pos(' ',Zeile)-1);Inc(x);
 y:=StrToInt(Copy(Teil,2,Length(Teil)-1));
 FeldAusgabe.Cells[x,BrettGroesse+1-y]:='D';
 Delete(Zeile,1,Pos(' ',Zeile));
 end;
 end;
 
 procedure TNDamen.StartClick(Sender: TObject);
 var x,y:Integer;
 TSek:Extended;
 begin
 BrettGroesse:=StrToInt(Anzahl.Text);
 SetLength(Brett,BrettGroesse+1);
 StellungsAusgabe.Clear;
 StellungsAusgabe.Width:=160;
 FeldAusgabe.Width:=25*(BrettGroesse+1);
 FeldAusgabe.Height:=25*(BrettGroesse+1);
 FeldAusgabe.ColCount:=BrettGroesse+1;
 FeldAusgabe.RowCount:=BrettGroesse+1;
 FeldAusgabe.Visible:=False;
 if BrettGroesse>4 then StellungsAusgabe.Width:=120+(BrettGroesse-4)*30;
 if BrettGroesse>11 then Zeigen.Checked:=False;
 if Not Zeigen.Checked then
 begin
 StellungsAusgabe.Visible:=False;
 FeldAusgabe.Visible:=False
 end
 else
 begin
 StellungsAusgabe.Visible:=True;
 FeldAusgabe.Visible:=True;
 end;
 Stellungen:=0;
 Screen.Cursor:=crHourGlass;
 TSek:=TimeSekunden;Setz(1);TSek:=TimeSekunden-TSek;
 Screen.Cursor:=crDefault;
 LabelZeit.Caption:=Format('%0.2f',[TSek])+' sek.';
 LabelN.Caption:=IntToStr(Stellungen);
 for x:=1 to BrettGroesse do
 FeldAusgabe.Cells[x,0]:=char(64+x);
 for y:=1 to BrettGroesse do
 FeldAusgabe.Cells[0,BrettGroesse+1-y]:=IntToStr(y);
 for y:=1 to BrettGroesse do
 for x:=1 to BrettGroesse do
 FeldAusgabe.Cells[y,BrettGroesse+1-x]:=' ';
 end;
 
 procedure TNDamen.StellungsAusgabeClick(Sender: TObject);
 var Nr:Integer;
 begin
 Nr:=StellungsAusgabe.ItemIndex;
 if (NR+2)mod 3=0 then Fuellen(Nr);
 end;
 
 end.
 |  Ein neuer Algorithmus ist ein Rotationsalgorithmus, Beispiel für N=4					
 												| 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:
 
 | 1  2  3  4            2  3  4  1
 3  4  1  2
 4  1  2  3
 1  2  3  4    T[4]=L, also dec(L)
 2  3  1  4    es werden nur die ersten drei rotiert
 3  1  4  2
 1  4  2  3
 4  2  3  1
 2  3  1  4    T[4]=L, also dec(L)
 3  1  2  4    es werden nur die ersten drei rotiert
 1  2  4  3
 2  4  3  1
 4  3  1  2
 3  1  2  4    T[4]=L, also dec(L)
 1  2  3  4    es werden nur die ersten drei rotiert
 T[3]=L, also dec(L)
 2  1  3  4    es werden nur die ersten zwei rotiert
 1  3  4  2
 3  4  2  1
 4  2  1  3
 2  1  3  4    T[4]=L, also dec(L)
 1  3  2  4    es werden nur die ersten drei rotiert
 3  2  4  1
 2  4  1  3
 4  1  3  2
 1  3  2  4    T[4]=L, also dec(L)
 3  2  1  4    es werden nur die ersten drei rotiert
 2  1  4  3
 1  4  3  2
 4  3  2  1
 3  2  1  4    T[4]=L, also dec(L)
 es werden nur die ersten drei rotiert
 2  1  3  4    T[3]=L, also dec(L)
 es werden nur die ersten zwei rotiert
 1  2  3  4    T[2]=L, also dec(L)
 L hat den Wert 1 ==> Schluß
 |  												| 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:
 
 | unit Perm;
 interface
 
 uses
 Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
 Dialogs, StdCtrls, Spin;
 
 type
 TPermutationen = class(TForm)
 SpinEditN: TSpinEdit;
 LabelN: TLabel;
 ButtonWeiter: TButton;
 ButtonAlle: TButton;
 Ausgabe: TMemo;
 ButtonStart: TButton;
 procedure ButtonWeiterClick(Sender: TObject);
 procedure ButtonStartClick(Sender: TObject);
 procedure ButtonAlleClick(Sender: TObject);
 private
 
 Perm:Array[1..10]of Byte;
 N,Index:Byte;
 Anzahl:Integer;
 
 procedure Anzeigen;
 public
 
 end;
 
 var
 Permutationen: TPermutationen;
 
 implementation
 
 {$R *.dfm}
 
 procedure TPermutationen.ButtonWeiterClick(Sender: TObject);
 var Tausch,K:Byte;
 begin
 if Index>1 then
 begin
 Tausch:=Perm[1];
 for K:=2 to Index do Perm[K-1]:=Perm[K];
 Perm[Index]:=Tausch;
 if Perm[Index]=Index then dec(Index) else Anzeigen
 end
 else
 begin
 Ausgabe.Lines.Add('Anzahl der Permutationen: '+IntToStr(Anzahl));
 ButtonWeiter.Enabled:=False;
 ButtonAlle.Enabled:=False;
 end;
 end;
 
 procedure TPermutationen.ButtonStartClick(Sender: TObject);
 var K:Byte;
 begin
 N:=SpinEditN.Value;Ausgabe.Clear;
 for K:=1 to N do Perm[K]:=K;
 Anzahl:=0;Anzeigen;
 ButtonWeiter.Enabled:=True;
 ButtonAlle.Enabled:=True;
 end;
 
 procedure TPermutationen.Anzeigen;
 var K:Byte;
 Zeile:String;
 begin
 Zeile:='';
 for K:=1 to N do Zeile:=Zeile+Char(ord('@')+Perm[K])+' ';;
 Ausgabe.Lines.Add(Zeile);
 inc(Anzahl);Index:=N;
 end;
 
 procedure TPermutationen.ButtonAlleClick(Sender: TObject);
 var Tausch,K:Byte;
 begin
 N:=SpinEditN.Value;
 if N>8 then
 if MessageDlg('Bist du dir sicher',mtConfirmation,[mbYes,mbNo],0)=mrNo then exit;
 Ausgabe.Clear;
 for K:=1 to N do Perm[K]:=K;
 Anzahl:=0;Anzeigen;
 while Index>1 do
 begin
 Tausch:=Perm[1];
 for K:=2 to Index do Perm[K-1]:=Perm[K];
 Perm[Index]:=Tausch;
 if Perm[Index]=Index then dec(Index) else begin Index:=N;Anzeigen end;
 end;
 Ausgabe.Lines.Add('Anzahl der Permutationen: '+IntToStr(Anzahl));
 ButtonWeiter.Enabled:=False;
 ButtonAlle.Enabled:=False;
 end;
 
 end.
 |  Gruß an alle Tüftler
 Fiete_________________ Fietes Gesetz: use your brain (THINK)
 |  |  |  
| Horst_H 
          Beiträge: 1654
 Erhaltene Danke: 244
 
 WIN10,PuppyLinux
 FreePascal,Lazarus
 
 | 
Verfasst: So 27.09.09 15:31 
 
Hallo,
  Delphi-Laie  :
 Deine rekursive procedure generate(n:word); ist ohne Rekursion.
 Kein generate (n-1)  oder ähnliches drin.
  Fiete  :
 Dieser Rotationsalgorithmus schiebt ja gewaltige Datenmengen
 		                       Delphi-Quelltext 
 									| 1:2:
 3:
 
 |      Tausch:=Perm[1];for K:=2 to Index do Perm[K-1]:=Perm[K];
 Perm[Index]:=Tausch;
 |  Ob das so performant ist?
 Bei N-Damen habe ich die Permutionsrekursion und Stellungsprüfung kombiniert.
 Ich brauche ja nicht alle Permutation von DameInZeileKommtInSpalte[1,3,2....] testen, wenn 1,3,2 schon zu einem Fehler führt. Ich hoffe ich habe da nicht übersehen...
 Gruß Horst |  |  |  
| Fiete 
          Beiträge: 617
 Erhaltene Danke: 364
 
 W7
 Delphi 6 pro
 
 | 
Verfasst: Fr 09.10.09 12:42 
 
Moin Horst_H,
im Anhang ist eine Testversion mit Zeitmessung.
 Ich habe nichts besseres gefunden oder entwickelt.
 Gruß
 Fiete
 
Einloggen, um Attachments anzusehen!
 
_________________ Fietes Gesetz: use your brain (THINK)
 |  |  |  
| Horst_H 
          Beiträge: 1654
 Erhaltene Danke: 244
 
 WIN10,PuppyLinux
 FreePascal,Lazarus
 
 | 
Verfasst: Sa 10.10.09 09:23 
 
Hallo,
 Deine Version ist erheblich schneller, wenn man statt des normalen kopierens in einer Schleife den move-Befehl nimmt. Sonst etwa gleichschnell.
 permlex habe ich angepasst, sodass kein RangeCheck Error auftritt und ein paar Variablen eingespart.
 Ich habe mal spasseshalber statt byte integer benutzt. 
 Das beschleunigt um 10%, außer die Version mit move welche langsamer wird, die aber immer noch schneller als die anderen Versionen bleibt. 
 Gruß Horst
 P.S.
 Meine Lösung für das 8-Damen Problem von 2002 finde ich nicht mehr    Ich meine, die hätte nur 1536 statt 8! = 40320 Durchläufe gehabt um alle 92 Lösungen zu finden.
 Alles gelogen    es sind 5508 Test's von 40320
www.delphi-forum.de/viewtopic.php?p=440863 EDIT: 
 Eine neue Version von permlex mit Zeigern.Etwas schneller. ~22 CpuTakte pro Permutation.
Einloggen, um Attachments anzusehen!
 
 Zuletzt bearbeitet von Horst_H am Do 10.01.13 23:08, insgesamt 1-mal bearbeitet
 |  |  |  
| Delphi-Laie 
          Beiträge: 1600
 Erhaltene Danke: 232
 
 
 Delphi 2 - RAD-Studio 10.1 Berlin
 
 | 
Verfasst: Sa 17.10.09 17:16 
 
	  |  Horst_H hat folgendes geschrieben  : |  	  | Hallo, 
 
  Delphi-Laie : Deine rekursive procedure generate(n:word); ist ohne Rekursion.
 Kein generate (n-1)  oder ähnliches drin.
 | 
 Hast Du recht, Horst. Weiß auch nicht, wie die abhanden kommen konnte. Asche auf mein Haupt.
 Das Original von Herrn Sedgewick habe ich ohnehin verlinkt, und das ist allemal aussagekräftiger.
 Allerdings war ich zu euphorisch, was ich erst nach dem Posten richtig bemerkte (ja, ich war voreilig!): Ältere Permutationsalgorithmen, ob rekursiv oder iterativ, kommen anscheinend doch nicht mit der Minimalanzahl von einer Vertauschung (logischerweise zweier Elemente) zur Generation jeder nächsten Permutation aus der / einer / ihrer Vorgängerpermutation (oder lexikographisch mit jeweils kompletter Neugeneration?!) aus (und sind damit in bezug auf deren Laufzeit tendenziell unterlegen), was ich zunächst so an- und wahrnahm. Das scheint wohl doch nur der sehr neue Algortihmus des Herren (?) Viktorov von der Ammaner Universität von 2007 zu bieten. Er äußerte sich ja auch in dahingehend.
 Genial wäre es nun noch, einen Algoritmus mit jeweils nur einer Vertauschung pro Generationsschritt zu entwerfen, der die Inversionsanzahl der Permutation(en) immer nur um 1 ändert - aber das ist schon theoretische bzw. "höhere" Informatik. |  |  |  
| Delphi-Laie 
          Beiträge: 1600
 Erhaltene Danke: 232
 
 
 Delphi 2 - RAD-Studio 10.1 Berlin
 
 | 
Verfasst: Do 09.05.13 20:53 
 
Nach Jahren möchte ich diese Diskussion noch einmal aufwärmen; zunächst geht es nochmal um diese Bemerkung:
 	  |  Horst_H hat folgendes geschrieben  : |  	  | Was ich noch nicht gefunden habe ist eine nextpermutation, mit jeweils nur einer Tauschung. | 
 sowie ein Eigenzitat aus dem Beitrage zuvor:
 	  |  Delphi-Laie hat folgendes geschrieben  : |  	  | Ältere Permutationsalgorithmen, ob rekursiv oder iterativ, kommen anscheinend doch nicht mit der Minimalanzahl von einer Vertauschung (logischerweise zweier Elemente) zur Generation jeder nächsten Permutation aus der / einer / ihrer Vorgängerpermutation (oder lexikographisch mit jeweils kompletter Neugeneration?!) aus (und sind damit in bezug auf deren Laufzeit tendenziell unterlegen), was ich zunächst so an- und wahrnahm. Das scheint wohl doch nur der sehr neue Algortihmus des Herren (?) Viktorov von der Ammaner Universität von 2007 zu bieten. Er äußerte sich ja auch in dahingehend. 
 Genial wäre es nun noch, einen Algoritmus mit jeweils nur einer Vertauschung pro Generationsschritt zu entwerfen, der die Inversionsanzahl der Permutation(en) immer nur um 1 ändert - aber das ist schon theoretische bzw. "höhere" Informatik.
 | 
 Natürlich gibt es einen solchen Algorithmus schon längst (seit den 60ern), er heißt Steinhaus-Johnson-Trotter-Algorithmus. Er wird in www.math.uiowa.edu/~...ration%20Methods.pdf  beschrieben. Ich hatte die Datei schon länger, überlas das jedoch beim Kampfe durch das Angelsächsisch. Letztlich zum Ziele führte mich jedoch www.textarchiv.aloja...erzeugen-ap1070.html . Jedenfalls fand ich zu diesem Algorithmus nichts auf Deutsch, geschweige denn, in Pascal.
 Im Anhang ist ein auf die Schnelle zusammengewerkeltes Beispielprojekt. Bei der Eingabe der zu permutierenden Elementemenge am besten nur voneinander verschiedene Elemente in aufsteigender Reihenfolge, also z.B. "123" oder "ABCDE" o.ä. eingeben. Anhand der Inversionsanzahlen und der Differenz zur Anzahl der Inversionen zur Vorgängerpermutation kann man erkennen, daß immer nur benachbarte Elemente getauscht werden. Wiederum hat der Quelltext mehr Labels als Schleifen. Daß BenBE nochmal als Helfer in der gar nicht vorhandenen Not einspringt, diese Labels zu eliminieren, wage ich nicht zu hoffen. Mir gelang es bisher nicht.
Einloggen, um Attachments anzusehen!
 Für diesen Beitrag haben gedankt: Horst_H
 |  |  |  
| Horst_H 
          Beiträge: 1654
 Erhaltene Danke: 244
 
 WIN10,PuppyLinux
 FreePascal,Lazarus
 
 | 
Verfasst: Fr 10.05.13 08:31 
 
Hallo,
 die Goto's habe ich mit einer Dummy-Repeat Schleife und Abfrage der Abbruchbedingung eliminiert.
 Es ist schneller als nextPermLex und permMove. Bei n= 12 4.1 s statt 4.3 s/4.6 s
 Diese first wird doch eigentlich nur gebraucht, um die letzte Ausgabe zu machen.
 Die Initialisierung wird wirklich nur einmal aufgerufen.Die Felder habe ich nicht dynamisch erzeugt. Das macht freepascal langsam und N= 19 will niemand abwarten    												| 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:
 
 | Program PermSteinJohnsTrott;{$IFDEF FPC}
 {$MODE DELPHI}
 {$Optimization ON}
 {$Optimization RegVar}
 {$Optimization PEEPHOLE}
 {$Optimization CSE}
 {$Optimization ASMCSE}
 {$Else}
 {$APPTYPE console}
 {$Endif}
 
 uses
 sysutils,classes;
 type
 tFeld= array[0..19] of Integer;
 var
 T1,T0: TDateTime;
 sl : TStringlist;
 zaehler:cardinal;
 
 procedure GetPermutations(x:string);
 var
 p,d : tFeld;
 Nr : longint;
 
 i,
 k,l,n:integer;
 first:boolean;
 
 h:char;
 {$IFDEF AUSGABE}
 InvCnt_alt,InvCnt_neu,
 sAusgabe : String;
 function InvCnt:word;
 var
 k,l:word;
 begin
 result:=0;
 for k:=1 to pred(length(x)) do
 for l:=k to length(x) do
 if x[k]>x[l] then
 inc(result);
 end;
 {$ENDIF}
 function Init(j: integer): boolean;
 begin
 writeln('Init ');
 for j := j downto 2 do Begin
 p[j]:=0;
 d[j]:=1
 end;
 result :=false
 end;
 
 begin
 {$IFDEF AUSGABE}
 InvCnt_alt:=1;
 Nr := 1;
 For i := n downto 2 do
 Nr := Nr*i;
 sl.capacity := Nr;
 {$EndIF}
 n := length(x);
 zaehler:=0;
 Nr := 0;
 
 first:= true;
 IF first then
 first := Init(length(x));
 
 repeat
 n:=length(x);
 For i := n-1 downto 0 do Begin
 k:=0;
 repeat
 l:=p[n]+d[n];
 inc(zaehler);
 p[n]:=l;
 if l=n then begin
 d[n]:=-1;
 if n>2 then begin
 dec(n);
 continue
 end
 else
 break;
 end;
 if l<>0 then
 break;
 d[n]:=1;
 inc(k);
 
 if n>2 then
 dec(n);
 until false;
 
 IF L= n then         begin
 l:=1;
 first:=true;
 end;
 
 inc(Nr);
 {$IFDEF AUSGABE}
 InvCnt_neu:=InvCnt;
 sAusgabe := Format('%s %5d InvZahl %3d  DiffInvZahl %3d',[x,Nr,InvCnt_neu,InvCnt_neu-InvCnt_alt]));
 sl.add(sAusgabe);
 InvCnt_alt:=InvCnt_neu;
 {$ENDIF}
 
 l:=l+k;      h:=x[l];
 x[l]:=x[succ(l)];
 x[succ(l)]:=h;
 
 end;
 until first;
 Writeln(Nr);
 end;
 
 procedure FormCreate(s : string);
 
 begin
 T0 := now;
 GetPermutations(s);
 T1 := now;
 end;
 
 var
 i : integer;
 s : String;
 Begin
 formcreate( '123456789');
 {$IFNDEF AUSGABE}
 s := '123456789ABC';
 FormCreate(s);
 {$ELSE}
 s := '12345';
 sl := TStringlist.create;
 FormCreate(s);
 i:= sl.count;
 writeln(i);
 sl.sorted := true;
 sl.sorted := false;
 sl.add('');
 sl.add('Anzahl Permutationnen '+IntToStr(i));
 sl.add('');
 sl.add('Aufrufe zur Bestimmung der Tauschstelle '+InttoStr(Zaehler));
 sl.add('');
 sl.add(Format('Relation %5.3f',[Zaehler/i]));
 writeln(sl.text);
 sl.free;
 {$ENDIF}
 writeln(FormatdateTime('HH:NN:SS.zzz',T1-T0));
 end.
 |  Gruß Horst Für diesen Beitrag haben gedankt: Delphi-Laie
 |  |  |  
| Delphi-Laie 
          Beiträge: 1600
 Erhaltene Danke: 232
 
 
 Delphi 2 - RAD-Studio 10.1 Berlin
 
 | 
Verfasst: Fr 10.05.13 17:27 
 
Hallo Horst, vielen Dank für Deine Reaktion und Dein Interesse!
 Dir gelang es, die Labels / gotos zu eliminieren, auch dafür ein ganz dickes Dankeschön und ein genausodickes Lob! Ich muß mich nochmal damit beschäftigen, ganz schlau bin ich daraus noch nicht geworden.
 Tatsächlich ist es möglich, das p- und d-Array nur einmal zu initialisieren, bzw. es ist nur ein einmaliges Initialisieren nötig, also ist es in der großen repeat-Schleife deplaciert.
 Doch ein klein wenig Redundanz ist Dir dabei doch passiert: 		                       Delphi-Quelltext 
 									| 1:2:
 3:
 
 | first:= true;IF first then
 first := Init(length(x));
 |  Erst setzt Du first auf true, fragst es danach ab (die Bedingung ist dann immer erfüllt) und setzt es dann in der Init-Funktion auf false. Es ist mithin einfacher, vor der großen repeat-Schleife einfach ein
 		                       Delphi-Quelltext 
 									| 1:2:
 3:
 4:
 5:
 6:
 
 | for j:=2 to length(x) dobegin
 p[j]:=0;
 d[j]:=1
 end;
 first:=false;
 |  zu setzen.
 Neu ist mir, daß der Zugriff auf dynamische Arrays (konkret deren Elemente) bei FPC-Compilaten langsamer als bei statischen sein soll. Trifft das für den lesenden und/oder schreibenden Zugriff zu? Außerdem hoffe ich, daß das nicht auch für die Delphi-Pendants zutrifft - oder doch?
 Mithin liegen jetzt in dieser Diskussion zwei Permutationsenumerationsalgorithmen (welch ein monströses Wort!) auf der Basis nur einer Vertauschung (logischerweise zweier Elemente) zur Generierung der jeweils nächsten Permutation (was aufwandsminimiert ist) vor - ein sehenswertes Ergebnis! |  |  |  
| Horst_H 
          Beiträge: 1654
 Erhaltene Danke: 244
 
 WIN10,PuppyLinux
 FreePascal,Lazarus
 
 | 
Verfasst: Fr 10.05.13 18:34 
 
Hallo,
 dass Init habe ich erst rausgeschoben, um zu testen, ob man das so überhaupt mehrfach braucht und dass es nicht in der Hauptschleife Platz frisst.Kleine Schleife meistschnelle Schleife.
 Aber dann war ich mir sicher, dass bei first = true die Schleife garantiert verlassen wird.
 Das ist dann nur als Gag so geblieben.Das ist so kryptisch wie die Anordnung der Goto.
 
 [offtopic]
 Ich glaube, dass Delphi bei dynamischen Array wesentlich schneller war, was ich nicht mehr auf Richtigkeit testen kann. Aber ich bin mehr sicher, da freepascal wesentlich schneller geworden ist.
 Vor Jahren hatte ich bei 4 gewinnt 3 Schleifen geschachtelt und im Assemblerlisting belegte der äußere Schleifenindex hartnäckig ein Register und der innere Schleifenindex griff ständig auf seine Speicheradresse zu, grauselig.Deshalb habe ich möglichst kleine Proceduren genutzt, um die Register frei zu bekommen.
 
 Gruß Horst
 |  |  |  
| IhopeonlyReader 
          Beiträge: 600
 Erhaltene Danke: 23
 
 
 Delphi 7 PE
 
 | 
Verfasst: Fr 10.05.13 19:33 
 
mhh.. eine Sache.. die "Art" die am Anfang geschrieben wurde
For C:=123456789 to 987654321 do
 if keineZiffer doppelt
 //blabla
 
 liefert auch Ergebnisse wie
 1234567890 !
 bei der Permutation werden solche Ergebnisse vernachlässigt, da die 0 keine im ersten String vorkommende Zahl ist !!!
 
 Entweder geht es darum alle Zahlen zwischen 123456789 (und da wäre die erste eigentlich 012345678) und 987654321 herauszufinden, die keine Ziffer doppelt hat, oder jede "vertauschungsmöglichkeit", die dann entweder doppelte zulässt oder nicht..
 
 was willst du?
 
 Mit 0, dann gibt es 10*9*8*7*6*5*4*3*2 Möglichkeiten (3.628.800)
 sonst nur 9*8*7*6*5*4*3*2 (*1) Möglichkeiten (362.880)
 _________________ Sucht "neueres" Delphi    Wer nicht brauch was er hat, brauch auch nicht was er nicht hat!
 |  |  |  
| Delphi-Laie 
          Beiträge: 1600
 Erhaltene Danke: 232
 
 
 Delphi 2 - RAD-Studio 10.1 Berlin
 
 | 
Verfasst: Sa 11.05.13 10:32 
 
IhopeonlyReader, ich verstehe das Anliegen Deines vorigen Beitrage nicht recht.
 "123456789" ist ein aus 9 Elementen bestehender String. Wie soll dort eine zusätzliche "0" hineingeraten?
 Auch im Eröffnungsbeitrage war von einer "0" in der zu permutierenden Elementemenge nie die Rede. Das stellst auch Du richtig fest. Warum erwähnst Du dann das offenslichtliche überhaupt? Im Eröffnungsbeitrage und im Diskussionsverlauf war ja auch bisher nicht von Wellensittichen die Rede, also unterließ ich es bisher, sie zu erwähnen.
 Zudem ist 1234567890 > 987654321. Doch 987654321 war als Obergrenze festgelegt. Also kann die For-Schleife nicht bis zu 1234567890 gelangen.
 Wie man die Anzahl der Permutationen für 9 bzw. 10 Elemente berechnet, mußt Du nun wirklich nicht noch mal erläutern.
 Abgesehen davon, daß nicht klar ist, wen Du meinst, mache ich daraus nun eine Retourkutsche: Was wolltest Du mit Deinem letzten Beitrage bezwecken? Für diesen Beitrag haben gedankt: Mathematiker
 |  |  |  
| IhopeonlyReader 
          Beiträge: 600
 Erhaltene Danke: 23
 
 
 Delphi 7 PE
 
 | 
Verfasst: Sa 11.05.13 12:09 
 
hätte er die Methode mit der Schleife gewählt, wäre auch die Zahl
123456790 durchlaufen.. da hier ebenfalls keine Ziffer doppelt vorkommt, wäre es nach seiner Schleife einer Lösung, nach der Permutation aber nicht !
 
 Die Frage, welche Methode willst du? war den Ersteller des Threads gerichtet
 _________________ Sucht "neueres" Delphi    Wer nicht brauch was er hat, brauch auch nicht was er nicht hat!
 |  |  |  
| Delphi-Laie 
          Beiträge: 1600
 Erhaltene Danke: 232
 
 
 Delphi 2 - RAD-Studio 10.1 Berlin
 
 | 
Verfasst: Sa 11.05.13 12:29 
 
	  |  IhopeonlyReader hat folgendes geschrieben  : |  	  | hätte er die Methode mit der Schleife gewählt, wäre auch die Zahl 123456790 durchlaufen..
 | 
 durchlaufen worden .
 Nein, wäre eben nicht, weil 1234567890>987654321. Wann verinnerlichst Du das endlich?
 Recht hast Du nur insofern, als daß bei einer for-Schleife natürlich auch Zahlen entstanden wären, die 0(en) behinhaltet hätten.
 Doch die For-Schleife ist zur Enumeration von Permutationen ohnehin denkbar ungeeignet, wenn auch bei Anwendung eines geeigneten Siebes nicht untauglich.
 	  |  IhopeonlyReader hat folgendes geschrieben  : |  	  | da hier ebenfalls keine Ziffer doppelt vorkommt, wäre es nach seiner Schleife einer Lösung, nach der Permutation aber nicht ! | 
 ??
 Verstehe ich leider nicht. Edit: Nach wiederholtem Lesen ahne ich, was Du meinst. Dann treffen meine Aussagen zuvor zu. Soweit war diese Diskussion schon längst.
 	  |  IhopeonlyReader hat folgendes geschrieben  : |  	  | Die Frage, welche Methode willst du? war den Ersteller des Threads gerichtet | 
 Dem werden wir nach ein paar Jahren ohnehin wahrscheinlich nicht mehr helfen (können), aber vielleicht ist sein Anliegen auch bei ihm wieder aufwärmbar. Inzwischen geht es hier um die generelle Permutationsenumeration. |  |  |  
| IhopeonlyReader 
          Beiträge: 600
 Erhaltene Danke: 23
 
 
 Delphi 7 PE
 
 | 
Verfasst: Sa 11.05.13 15:15 
 
	  |  Delphi-Laie hat folgendes geschrieben  : |  	  | Nein, wäre eben nicht, weil 1234567890>987654321. Wann verinnerlichst Du das endlich? | 
 wann Liest du genau?
 nicht 1234567890 sondern 123456790 ! (123456789+1)
 somit diese zahl eine Lösung, die bei seiner Schleife enstanden wäre!
 Ihr betrachtet jetzt aber nur "Vertauschungen" ! ihr lasst also 90% der lösungen außer acht !
 Oder ihm geht es wirklich nur um die Vertauschung aller Ziffern und dann wäre sein "erster Einfall" falsch_________________ Sucht "neueres" Delphi    Wer nicht brauch was er hat, brauch auch nicht was er nicht hat!
 |  |  |  
| jfheins 
          Beiträge: 918
 Erhaltene Danke: 158
 
 Win 10
 VS 2013, VS2015
 
 | 
Verfasst: Sa 11.05.13 15:24 
 
	  |  IhopeonlyReader hat folgendes geschrieben  : |  	  | Ihr betrachtet jetzt aber nur "Vertauschungen" ! ihr lasst also 90% der lösungen außer acht ! Oder ihm geht es wirklich nur um die Vertauschung aller Ziffern und dann wäre sein "erster Einfall" falsch
 | 
 Ja genau das hat er soch schon im Originalpost geschrieben: 	  | Zitat: |  	  | dies erscheint mir aber nicht sehr elegant und schnell, da sehr viele zahlen "umsonst" erzeugt werden. Ich bin mir sicher es gibt eine bessere Lösung, ich komme nur nicht drauf. | 
 |  |  |  
| Delphi-Laie 
          Beiträge: 1600
 Erhaltene Danke: 232
 
 
 Delphi 2 - RAD-Studio 10.1 Berlin
 
 | 
Verfasst: Sa 11.05.13 15:28 
 
	  |  IhopeonlyReader hat folgendes geschrieben  : |  	  | nicht 1234567890 sondern 123456790 ! (123456789+1) somit diese zahl eine Lösung, die bei seiner Schleife enstanden wäre!
 | 
 Und? Soweit war diese Diskussion doch längst! Es entstehen bei der For-Schleife auch bannig viele "Lösungen", bei denen mehr als eine Ziffer vorhanden ist - wir haben aber von jeder nur eine (außer der Null).
 	  |  IhopeonlyReader hat folgendes geschrieben  : |  	  | Ihr betrachtet jetzt aber nur "Vertauschungen" ! ihr lasst also 90% der lösungen außer acht ! | 
 Das sind keine "Lösungen", sondern Ziffernwiederholungen und auch die Null wurden ausgeschlossen. Nur die Anzahl der verschiedenen Anordnungen (Anordnungsmöglichkeiten) einer vorgegebenen Elementemenge - und schon sind wir wieder bei den Permutationen.
 Übrigens, die Anzahl der Schleifendurchläufe ("Lösungen") ins Verhältnis zur Anzahl der Permutationen gesetzt: (1+987654321-123456789)/10!=0,0041990399896223725970761362957975.... bedeutet, daß nur ca. 0,42% der Schleifendurchläufe ein gewünschtes Ergebnis, eben eine Permutation erzeugen. Also lassen "wir" sogar über 99% der "Lösungen" außer acht. Das zeigt die Ineffizienz der Schleifen-Filter-Variante deutlich.
 	  |  IhopeonlyReader hat folgendes geschrieben  : |  	  | Oder ihm geht es wirklich nur um die Vertauschung aller Ziffern und dann wäre sein "erster Einfall" falsch | 
 Bingo alias Groschenfall. 
 Zuletzt bearbeitet von Delphi-Laie am Sa 11.05.13 15:36, insgesamt 5-mal bearbeitet
 |  |  |  
| IhopeonlyReader 
          Beiträge: 600
 Erhaltene Danke: 23
 
 
 Delphi 7 PE
 
 | 
Verfasst: Sa 11.05.13 15:28 
 
ich hatte das so verstamdem, dass mit "umsonst erzeugt" umsonst durchlaufen/getestet werden _________________ Sucht "neueres" Delphi    Wer nicht brauch was er hat, brauch auch nicht was er nicht hat!
 |  |  |  
| Mathematiker 
          Beiträge: 2622
 Erhaltene Danke: 1448
 
 Win 7, 8.1, 10
 Delphi 5, 7, 10.1
 
 | 
Verfasst: So 12.05.13 07:33 
 
Hallo,
 da mich solche Zahlprobleme immer interessieren, habe ich es auch einmal versucht.
 Allerdings erweitere ich das Problem auf:
 	  | Zitat: |  	  | Ordnet man die k-elementigen Teilmengen einer n-elementigen Menge in beliebiger Reihenfolge an, so ergeben sich die k-Permutationen dieser Menge. Diese entsprechen den Variationen zur k-ten Klasse. Da es (n über k) k-Teilmengen gibt, die je auf k! Arten angeordnet werden können, gibt es insgesamt (n über k) k! = n!/(n-k)! k-Permutationen.
 | 
 Und diese k-Permutationen versuche ich zu ermitteln. Ist n=k ergibt sich das ursprüngliche Problem.
 Die Routine
 												| 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:
 
 | procedure TForm1.BerechnenClick(Sender: TObject);var n,k,anz,h,i,m:integer;
 x,y:array[1..20] of integer;
 t1:int64;
 liste:tstringlist;
 procedure ausgabe;
 var i:integer;
 kk:string;
 begin
 kk:='';
 for i:=1 to k do kk:=kk+inttostr(x[i])+' ';
 liste.add(kk);
 application.processmessages;
 end;
 begin
 listbox1.clear;
 liste:=tstringlist.create;
 n:=updown1.position;
 k:=updown2.position;
 if k>n then k:=n;
 anz:=0;
 if k=n then m:=k-1
 else m:=k;
 for i:=1 to n do x[i]:=i;
 for i:=1 to m do y[i]:=i;
 
 t1:=gettickcount;
 i:=m;
 inc(anz);
 {$IFDEF Ausgabe}
 ausgabe;
 {$ENDIF}
 repeat
 if y[i]<n then
 begin
 inc(y[i]);
 h:=x[i];
 x[i]:=x[y[i]];
 x[y[i]]:=h;
 i:=m;
 inc(anz);
 {$IFDEF Ausgabe}
 ausgabe
 {$ENDIF}
 end
 else
 begin
 repeat
 h:=x[i];
 x[i]:=x[y[i]];
 x[y[i]]:=h;
 dec(y[i]);
 until y[i]<=i;
 dec(i);
 end;
 if anz mod 100000 = 0 then
 begin
 label3.caption:=inttostr(x[1]);
 application.processmessages;
 end;
 until (i=0);
 liste.add(inttostr(anz));
 liste.add(inttostr(gettickcount-t1)+' ms');
 listbox1.items:=liste;
 liste.free;
 end;
 |  berechnet diese k-Permutationen.
 	  |  Horst_H hat folgendes geschrieben  : |  	  | Es ist schneller als nextPermLex und permMove. Bei n= 12 4.1 s statt 4.3 s/4.6 s | 
 Leider bin ich von diesen Geschwindigkeiten wieder meilenweit entfernt. Für n = k = 12 brauche ich (ohne Anzeige) knapp 17 Sekunden.
 Aber zumindest ermittelt das kleine Programm auch die oben beschriebenen k-Permutationen.
 Beste Grüße
 Mathematiker
 Nachtrag: Durch   Horst_H  habe ich erfahren, dass es in der EE schon Hinweise zu "Permutation mit Doppelten" gibt. siehe www.entwickler-ecke....&highlight=5aus8
Einloggen, um Attachments anzusehen!
 
_________________ Töten im Krieg ist nach meiner Auffassung um nichts besser als gewöhnlicher Mord. Albert Einstein
 |  |  |  
| Horst_H 
          Beiträge: 1654
 Erhaltene Danke: 244
 
 WIN10,PuppyLinux
 FreePascal,Lazarus
 
 | 
Verfasst: Mi 15.05.13 07:11 
 
Hallo,
 Deine Version mit FPC auf Linux 64-Bit ( qword statt integer ) compiliert rennt doch schnell:
 		                       Quelltext 
 									| 1:2:
 3:
 4:
 5:
 
 |   # ./Perm
 n=12,k=12
 479001600
 00:00:04.704
 |  Das sind knapp über 31 CPU-Takte pro Permutation.
 Jetzt fällt mir aber auf, Du hast gar keine Permutation mit Doppelten darin, wie kam ich denn darauf???
 Gruß Horst |  |  |  |