In-Place-Permutation eines Arrays folgt dieser Regel

8

Nehmen wir an, es gibt ein Array, wir wollen alles im ungeraden Index finden (Index beginnend mit 0) und es bis zum Ende verschieben. Alles im geraden Index verschiebt es an den Anfang. Die relative Reihenfolge aller ungeraden Indexelemente und aller geraden Indexelemente bleibt erhalten.

d. wenn das Array

ist %Vor%

Nach der Operation wird

%Vor%

Kann dies vor Ort und in O (n) Zeit geschehen?

    
Chao Xu 17.06.2010, 15:16
quelle

4 Antworten

7

Es ist möglich, aber es ist sehr kompliziert! Eine einfachere O (nlogn) - und O (1) -Raumlösung ist möglicherweise besser zu codieren und in Bezug auf den Cache.

Wir werden ein anderes Problem lösen als Ihr, aber Ihr Problem ist trivial zu lösen, sobald wir dieses Problem gelöst haben.

Betrachten Sie das Array als

%Vor%

und du musst das in

umwandeln %Vor%

Arbeiten mit Indizes 1 bis 2n,

wir sehen, dass dies von

gegeben ist %Vor%

Eine O (nlogn) Zeit O (1) Raumlösung

Wir können wie folgt teilen und erobern.

Zuerst für einige m nahe an n / 2 konvertieren

b1, a1, ..., bn , an

bis

%Vor%

durch rekursives Anwenden auf die ersten 2m Elemente und dann auf die verbleibenden.

Jetzt müssen wir nur noch das mittlere Array um m Spots verschieben (dies kann in O (n) time und O (1) Space erfolgen)

geben

%Vor%

Natürlich, wie IVlad darauf hingewiesen hat, benötigt dies O (logn) Stapelspeicherplatz. Wir können das umgehen, indem wir Folgendes tun:

Wir haben:

%Vor%

Tausche nun Paare im letzten Teil des Arrays zu

aus %Vor%

Nun werden die Elemente zyklisch an der ungeraden Position verschoben: b1, b2, .., bm, a(m+1), a(m+2) ..., a(n).

Das ergibt etwas wie

%Vor%

Tauschen Sie nun den letzten Teil des Arrays erneut aus, um

zu erhalten %Vor%

Löse nun rekursiv den ersten Teil und den zweiten Teil, um

zu erhalten %Vor%

Dies funktioniert, ob 2m & gt; = n oder nicht.

Also, das ist O (nlogn) Zeit und O (1) Raumalgorithmus.

Eine O (n) Zeit O (1) Raumlösung.

Die verwendeten Ideen ähneln den Ideen in der folgenden Arbeit: Ein einfacher In-Place-Algorithmus für Inshuffle .

Sie müssten dieses Papier lesen, um das Folgende zu verstehen. Ich schlage vor, Sie lesen auch: Wie In-Place-Array-Modifikationsalgorithmen zu meistern?

Dies ist im Grunde die umgekehrte Permutation dessen, was in dem obigen Papier gelöst ist.

Es ist genug, um dies zu lösen, wenn 2n + 1 eine Potenz von 3 = (sagen wir 3 ^ m) ist, da wir danach teilen und erobern können (wie die O (nlogn) -Lösung).

Nun sind 2n + 1 und n + 1 relativ prim, also funktioniert Modulo 3 ^ m, wir sehen, dass n + 1 muss eine Potenz von 2 sein muss. (Sehen Sie das Papier noch einmal, um zu sehen, warum : Grundsätzlich ist jede Zahl modulo 3 ^ m, die relativ zu 3 ^ m ist, eine Potenz von 2, wieder modulo 3 ^ m).

Sage n + 1 = 2 ^ k (wir kennen k noch nicht und notieren, das ist modulo 3 ^ m).

Ein Weg, um k herauszufinden, berechne Potenzen von n + 1 modulo 3 ^ m, bis es 1 wird. Dies gibt uns k (und ist höchstens O (n) Zeit).

Nun können wir sehen, dass die Zyklen der Permutation (siehe oben, Papier / Stapelüberlauf-Verknüpfung für was das ist) bei

beginnen

2 ^ a * 3 ^ b

wobei 0 & lt; = a & lt; k und 0 & lt; = b & lt; m.

Sie beginnen also mit jedem möglichen Paar (a, b) und folgen den Zyklen der Permutation, und dies ergibt einen O (n) Zeit, In-Place-Algorithmus, wenn Sie jedes Element nicht mehr als eine konstante Anzahl von berühren mal!

Dies war ein bisschen kurz (!) und wenn Sie weitere Informationen benötigen, lassen Sie es mich wissen.

    
Aryabhatta 17.06.2010, 16:38
quelle
3

Was Sie haben, ist eine N X 2-Matrix, die in einem eindimensionalen Array dargestellt ist, das in ein 2 X N-Array transponiert werden soll.

Zum Beispiel Ihre Liste:

a 1 , <1>, a 2, b2, ... a n , b

kann genauso gut als Matrix dargestellt werden:

x 1,1 , x 1,2, x2,1, x2,2 , ... x n, 1, x , 2

Was du transponieren willst:

x 1,1 , x <2,1>, ... xn, 1, x1,2 , x 2,2 <, ... x n, 2

Der In-Place-Matrix-Transpositionsalgorithmus erledigt die Aufgabe.

BEARBEITEN

Ok, lass es mich buchstabieren. Probieren Sie das folgende Bit aus:

%Vor%

Dadurch werden die Array-Elemente ausgedruckt, die ausgetauscht werden müssen. Dies funktioniert für beliebig große Matrizen in einem linearen Array, in dem die Elemente nach Spalten und Zeilen angeordnet sind. Mit einem N X 2 Martix wären die Elemente wie folgt angeordnet:

x 1,1 , x 1,2, x2,1, x2,2 , ... x n, 1, x , 2

Der Algorithmus gibt die Elemente aus, die getauscht werden müssen, um ein Array in der folgenden Reihenfolge zu erhalten:

x 1,1 , x <2,1>, ... xn, 1, x1,2 , x 2,2 <, ... x n, 2

Beginnen Sie zum Beispiel mit r = 4, c = 2 und dem Array:

%Vor%

Erfordert die folgenden Swaps:

%Vor%

wird:

%Vor%

Dieser Algorithmus ist sowohl räumlich als auch zeitlich effizient.

Groß-O

Mein O-foo ist nicht großartig, aber ich werde es versuchen ...

Die Matrix enthält 2 Spalten ('A' und 'B') und 'M' Zeilen. Um dies als darzustellen ein lineares Array benötigen wir 2M Elemente. Rufen wir diese Zahl N (die Größe des linearen Arrays) auf.

Der Algorithmus hat zwei Iterationsschleifen, eine für 'r' (Zeilen) und eine für 'c' (Spalten). Gesamte Iterationen sind dann r * c, was in unserem Fall auf 2M = N kommt. So weit so gut.

Der Platzhalter ist die innere DO WHILE -Schleife. Wie viele Iterationen benötigt es für eine bestimmte Anzahl von Zeilen? Die Antwort könnte sein: Ziemlich viele. Basierend auf einigen empirischen Ergebnissen (siehe unten) sieht es aus wie die Anzahl von DO WHILE Iterationen ist eine komplexe Funktion mit 'r', wenn 'c' = 2 (oder wahrscheinlich ein beliebiger Wert von 'c'). Ich habe nicht genug O-Foo, um genau herauszufinden, was diese Funktion ist. Es sollte jedoch nicht schlechter werden als N 3 <(ein vollständiger Durchlauf durch die Matrix, N 2 ) jedes Element, N). Kein gutes Bild - in der Theorie. Also ich denke, das macht es O (N 3 )? Das mag sein ein Nicht-O (N) -Algorithmus, scheint aber in der Praxis bei den Bits von empirische Daten unten. Ich bin an diesem Punkt irgendwie verloren - Kommentare willkommen!

Eine Beobachtung über die DO WHILE -Schleife: Sie verwendet Ganzzahl-basierte Mathematik für einfache Variablen (kein Array Referenzen erforderlich). Wenn du nichtlinear gehst, hat es das der "billigste" Ort dafür zu sein!

Wie viele Swaps werden benötigt? Die Anzahl der Swaps ist auf eins pro Iteration durch das Äußere beschränkt zwei Schleifen, die höchstens N mal ist. Die Anzahl der Swaps entspricht einer O (N) -Leistung.

Meine Vermutung ist, dass dies ein Nicht-O (N) -Algorithmus ist, aber er scheint ein vernünftiges Verhalten zu haben für 2 Spaltenmatrizen mittlerer Größe.

Hier sind einige empirische Ergebnisse für verschiedene 2-Säulen-Matrixgrößen:

%Vor%

Die Anzahl der Schleifen pro Zeile wächst mit der Anzahl der Zeilen, aber nicht mit einer alarmierenden Rate. Die Gefahr besteht darin, einen "süßen" Punkt zu treffen, wo es exponentiell wird - aber ich weiß nicht, ob es wirklich dieses Potenzial hat.

Mein Rat an Mgccl wäre, diesen Algorithmus über den Bereich der Zeilenanzahl zu benchmarken typisch in seiner Anwendung dann entscheiden, ob es eine akzeptable Leistung im Vergleich zu anderen ergeben würde Algorithmus-Benchmarks. Big-O-Analyse ist interessant, aber die Ergebnisse über einen operativen Bereich von Daten zählen.

Letzter Tritt in der Dose: Warum funktioniert dieser Algorithmus?

Transponiere eine in linearer Form dargestellte Matrix:

Gegebene Matrix X mit M Zeilen und N Spalten.

Legen Sie X in einer Reihenanordnung in Zeilenreihenfolge aus. Das Das Array wird wie folgt organisiert:

%Vor%

Eine Notation, die jedes Element im linearen Array beschreibt ist:

%Vor%

Mit dieser Notation Ein Array in der Reihenfolge der Zeilen kann wie folgt generiert werden:

%Vor%

Beachten Sie, dass die angegebenen Werte für j (Zeile) und k (Spalte) wie folgt berechnet werden können:

%Vor%

Die Transponierte der Matrix X wird durch den Austausch der Elemente konstruiert X [i, r, c] mit X [t, c, r] über den Bereich von r und c. Im Wesentlichen tauschen wir uns aus Alle Zeilenvariablen für Spaltenvariablen.Verwenden der Notation Wie oben beschrieben, führt dies zum Austausch linearer Array-Elemente:

%Vor%

Die Anzahl der Austauschvorgänge, die für die Umsetzung der Matrix erforderlich sind, beträgt weniger als M * N, weil höchstens ein Tausch erforderlich ist, um ein Element richtig zu platzieren Position. In einigen Fällen ist ein Austausch nicht erforderlich, da die Element ist bereits vorhanden. Zum Beispiel das erste und letzte Element von X Niemals tauschen müssen.

Durch das lineare Array durch Erhöhung von i wissen wir, dass solange keiner der Austausche beinhaltet Elemente, wo ich & gt; t, wird die Matrix sein Großordnung der Spalte für alle Elemente mit Indizes kleiner oder gleich i.

Immer wenn ich & gt; t bedeutet dies, dass ein vorheriger Austausch bei Index t stattgefunden hat. Das Element bei t wurde wie oben beschrieben ausgetauscht, indem es platziert wurde an einer neuen Position t '. Gegeben ein Index t, können wir den Hauptindex t 'berechnen sowie die Zeilen- und Spaltennummern wie folgt:

%Vor%

Wenn t 'kleiner als i ist, bedeutet das wiederum, dass auch dieses Element ausgetauscht wurde, und wir müssen es tun fahre mit einer weiteren Berechnungsrunde fort. Setze t auf das berechnete t 'und wiederhole es. Irgendwann werde ich & lt; = t und der Austausch kann getan werden. Im Grunde "jagen" wir das Element durch alle seine vorheriger Austausch, bis wir ihn bei i oder rechts von i im linearen finden Array.

Wiederholen Sie diesen Zyklus für jedes Element im linearen Array und die Matrix wird haben wurde transponiert.

    
NealB 17.06.2010 18:44
quelle
0

O (1) Zeit, O (1) Zusätzlicher Platz

%Vor%

Ausgabe:

%Vor%     
andand 17.06.2010 17:38
quelle
0

Nun, schauen wir uns dieses Beispiel an:

0 1 2 3 4 5 6 7 8 9 10

0 2 4 6 8 10 1 3 5 7 9

Die erste Hälfte des Ergebnisses enthält Elemente, deren Indizes i / 2 des ursprünglichen Index i sind, während die andere Hälfte i - n / 2 + 1 ist, wobei n die Anzahl der Elemente im Array ist.

>

Dies ist im Grunde ein Sonderfall von Sortieralgorithmen, nur mit einer speziellen Reihenfolge.

Hier ist eine Idee, einen Bubblesort-Algorithmus zu verwenden, um ein Array von ganzen Zahlen zu sortieren. Was wir brauchen, ist, die geraden Werte an den Anfang zu ziehen und dabei ungerade Werte zurückzulassen:

%Vor%

Dies funktioniert jedoch nur, wenn Sie die Indizes beibehalten können (d. h. in diesem Fall korrelieren Indizes mit Array-Werten). Und die Leistung von bubblesort ist O (n ^ 2) und die Speicherbelegung ist 1.

Es kann nicht in O (n) Zeit und 1 Speicher durchgeführt werden [EDIT: Verwenden Sie nur generische Sortieralgorithmen!], die besten generischen Sortieralgorithmen funktionieren in O (nlog n):

Ссылка

Um Ihr Problem zu lösen, wäre der beste Weg, ein Array von Indizes zu generieren, die Ihrem Kriterium entsprechen:

%Vor%

und verwenden Sie es dann einfach, um die Elemente im ursprünglichen Array an neue Positionen zuzuordnen:

%Vor%

Dieses Ding würde in O (n) laufen, würde aber auch O (n) Speicher benötigen. Aber wenn die Größe von Type viel größer als sizeof int ist, ist es immer noch eine Erhöhung der Speichernutzung (im Gegensatz zum Kopieren von Type-Objekten).

[EDIT2:]

Anstelle des netten OOP-Codes andand did, bei dem Sie auch den Originalvektor in die Instanz Ihrer Klasse kopieren müssen, können Sie einfach eine Funktion schreiben:

%Vor%

und verwenden Sie das, wann immer Sie den permutierten Wert benötigen. In der Tat erfordert dies nur O (n) Zeit, da permuted_index dann nicht mehr als n-mal für den gegebenen Vektor (zumindest in einer vollständigen "für jede" Schleife) ausgewertet wird.

    
Andrei Sosnin 17.06.2010 16:27
quelle

Tags und Links