Wenn wir eine Liste sortieren, wie
a = [1,2,3,3,2,2,1]
sorted(a) => [1, 1, 2, 2, 2, 3, 3]
Gleiche Elemente sind in der resultierenden Liste immer benachbart.
Wie kann ich die entgegengesetzte Aufgabe erreichen - die Liste so mischen, dass gleiche Elemente niemals (oder so selten wie möglich) benachbart sind?
Für die obige Liste ist beispielsweise eine der möglichen Lösungen
p = [1,3,2,3,2,1,2]
Wenn eine Liste gegeben ist a
, generieren Sie formeller eine Permutation p
davon, die die Anzahl der Paare minimiert p[i]==p[i+1]
.
Da die Listen groß sind, ist das Generieren und Filtern aller Permutationen keine Option.
Bonusfrage: Wie lassen sich all diese Permutationen effizient generieren?
Dies ist der Code, mit dem ich die Lösungen teste : https://gist.github.com/gebrkn/9f550094b3d24a35aebd
UPD: Die Auswahl eines Gewinners hier war eine schwierige Entscheidung, da viele Leute hervorragende Antworten gepostet haben. @VincentvanderWeele , @David Eisenstat , @Coady , @ enrico.bacis und @srgerg stellten Funktionen bereit, die fehlerfrei die bestmögliche Permutation erzeugen. @tobias_k und David beantworteten auch die Bonusfrage (generiere alle Permutationen). Zusätzliche Punkte an David für den Korrektheitsnachweis.
Der Code von @VincentvanderWeele scheint der schnellste zu sein.
quelle
[1, 2, 1, 3, 1, 4, 1, 5]
ist genau das gleiche wie[1, 3, 1, 2, 1, 4, 1, 5]
nach deinem kriterium?[1, 1, 1, ..., 2, 3, 4, ..., N]
mit2N
Elementen. Sie können eine Zahln > 1
zwischen jedes aufeinanderfolgende Paar setzen1
, um eine gute Permutation zu erhalten. Dann permutieren Sie dieN/2
Elemente und erhalten alle gültigen Permutationen (was bedeutet, dass keine schlecht ist, aber es kann mehr geben). Die Anzahl solcher Permutationen ist O (N ^ 2), daher können Sie es nicht besser machen als O (N ^ 2). Immer noch besser als O (N ^ 3) des naiven Ansatzes.Antworten:
Dies entspricht dem derzeit unvollständigen Pseudocode von Thijser. Die Idee ist, den häufigsten der verbleibenden Artikeltypen zu verwenden, es sei denn, er wurde gerade genommen. (Siehe auch Coadys Implementierung dieses Algorithmus.)
Korrektheitsnachweis
Für zwei Elementtypen mit den Zählungen k1 und k2 weist die optimale Lösung k2 - k1 - 1 Fehler auf, wenn k1 <k2, 0 Fehler, wenn k1 = k2, und k1 - k2 - 1 Fehler, wenn k1> k2. Der Fall = ist offensichtlich. Die anderen sind symmetrisch; Jede Instanz des Minderheitselements verhindert höchstens zwei Fehler von insgesamt k1 + k2 - 1 möglich.
Dieser gierige Algorithmus liefert nach der folgenden Logik optimale Lösungen. Wir nennen ein Präfix (Teillösung) sicher, wenn es sich um eine optimale Lösung handelt. Das leere Präfix ist eindeutig sicher, und wenn ein sicheres Präfix eine vollständige Lösung ist, ist diese Lösung optimal. Es genügt, induktiv zu zeigen, dass jeder gierige Schritt die Sicherheit aufrechterhält.
Der einzige Weg, wie ein gieriger Schritt einen Fehler einführt, besteht darin, dass nur noch ein Elementtyp übrig bleibt. In diesem Fall gibt es nur einen Weg, um fortzufahren, und dieser Weg ist sicher. Andernfalls sei P das (sichere) Präfix unmittelbar vor dem betrachteten Schritt, sei P 'das Präfix unmittelbar danach und sei S eine optimale Lösung, die P erweitert. Wenn S auch P' erweitert, sind wir fertig. Andernfalls sei P '= Px und S = PQ und Q = yQ', wobei x und y Elemente und Q und Q 'Sequenzen sind.
Angenommen, P endet nicht mit y. Nach Wahl des Algorithmus ist x in Q mindestens so häufig wie y. Betrachten Sie die maximalen Teilzeichenfolgen von Q, die nur x und y enthalten. Wenn der erste Teilstring mindestens so viele x wie y hat, kann er umgeschrieben werden, ohne zunächst zusätzliche Fehler einzuführen, beginnend mit x. Wenn die erste Teilzeichenfolge mehr y als x hat, hat eine andere Teilzeichenfolge mehr x als y, und wir können diese Teilzeichenfolgen ohne zusätzliche Fehler neu schreiben, sodass x zuerst geht. In beiden Fällen finden wir eine optimale Lösung T, die P 'nach Bedarf erweitert.
Angenommen, P endet jetzt mit y. Ändern Sie Q, indem Sie das erste Vorkommen von x nach vorne verschieben. Dabei führen wir höchstens einen Defekt ein (wo x früher war) und beseitigen einen Defekt (das yy).
Alle Lösungen generieren
Dies ist die Antwort von tobias_k sowie effiziente Tests, um festzustellen, wann die derzeit in Betracht gezogene Auswahl in irgendeiner Weise global eingeschränkt ist. Die asymptotische Laufzeit ist optimal, da der Overhead der Erzeugung in der Größenordnung der Länge der Ausgabe liegt. Die Verzögerung im schlimmsten Fall ist leider quadratisch; es könnte mit besseren Datenstrukturen auf linear (optimal) reduziert werden.
quelle
Pseudocode:
Sie haben nur dann,
p[i]==p[i+1]
wenn mehr als die Hälfte der Eingabe aus demselben Element besteht. In diesem Fall gibt es keine andere Wahl, als dasselbe Element an aufeinanderfolgenden Stellen zu platzieren (nach dem Pidgeon-Hole-Prinzip).Wie in den Kommentaren ausgeführt, kann dieser Ansatz einen Konflikt zu viele haben, falls eines der Elemente mindestens
n/2
einmal auftritt (odern/2+1
für ungeraden
; dies gilt(n+1)/2)
sowohl für gerade als auch für ungerade). Es gibt höchstens zwei solcher Elemente, und wenn es zwei gibt, funktioniert der Algorithmus einwandfrei. Der einzige problematische Fall ist, wenn es ein Element gibt, das mindestens die Hälfte der Zeit auftritt. Wir können dieses Problem einfach lösen, indem wir das Element finden und es zuerst behandeln.Ich weiß nicht genug über Python, um dies richtig zu schreiben, daher habe ich mir erlaubt, die Implementierung einer früheren Version von github durch das OP zu kopieren:
quelle
[0, 1, 1]
oder fehl[0, 0, 1]
, je nachdem, ob Sie 0-basierte oder 1-basierte Indizes verwenden.Der bereits angegebene Algorithmus zum Aufnehmen des am häufigsten verwendeten Elements, das nicht das vorherige Element ist, ist korrekt. Hier ist eine einfache Implementierung, bei der ein Heap optimal verwendet wird, um die häufigsten zu verfolgen.
quelle
Mit einem rekursiven Backtracking-Algorithmus können Sie alle 'perfekt unsortierten' Permutationen (die keine zwei gleichen Elemente an benachbarten Positionen haben) generieren . Der einzige Unterschied zum Generieren aller Permutationen besteht darin, dass Sie die letzte Zahl verfolgen und einige Lösungen entsprechend ausschließen:
Beachten Sie, dass die Funktion in dieser Form nicht sehr effizient ist, da sie viele Unterlisten erstellt. Wir können es auch beschleunigen, indem wir zuerst die am stärksten eingeschränkten Zahlen betrachten (diejenigen mit der höchsten Anzahl). Hier ist eine viel effizientere Version, bei der nur
counts
die Zahlen verwendet werden.Sie können dies verwenden, um nur die
next
perfekte Permutation zu generieren oderlist
um alle zu halten. Beachten Sie jedoch, dass dieser Generator keine Ergebnisse liefert , wenn keine perfekt unsortierte Permutation vorliegt .Um dieses Problem zu umgehen, können Sie dies zusammen mit einem der in den anderen Antworten vorgeschlagenen Algorithmen als Fallback verwenden. Dies garantiert, dass eine perfekt unsortierte Permutation zurückgegeben wird, falls es eine gibt, oder eine gute Annäherung, falls nicht.
quelle
T(n+1) = something + T(n)
.next(unsort2(collections.Counter(a)))
;-) Aber da dieses Algo alle Möglichkeiten generiert, warum nicht alle überprüfen? Es sind nur 38 für diese 7-elementige Testliste.In Python können Sie Folgendes tun.
Angenommen, Sie haben eine sortierte Liste
l
, können Sie Folgendes tun:Diese sind nur an Ort und Stelle und sollten daher ziemlich schnell sein (
O(N)
). Beachten Sie, dass Sie vonl[i] == l[i+1]
nach wechseln ,l[i] == l[i+2]
sodass die Reihenfolge, in der Sie enden, alles andere als zufällig ist, aber nach meinem Verständnis der Frage ist es keine Zufälligkeit, nach der Sie suchen.Die Idee ist, die sortierte Liste in der Mitte aufzuteilen und dann jedes andere Element in den beiden Teilen auszutauschen.
Denn
l= [1, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5]
das führt zul = [3, 1, 4, 2, 5, 1, 3, 1, 4, 2, 5]
Die Methode kann nicht alle
l[i] == l[i + 1]
Elemente entfernen, sobald die Häufigkeit eines Elements größer oder gleich der Hälfte der Länge der Liste ist.Während das oben Genannte gut funktioniert, solange die Häufigkeit des häufigsten Elements kleiner als die Hälfte der Größe der Liste ist, behandelt die folgende Funktion auch die Grenzfälle (das berühmte Off-by-One-Problem), bei denen jedes andere Element mit dem beginnt Der erste muss der am häufigsten vorkommende sein:
quelle
[3, 2, 1, 2, 1, 3, 2]
(Rückkehr[2, 1, 3, 1, 2, 2, 3]
sollte sein(3, 2, 1, 2, 1, 3, 2)
) - das Wesentliche sehen+1
. Versuchen Sie es jetzt noch einmal.[1, 3, 3, 3, 3, 1, 1]
=>[3, 1, 3, 3, 1, 3, 1]
Hier ist ein guter Algorithmus:
Zählen Sie zunächst für alle Zahlen, wie oft sie vorkommen. Platziere die Antwort in einer Karte.
Sortieren Sie diese Karte so, dass die am häufigsten vorkommenden Zahlen an erster Stelle stehen.
Die erste Nummer Ihrer Antwort ist die erste Nummer in der sortierten Karte.
Resortieren Sie die Karte, wobei die erste jetzt eine kleinere ist.
Wenn Sie die Effizienz verbessern möchten, suchen Sie nach Möglichkeiten, um die Effizienz des Sortierschritts zu steigern.
quelle
Antwort auf die Bonusfrage: Dies ist ein Algorithmus, der alle Permutationen einer Menge findet, bei denen keine benachbarten Elemente identisch sein können. Ich glaube, dass dies konzeptionell der effizienteste Algorithmus ist (obwohl andere in der Praxis möglicherweise schneller sind, weil sie in einfacheren Code übersetzt werden). Es verwendet keine rohe Gewalt, es erzeugt nur eindeutige Permutationen, und Pfade, die nicht zu Lösungen führen, werden zum frühesten Zeitpunkt abgeschnitten.
Ich werde den Begriff "reichlich vorhandenes Element" für ein Element in einer Menge verwenden, das häufiger vorkommt als alle anderen Elemente zusammen, und den Begriff "Fülle" für die Anzahl der reichlich vorhandenen Elemente abzüglich der Anzahl anderer Elemente.
zB hat die Menge
abac
kein reichlich vorhandenes Element, die Mengenabaca
undaabcaa
habena
als reichlich vorhandenes Element und Fülle 1 bzw. 2.Dieser Algorithmus erzeugt eindeutige Permutationen. Wenn Sie die Gesamtzahl der Permutationen wissen möchten (wobei
aba
zweimal gezählt wird, weil Sie die a-Werte wechseln können), multiplizieren Sie die Anzahl der eindeutigen Permutationen mit einem Faktor:Dabei ist N die Anzahl der Vorkommen jedes Elements in der Menge. Für einen Satz
abcdabcaba
wäre dies 4! * 3! * 2! * 1! oder 288, was zeigt, wie ineffizient ein Algorithmus ist, der alle Permutationen anstelle nur der eindeutigen generiert. Um in diesem Fall alle Permutationen aufzulisten, listen Sie einfach die eindeutigen Permutationen 288 Mal auf :-)Unten finden Sie eine (ziemlich ungeschickte) Implementierung in Javascript. Ich vermute, dass eine Sprache wie Python für solche Dinge besser geeignet ist. Führen Sie das Code-Snippet aus, um die getrennten Permutationen von "Abrakadabra" zu berechnen.
quelle
Die Idee ist, die Elemente von den häufigsten zu den am wenigsten verbreiteten zu sortieren, die häufigsten zu nehmen, die Anzahl zu verringern und sie wieder in die Liste aufzunehmen, wobei die absteigende Reihenfolge beibehalten wird (wobei jedoch zu vermeiden ist, dass das zuletzt verwendete Element an erster Stelle steht, um Wiederholungen zu vermeiden, wenn dies möglich ist). .
Dies kann implementiert werden mit
Counter
undbisect
:Beispiel
quelle
[1, 1, 2, 3]
wenn es Lösungen gibt, wie z[1, 2, 1, 3]
.[1, 2, 3, 2, 3, 2, 2]
es zurückkehrt[2, 3, 1, 2, 3, 2, 2]
(1 Fehler), während das Ideal ist(2, 1, 2, 3, 2, 3, 2)
) - siehe das Wesentliche.Es wird das Minimum an Elementen aus der Liste an ihrer ursprünglichen Stelle angegeben (nach Elementwert), sodass beispielsweise versucht wird, die Einsen, Zweien und Dreien von ihren sortierten Positionen zu entfernen.
quelle
best_shuffle
und es generiert[1,1,1,2,3] -> [3, 1, 2, 1, 1]
- nicht ideal!Beginnen Sie mit der sortierten Liste der Länge n. Sei m = n / 2. Nehmen Sie die Werte bei 0, dann m, dann 1, dann m + 1, dann 2, dann m + 2 und so weiter. Wenn Sie nicht mehr als die Hälfte der Zahlen gleich haben, erhalten Sie niemals äquivalente Werte in aufeinanderfolgender Reihenfolge.
quelle
Bitte verzeihen Sie meine Antwort im "Ich auch" -Stil, aber könnte Coadys Antwort darauf nicht vereinfacht werden?
Bearbeiten: Hier ist eine Python 2-Version, die eine Liste zurückgibt:
quelle
quelle