Permutationen

Code:
[B]InsertionSort[/B]
Sortieren durch Einfugen.
Gegeben sei eine Folge a[1],...,a[N] von Schlüsselelementen.
Prinzip: Wir betrachten den i-ten Durchgang der Schleife i = 2,...,N. Dabei sei die
i − 1-elementige Teilfolge a[1],...,a[i − 1] bereits sortiert

Programm:
PROCEDURE InsertionSort (VAR a : ARRAY[0..N] OF KeyType) =
	VAR j : CARDINAL;
	        v : KeyType;
	BEGIN
	a[0] := −∞; (* in Modula-3: a[0]:=FIRST(KeyType); *)
	FOR i := 2 TO N DO
		v := a[i];
		j := i;
		WHILE a[j-1]>v DO
			a[j] := a[j-1];
			DEC(j);
		END;
		a[j] := v;
	END;
END InsertionSort;

Beachte: Um in der WHILE-Schleife eine zusatzliche Abfrage auf die linke Feldgrenze zu
vermeiden, wird ein sog. Sentinel-Element (=Warter; Anfangs- oder Endmarkierung)
a[0] := −∞ verwendet.
Bemerkung: Auf die Verwendung eines Sentinel-Elementes kann bei nicht-strikter
Auswertung (auch lazy evaluation) des Compilers verzichtet werden:
WHILE 1<j AND a[j-1]>v DO
Dabei wird zunachst nur der linke Teil 1<j der Abbruchbedingung ausgewertet.
Liefert die Auswertung den Wert FALSE, so wird die Schleife verlassen, ohne daß der
rechte Teilausdruck a[j-1]>v ausgewertet wird. (Sprechweise:
Der Operator „AND ist nicht-strikt“ bzw. „AND ist ein short-circuit operator“) Die Auswertung des rechten
Teilausdrucks erfolgt nur dann, wenn das linke Argument von AND zu TRUE ausgewertet
werden kann.
Bei strikterUbersetzung des Compilers kann die Abbruchbedingung der WHILE-Schleife
auch innerhalb einer LOOP-Anweisung formuliert werden:
LOOP
    IF 1<j THEN
	IF a[j-1]>v THEN
		a[j] := a[j-1];
		DEC(j);
	ELSE EXIT; END;
       ELSE EXIT; END;
END

Beispiel:
InsertionSort durchläuft die zu sortierende Folge von links nach rechts. Dabei ist die
Anfangsfolge des Feldes zwar in sich sortiert, die Elemente befinden sich jedoch noch
nicht notwendigerweise an ihrer endgultigen Position. 

−∞	3 	[B]7[/B]	8	6	4	2
		[B]*[/B]
−∞	3 	7	[B]8[/B]	6	4	2
			[B]*[/B]
−∞	3 	7 --->	8   ---->      [B]6[/B]	4	2
                         <-------  <----------------|
−∞	3 	6 --->	7 ----->	 8 ---->	[B]4[/B]	2
		<--------- <-------- <---------|
−∞	3 ----->	4 ----->	6 ----->	7 ----->   8 ----->[B]2[/B]
	<-------  <------- <------- <------  <-----------|
−∞	2 	3	4	6	7	8
−∞
Sentinel-Element a[0] = −∞	
Komplexitatsanalyse
Vergleiche: Das Einfügen des Elements a[i] in die bereits sortierte Anfangsfolge
a[1],...,a[i − 1] erfordert mindestens einen Vergleich, hochstens jedoch i Vergleiche.

Das folgende Programm führt eine in-situ-Permutation durch wiederholte verkettete
Ringtauschoperationen aus:

PROCEDURE InsituPermutation (VAR a : ARRAY[1..N] OF KeyType;
				    VAR p : ARRAY[1..N] OF CARDINAL) =
	VAR t : KeyType;
	         j,  k : CARDINAL;
	BEGIN
	   FOR i := 1 TO N DO
	        IF p[i] <> i THEN
		t := a[i];
		k := i;
		REPEAT
		     j := k; a[j] := a[p[j]];
                            k := p[j]; p[j] := j;
		UNTIL k=i;
		a[j] := t;
	END;
       END;
END InsituPermutation;
 
Es ist immer wieder erstaunlich wie wenig nachvollziehbar Programmiersprachen sind. Ich stehe gerade auf dem Schlauch. Jetzt wüsste ich natürlich gerne, welche Laufzeit diese in-situ Permutation hat.
 
Hi [LoN]Kamikaze,

wenn Du statt des taken[]-array ein mit 1 initialisiertes Integerarray notTaken[] verwendest kannst Du die for(p)-Schleife noch erheblich komprimieren

Code:
for(int p=0; position ; position -= notTaken[p++])
  ; /* empty body */
notTaken[p] = 0;
objects[p]  = original[i];

Ciao und vielen Dank für das hübsche Stück Code
PhysChemist
 
Ja, keine schlechte Idee. Ein kleiner Fehler hat sich aber bei dir eingeschlichen, p ist nur innerhalb der Schleife definiert. Leider kann dein Ansatz nicht funktionieren, weil notTaken geprüft werden muss, bevor position geprüft wird. Auf jeden Fall Danke für die Anregung. Das hat trotzdem weitergeholfen.

Auf jeden Fall aber kann ich auf boolean taken[] verzichten. Deshalb habe ich die Schleife überarbeitet.
 
Das ich auch immer die hälfte übersehen muss!! :mad:

also vielleicht so
Code:
int p;
for(p=0; 0 != notTaken[p];++p)
  ; /* search for first unused object */
for(  ; position ; position -= notTaken[p++])
  ; /* empty body */
notTaken[p] = 0;
objects[p]  = original[i];

Möglicherweise funktioniert auch etwas anderes( ich überblicke auf die Schnelle nicht ob das hinhaut), würde aber die gesammte inner Schleife und das Kopieren des Objektarrays ersparen :
Code:
  tempObject           = original[i];
  original[i]          = original[i+position];
  original[i+position] = tempObject;

Irgenwie sollte es doch klappen da nicht jedes mal suchen zu müssen, wenn man schon weiß, daß das n-te nicht verwendete Object genommen werden soll, oder?
 
PhysChemist schrieb:
Irgenwie sollte es doch klappen da nicht jedes mal suchen zu müssen, wenn man schon weiß, daß das n-te nicht verwendete Object genommen werden soll, oder?
Die Variable position gibt an die wievielte der möglichen Positionen gewählt werden soll. Man kann mit Gewissheit sagen, das der Index des freien Platzes mindestens position ist. Jedoch muss man trotzdem bei 0 anfangen zu suchen, da man ja wissen muss, wie viele freie Positionen davor waren.

Inzwischen habe ich den Algorithmus dank deiner Anmerkungen etwas verkürzt. Ich denke das Optimierungspotential ist so ziemlich ausgeschöpft. Das Kopieren des Array ist übrigens recht unproblematisch, da ja lediglich Referenzen kopiert werden.

Code:
	/**
	 * Creates a permutation of an array. This method performs O(n^2).
	 * 
	 * @param	objects			An array to create a permutation from.
	 * @param	permutation		A number defining which permutation is wanted.
	 */
	public static void permutate(Object objects[], long permutation) {
		// A backup of the original array, required to build the new one.
		Object original[] = (Object[]) objects.clone();
		
		// Set all objects to null, to mark them as free.
		for (int i = 0; i < objects.length; i++)
			objects[i] = null;
		
		// Assign a new position to every object.
		for (int i = 0; i < objects.length; i++) {
			// Get the position for this object.
			int position = (int) (permutation % (objects.length - i));
			// Lower permutation for the next object.
			permutation /= objects.length - i;

			// Find the free position.
			int freePosition = 0;
			for (;position > 0 || objects[freePosition] != null; freePosition++)
				if (objects[freePosition] == null)
					position--;

			// Assign the position.
			objects[freePosition] = original[i];				
		}
	}
 
Ich habe noch eine Methode angefügt, mit der man eine Permutation rückgängig machen kann. Ziemlich einfach, aber was soll's.

Code:
	/**
	 * Undoes the permutation of an array.
	 *  
	 * @param	objects			An array to turn into its original form.
	 * @param	permutation		A number defining which permutation the given
	 * 							array is.
	 */
	public static void unPermutate(Object objects[], long permutation) {
		// A backup of the given array, required for the reconstruction.
		Object permutated[] = (Object[]) objects.clone();
		
		// An array containing its index numbers.
		Integer order[] = new Integer[objects.length];
		for (int i = 0; i < objects.length; i++)
			order[i] = i;
		
		// Permutate the index numbers as the original objects are permutated.
		permutate(order, permutation);
		
		// Restore the original order.
		for (int i = 0; i < objects.length; i++)
			objects[i] = permutated[order[i]];	
	}
 
Zurück
Oben