Je cherche une sorte d'algorithme de "Domino sort" qui trie une liste d'éléments à deux côtés en fonction de la similarité des côtés "tangents" des éléments suivants.
Supposons la liste suivante où les éléments sont représentés par des 2-tuples :
>>> items
[(0.72, 0.12),
(0.11, 0.67),
(0.74, 0.65),
(0.32, 0.52),
(0.82, 0.43),
(0.94, 0.64),
(0.39, 0.95),
(0.01, 0.72),
(0.49, 0.41),
(0.27, 0.60)]
Le but est de trier cette liste de telle sorte que la somme des différences au carré des extrémités tangentes de chacun des deux éléments suivants (la perte) soit minimale :
>>> loss = sum(
... (items[i][1] - items[i+1][0])**2
... for i in range(len(items)-1)
... )
Dans l'exemple ci-dessus, ce calcul peut être effectué en passant en revue toutes les permutations possibles, mais pour les listes comportant un plus grand nombre d'éléments, cela devient rapidement irréalisable ( O(n!)
).
L'approche de la sélection de la meilleure correspondance étape par étape, telle que décrite ici
def compute_loss(items):
return sum((items[i][1] - items[i+1][0])**2 for i in range(len(items)-1))
def domino_sort(items):
best_attempt = items
best_score = compute_loss(best_attempt)
for i in range(len(items)):
copy = [x for x in items]
attempt = [copy.pop(i)]
for j in range(len(copy)):
copy = sorted(copy, key=lambda x: abs(x[0] - attempt[-1][1]))
attempt.append(copy.pop(0))
score = compute_loss(attempt)
if score < best_score:
best_attempt = attempt
best_score = score
return best_attempt, best_score
donne le résultat suivant avec une perte de 0.1381
:
[(0.01, 0.72),
(0.72, 0.12),
(0.11, 0.67),
(0.74, 0.65),
(0.49, 0.41),
(0.39, 0.95),
(0.94, 0.64),
(0.82, 0.43),
(0.32, 0.52),
(0.27, 0.6)]
Ce n'est cependant pas la meilleure solution qui serait
[(0.01, 0.72),
(0.82, 0.43),
(0.27, 0.6),
(0.49, 0.41),
(0.32, 0.52),
(0.39, 0.95),
(0.94, 0.64),
(0.72, 0.12),
(0.11, 0.67),
(0.74, 0.65)]
avec une perte de 0.0842
. De toute évidence, l'algorithme ci-dessus donne de bons résultats pour les premiers éléments, mais les différences pour les derniers éléments deviennent si importantes qu'elles dominent la perte.
Existe-t-il un algorithme capable d'effectuer ce type de tri dans un délai acceptable (réalisable pour des listes de plusieurs centaines d'éléments) ?
S'il n'est pas possible de faire ce genre de choses exactement en moins de O(n!)
existe-t-il des approches approximatives susceptibles de rapporter un bon score (faible perte) ?