Lorsque nous trions une liste, comme
a = [1,2,3,3,2,2,1]
sorted(a) => [1, 1, 2, 2, 2, 3, 3]
les éléments égaux sont toujours adjacents dans la liste résultante.
Comment puis-je accomplir la tâche opposée - mélanger la liste afin que des éléments égaux ne soient jamais (ou aussi rarement que possible) adjacents?
Par exemple, pour la liste ci-dessus, l'une des solutions possibles est
p = [1,3,2,3,2,1,2]
Plus formellement, à partir d'une liste a
, générez-en une permutation p
qui minimise le nombre de paires p[i]==p[i+1]
.
Comme les listes sont volumineuses, générer et filtrer toutes les permutations n'est pas une option.
Question bonus: comment générer efficacement toutes ces permutations?
C'est le code que j'utilise pour tester les solutions: https://gist.github.com/gebrkn/9f550094b3d24a35aebd
UPD: Choisir un gagnant ici était un choix difficile, car de nombreuses personnes ont publié d'excellentes réponses. @VincentvanderWeele , @David Eisenstat , @Coady , @ enrico.bacis et @srgerg ont fourni des fonctions qui génèrent parfaitement la meilleure permutation possible. @tobias_k et David ont également répondu à la question bonus (générer toutes les permutations). Points supplémentaires à David pour la preuve d'exactitude.
Le code de @VincentvanderWeele semble être le plus rapide.
[1, 2, 1, 3, 1, 4, 1, 5]
est exactement le même que[1, 3, 1, 2, 1, 4, 1, 5]
par votre critère?[1, 1, 1, ..., 2, 3, 4, ..., N]
avec des2N
éléments. Vous pouvez mettre un nombren > 1
entre chaque paire de consécutives1
pour obtenir une bonne permutation. Ensuite, vous permutez lesN/2
éléments et obtenez toutes les permutations valides (ce qui signifie qu'aucune n'est mauvaise, mais il peut y en avoir plus). Le nombre de ces permutations est O (N ^ 2), vous ne pouvez donc pas faire mieux que O (N ^ 2). Encore mieux que O (N ^ 3) de l'approche naïve cependant.Réponses:
Cela va dans le sens du pseudocode actuellement incomplet de Thijser. L'idée est de prendre le plus fréquent des types d'éléments restants, sauf s'il vient d'être pris. (Voir aussi l'implémentation de cet algorithme par Coady .)
Preuve d'exactitude
Pour deux types d'items, avec des comptages k1 et k2, la solution optimale a k2 - k1 - 1 défauts si k1 <k2, 0 défaut si k1 = k2, et k1 - k2 - 1 défauts si k1> k2. Le cas = est évident. Les autres sont symétriques; chaque instance de l'élément minoritaire empêche au plus deux défauts sur un total de k1 + k2 - 1 possible.
Cet algorithme gourmand renvoie des solutions optimales, par la logique suivante. Nous appelons un préfixe (solution partielle) sûr s'il s'étend à une solution optimale. Il est clair que le préfixe vide est sûr, et si un préfixe sûr est une solution complète, cette solution est optimale. Il suffit de montrer de manière inductive que chaque pas gourmand maintient la sécurité.
La seule façon pour une étape gourmande d'introduire un défaut est s'il ne reste qu'un seul type d'élément, auquel cas il n'y a qu'une seule façon de continuer, et cette façon est sûre. Sinon, soit P le préfixe (sûr) juste avant l'étape considérée, soit P 'le préfixe juste après, et soit S une solution optimale étendant P. Si S étend P' aussi, alors c'est fini. Sinon, soit P '= Px et S = PQ et Q = yQ', où x et y sont des éléments et Q et Q 'sont des séquences.
Supposons d'abord que P ne se termine pas par y. Par le choix de l'algorithme, x est au moins aussi fréquent dans Q que y. Considérons les sous-chaînes maximales de Q contenant uniquement x et y. Si la première sous-chaîne a au moins autant de x que de y, elle peut être réécrite sans introduire de défauts supplémentaires pour commencer par x. Si la première sous-chaîne a plus de y que de x, alors une autre sous-chaîne a plus de x que de y, et nous pouvons réécrire ces sous-chaînes sans défauts supplémentaires afin que x passe en premier. Dans les deux cas, on trouve une solution optimale T qui étend P ', selon les besoins.
Supposons maintenant que P finisse par y. Modifiez Q en déplaçant la première occurrence de x vers l'avant. Ce faisant, nous introduisons au plus un défaut (où x était auparavant) et éliminons un défaut (le yy).
Générer toutes les solutions
C'est la réponse de tobias_k plus des tests efficaces pour détecter quand le choix actuellement considéré est globalement contraint d'une manière ou d'une autre. Le temps de fonctionnement asymptotique est optimal, puisque le surcoût de génération est de l'ordre de la longueur de la sortie. Le retard dans le pire des cas est malheureusement quadratique; il pourrait être réduit à linéaire (optimal) avec de meilleures structures de données.
la source
Pseudocode:
Vous ne l'aurez que
p[i]==p[i+1]
si plus de la moitié de l'entrée est constituée du même élément, auquel cas il n'y a pas d'autre choix que de placer le même élément à des endroits consécutifs (selon le principe du trou pidgeon).Comme indiqué dans les commentaires, cette approche peut avoir un conflit de trop au cas où l'un des éléments se produirait au moins
n/2
fois (oun/2+1
pour impairn
; cela se généralise à la(n+1)/2)
fois pour pair et impair). Il y a au plus deux de ces éléments et s'il y en a deux, l'algorithme fonctionne très bien. Le seul cas problématique est celui où un élément se produit au moins la moitié du temps. Nous pouvons simplement résoudre ce problème en trouvant l'élément et en le traitant en premier.Je ne connais pas assez python pour écrire cela correctement, alors j'ai pris la liberté de copier l'implémentation de l'OP d'une version précédente de github:
la source
[0, 1, 1]
ou[0, 0, 1]
, selon que vous utilisez des index de base 0 ou 1.L'algorithme déjà donné de prendre l'élément le plus courant à gauche qui n'est pas l'élément précédent est correct. Voici une implémentation simple, qui utilise de manière optimale un tas pour suivre les plus courantes.
la source
Vous pouvez générer toutes les permutations «parfaitement non triées» (qui n'ont pas deux éléments égaux dans des positions adjacentes) en utilisant un algorithme de retour arrière récursif. En fait, la seule différence pour générer toutes les permutations est que vous gardez une trace du dernier nombre et excluez certaines solutions en conséquence:
Notez que sous cette forme la fonction n'est pas très efficace, car elle crée de nombreuses sous-listes. De plus, nous pouvons l'accélérer en examinant d'abord les nombres les plus contraints (ceux qui ont le plus grand nombre). Voici une version beaucoup plus efficace utilisant uniquement
counts
les chiffres.Vous pouvez l'utiliser pour générer juste la
next
permutation parfaite, ou pour leslist
maintenir toutes. Mais notez que s'il n'y a pas de permutation parfaitement non triée, alors ce générateur ne donnera par conséquent aucun résultat.Pour contourner ce problème, vous pouvez l'utiliser avec l'un des algorithmes proposés dans les autres réponses comme solution de secours. Cela garantira de retourner une permutation parfaitement non triée, s'il y en a une, ou une bonne approximation dans le cas contraire.
la source
T(n+1) = something + T(n)
.next(unsort2(collections.Counter(a)))
;-) Mais puisque cet algo génère toutes les possibilités, pourquoi ne pas les vérifier toutes? Son seulement 38 pour cette liste de test à 7 éléments.En python, vous pouvez faire ce qui suit.
Considérez que vous avez une liste triée
l
, vous pouvez faire:Ce ne sont que des opérations en place et devraient donc être assez rapides (
O(N)
). Notez que vous passerez del[i] == l[i+1]
àl[i] == l[i+2]
afin que l'ordre dans lequel vous vous retrouvez soit tout sauf aléatoire, mais d'après ma compréhension de la question, ce n'est pas le hasard que vous recherchez.L'idée est de diviser la liste triée au milieu puis d'échanger tous les autres éléments dans les deux parties.
Car
l= [1, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5]
cela conduit àl = [3, 1, 4, 2, 5, 1, 3, 1, 4, 2, 5]
La méthode ne parvient pas à se débarrasser de tous les
l[i] == l[i + 1]
dès que l'abondance d'un élément est supérieure ou égale à la moitié de la longueur de la liste.Bien que ce qui précède fonctionne bien tant que l'abondance de l'élément le plus fréquent est inférieure à la moitié de la taille de la liste, la fonction suivante gère également les cas limites (le fameux problème off-by-one) où tous les autres éléments commençant par le le premier doit être le plus abondant:
la source
[3, 2, 1, 2, 1, 3, 2]
(retourne[2, 1, 3, 1, 2, 2, 3]
, devrait être(3, 2, 1, 2, 1, 3, 2)
) - voir l'essentiel+1
. Réessayez maintenant.[1, 3, 3, 3, 3, 1, 1]
=>[3, 1, 3, 3, 1, 3, 1]
Voici un bon algorithme:
Tout d'abord, comptez pour tous les nombres la fréquence à laquelle ils se produisent. Placez la réponse sur une carte.
trier cette carte de sorte que les nombres les plus fréquents viennent en premier.
Le premier chiffre de votre réponse est le premier chiffre de la carte triée.
Resort la carte avec le premier étant maintenant un plus petit.
Si vous souhaitez améliorer l'efficacité, recherchez des moyens d'augmenter l'efficacité de l'étape de tri.
la source
En réponse à la question bonus: il s'agit d'un algorithme qui trouve toutes les permutations d'un ensemble où aucun élément adjacent ne peut être identique. Je pense que c'est l'algorithme le plus efficace du point de vue conceptuel (bien que d'autres puissent être plus rapides en pratique car ils se traduisent par un code plus simple). Il n'utilise pas la force brute, il ne génère que des permutations uniques et les chemins ne menant pas à des solutions sont coupés au plus tôt.
J'utiliserai le terme «élément abondant» pour un élément dans un ensemble qui apparaît plus souvent que tous les autres éléments combinés, et le terme «abondance» pour le nombre d'éléments abondants moins le nombre d'autres éléments.
par exemple, l'ensemble
abac
n'a pas d'élément abondant, les ensemblesabaca
etaabcaa
onta
comme élément abondant, et l'abondance 1 et 2 respectivement.Cet algorithme génère des permutations uniques. Si vous voulez connaître le nombre total de permutations (où
aba
est compté deux fois car vous pouvez changer les a), multipliez le nombre de permutations uniques par un facteur:où N est le nombre d'occurrences de chaque élément de l'ensemble. Pour un ensemble
abcdabcaba
ce serait 4! * 3! * 2! * 1! ou 288, qui démontre à quel point un algorithme est inefficace qui génère toutes les permutations au lieu de seulement les uniques. Pour lister toutes les permutations dans ce cas, listez simplement les permutations uniques 288 fois :-)Voici une implémentation (plutôt maladroite) en Javascript; Je soupçonne qu'un langage comme Python peut être mieux adapté à ce genre de choses. Exécutez l'extrait de code pour calculer les permutations séparées de "abracadabra".
la source
L'idée est de trier les éléments du plus commun au moins commun, de prendre le plus commun, de diminuer son nombre et de le remettre dans la liste en gardant l'ordre décroissant (mais en évitant de mettre le dernier élément utilisé en premier pour éviter les répétitions lorsque cela est possible) .
Cela peut être implémenté en utilisant
Counter
etbisect
:Exemple
la source
[1, 1, 2, 3]
où il existe des solutions telles que[1, 2, 1, 3]
.[1, 2, 3, 2, 3, 2, 2]
il renvoie[2, 3, 1, 2, 3, 2, 2]
(1 faute), alors que l'idéal est(2, 1, 2, 3, 2, 3, 2)
) - voir l'essentiel.Il donnera le minimum d'éléments de la liste à leur emplacement d'origine (par valeur d'élément) afin qu'il essaie, pour votre exemple, de mettre les 1, 2 et 3 loin de leurs positions triées.
la source
best_shuffle
et cela a généré[1,1,1,2,3] -> [3, 1, 2, 1, 1]
- pas idéal!Commencez par la liste triée de longueur n. Soit m = n / 2. Prenez les valeurs à 0, puis m, puis 1, puis m + 1, puis 2, puis m + 2, et ainsi de suite. À moins que vous n'ayez plus de la moitié des nombres identiques, vous n'obtiendrez jamais des valeurs équivalentes dans un ordre consécutif.
la source
Veuillez pardonner ma réponse de style "moi aussi", mais la réponse de Coady ne pourrait-elle pas être simplifiée?
Edit: Voici une version de python 2 qui renvoie une liste:
la source
la source