© Prof. Dr. Andreas Solymosi

Permutationen rekursiv berechnen

Die Anzahl der Permutationen aus n Elementen ist n!. Dies ist durch die folgende Überlegung, genannt mathematische Induktion, einleuchtend. Angenommen, wir haben alle Permutationen aus n-1 Elementen. Daraus erzeugen wir die Permutationen aus n Elementen, indem wir die Permutationen aus n-1 Elementen n-mal kopieren. Das n-te Element wird nun in der ersten Kopie vor das erste Element geschoben, in der zweiten vor das zweite, usw.; in der n-1-sten Kopie vor das n-1-ste (also das letzte) Element, schließlich in der n-te Kopie nach das letzte Element. Wenn die Anzahl der Permutationen aus n-1 Elementen Pn-1 war, dann erhalten wir auf diese Weise n.Pn-1 verschiedene Permutationen. Für n=1 ist diese Anzahl 1, also ist Pn = n!.

Wir entwickeln nun eine Paketschablone für die rekursive Berechnung von Permutationen. Die Schnittstelle der Schablone ist:

generic -- Permut.ada
	type TBasis is private;
package GPermutationen is -- Permutationen rekursiv berechnen
	type TReihung is array (Positive range <>) of TBasis; -- TReihung'First = 1
	type TPermutationen is array (Positive range <>, Positive range <>) of TBasis; 
		-- TPermutationen'Range(1) = 1 .. Fak (TReihung'Last); TPermutationen'Range(2) = TReihung'Range
	function Permutationen (Reihung: TReihung) return TPermutationen; -- liefert alle Permutationen von Reihung
	function Fak (N: Positive) return Positive; -- Hilfsfunktion Fakultät
end GPermutationen;

In der rekursiven Funktion Permutationen werden wir eine lokale Reihung Temp: constant TPermutationen (1 .. Fak (Reihung'Length-1), 1 .. Reihung'Length-1) := Permutationen (Reihung (1 .. Reihung'Length-1)); anlegen, die die Permutationen aus n-1 Elementen enthält. Sie sollen diese in das Ergebnis (ebenfalls eine lokale zweidimensionale Reihung der Größe n! x n, ) n-mal kopieren und das letzte Element Reihung (Reihung'Length) an die 1-ste, 2-te, ... n-te Stelle einschieben. Eine geschickte Manipulation der Indizes ist hier der Schlüssel für eine elegante Lösung.

package body GPermutationen is
	-- Idee: Die Permutationen aus n können aus den Permutationenutationen aus n-1 berechnet werden,
	-- indem das n-te Glied auf die i-te Stelle (für i=1 .. n) aller Permutationenutationen aus n-1 eingeschoben wird.
	function Fak (N: Positive) return Positive is
	begin
		return N * Fak (N-1); -- raises Constraint_Error wenn N-1 not in Positive
	exception
		when Constraint_Error => return 1;
	end Fak;
	function Permutationen (Reihung: TReihung) return TPermutationen is
		Dimension: constant Positive := Reihung'Length;
		Fak_Dimension: constant Positive := Fak (Dimension); -- Anzahl der Permutationenutationen aus N ist N!
		Ergebnis: TPermutationen (1 .. Fak_Dimension, 1 .. Dimension); -- Dimension! Zeilen
	begin
		if Reihung'First /= 1 then -- Vorschrift pruefen
			raise Constraint_Error;
		end if;
		if Dimension = 1 then
			Ergebnis (1, 1) := Reihung (1);
		else
			declare
				Temp: TPermutationen (1 .. Fak (Dimension - 1), 1 .. Dimension - 1) := Permutationen (Reihung (1 .. Dimension - 1)); -- rekursiv Permutationen aus Dimension-1
				Erg_Zeile: Positive range Ergebnis'Range(1) := Ergebnis'First(1);
			begin -- nun wird Temp Dimension-mal kopiert; das letzte Element Reihung (Dimension) wird jedesmal dazwischengeschoben
				for Erg_Spalte in Ergebnis'Range(2) loop -- in jeder Zeile von Ergebnis wird das Element Reihung (Dimension) auf die I-te Stelle eingeschoben
					for Temp_Zeile in Temp'Range(1) loop
						for Temp_Spalte in Temp'Range(2) loop -- Spalten in Temp: Temp (J, K) kopieren
							if Temp_Spalte < Erg_Spalte then -- Spaltenvergleich: vor der I-ten Spalte normal, ab der I-ten Spalte verschoben kopieren
								Ergebnis (Erg_Zeile, Temp_Spalte) := Temp (Temp_Zeile, Temp_Spalte); -- normal kopieren
							else
								Ergebnis (Erg_Zeile, Temp_Spalte+1) := Temp (Temp_Zeile, Temp_Spalte); -- verschoben kopieren
							end if;
						end loop; -- Temp_Spalte
						Ergebnis (Erg_Zeile, Erg_Spalte) := Reihung (Dimension); -- letztes Element einschieben
						begin
							Erg_Zeile := Erg_Zeile + 1;
						exception -- das letzte Mal
							when Constraint_Error => 
								null;
								-- check Erg_Zeile = Dimension;
						end; -- exception für Erg_Zeile
					end loop; -- Temp_Zeile
				end loop; -- Erg_Spalte
			end; -- declare Temp
		end if; -- Dimension = 1
		return Ergebnis;
	end Permutationen;
end GPermutationen;

Diese Schablone wird im nächsten Stapeltestprogramm ausgeprägt:

with GPermutationen, Text_IO;
procedure Stapel_Permutation is
	type T is new Character; -- austauschbar
	package MPermutationen is new GPermutationen (T);
	Reihung: constant MPermutationen.TReihung := "abcdef"; -- austauschbar
	Permutationen: constant MPermutationen.TPermutationen (1 .. MPermutationen.Fak (Reihung'Last), Reihung'Range) := 
		MPermutationen.Permutationen (Reihung);
begin
	Text_IO.Put ("Alle Permutationen von ");
	for I in Reihung'Range loop
		Text_IO.Put (T'Image (Reihung (I)) & " ");
	end loop;
	Text_IO.Put_Line ("sind:");
	
	for I in Permutationen'Range(1) loop
		for J in Permutationen'Range(2) loop
			Text_IO.Put (T'Image (Permutationen (I, J)) & " ");
		end loop;
		Text_IO.New_Line;
	end loop;
end Stapel_Permutation;

Ein Dialogtestprogramm ist:

with GPermutationen, Text_IO;
procedure Dialog_Permutation is
	type T is new Integer; -- austauschbar
	package MPermutationen is new GPermutationen (T);
	package Pos_IO is new Text_IO.Integer_IO (Positive);
	Dimension: Positive;
begin
	Text_IO.Put_Line ("Permutationen werden rekursiv berechnet.");
	Text_IO.Put ("Aus wievielen Elementen? (positive ganze Zahl):");
	Pos_IO.Get (Dimension);
	declare
		Reihung: MPermutationen.TReihung (1 .. Dimension);
		Permutationen: MPermutationen.TPermutationen (1 .. MPermutationen.Fak (Reihung'Last), Reihung'Range);
	begin
		Text_IO.Put ("Alle Permutationen von ");
		for I in Reihung'Range loop
			Reihung (I) := T (I);
			Text_IO.Put (T'Image (Reihung (I)) & " ");
		end loop;
		Text_IO.Put_Line ("sind:");
		Permutationen := MPermutationen.Permutationen (Reihung);
		for I in Permutationen'Range(1) loop
			for J in Permutationen'Range(2) loop
				Text_IO.Put (T'Image (Permutationen (I, J)) & " ");
			end loop;
			Text_IO.New_Line;
		end loop;
	end;
end Dialog_Permutation;

© Prof. Dr. Andreas Solymosi

Rückmeldungen bitte an den Autor solymosi@tfh-berlin.de

Leitseite des Autors