Trouver la médiane en O(n log n)
- La méthode la plus simple consiste à trier la liste puis à choisir la médiane
- La complexité temporelle d’un tri par comparaison est de
O(n log n) - Exemple de code :
def nlogn_median(l): l = sorted(l) if len(l) % 2 == 1: return l[len(l) // 2] else: return 0.5 * (l[len(l) // 2 - 1] + l[len(l) // 2])
Trouver la médiane en O(n) en moyenne
-
On peut trouver la médiane en temps linéaire en moyenne en utilisant l’algorithme de "quickselect"
-
Algorithme récursif développé par Tony Hoare
-
Procédure :
- Choisir un index aléatoire dans la liste et le définir comme pivot
- Diviser la liste en deux groupes autour du pivot :
- les éléments inférieurs ou égaux au pivot
- les éléments supérieurs au pivot
- Explorer récursivement le groupe qui contient la médiane
-
Exemple de code :
import random def quickselect_median(l, pivot_fn=random.choice): if len(l) % 2 == 1: return quickselect(l, len(l) // 2, pivot_fn) else: return 0.5 * (quickselect(l, len(l) // 2 - 1, pivot_fn) + quickselect(l, len(l) // 2, pivot_fn)) def quickselect(l, k, pivot_fn): if len(l) == 1: assert k == 0 return l[0] pivot = pivot_fn(l) lows = [el for el in l if el < pivot] highs = [el for el in l if el > pivot] pivots = [el for el in l if el == pivot] if k < len(lows): return quickselect(lows, k, pivot_fn) elif k < len(lows) + len(pivots): return pivots[0] else: return quickselect(highs, k - len(lows) - len(pivots), pivot_fn)
Preuve du O(n) en moyenne
- Si le pivot coupe la liste à peu près en deux, chaque appel récursif travaille sur la moitié des données de l’étape précédente
- Preuve mathématique :
C = n + n/2 + n/4 + n/8 + ... = 2n = O(n)
O(n) déterministe
-
Garantit un temps linéaire même dans le pire des cas
-
Sélectionne le pivot avec l’algorithme de "median-of-medians"
-
Procédure :
- Diviser la liste en groupes de 5
- Trier chaque groupe et choisir sa médiane
- Choisir comme pivot la médiane des médianes
-
Exemple de code :
def pick_pivot(l): assert len(l) > 0 if len(l) < 5: return nlogn_median(l) chunks = chunked(l, 5) full_chunks = [chunk for chunk in chunks if len(chunk) == 5] sorted_groups = [sorted(chunk) for chunk in full_chunks] medians = [chunk[2] for chunk in sorted_groups] median_of_medians = quickselect_median(medians, pick_pivot) return median_of_medians def chunked(l, chunk_size): return [l[i:i + chunk_size] for i in range(0, len(l), chunk_size)]
Résumé
- L’algorithme quickselect peut trouver la médiane en temps linéaire s’il choisit un pivot approprié
- L’algorithme median-of-medians est une méthode de sélection du pivot qui garantit un temps linéaire même dans le pire des cas
- En pratique, un choix de pivot aléatoire est généralement suffisamment efficace
Le résumé de GN⁺
- Cet article explique différents algorithmes pour trouver la médiane, en particulier les méthodes qui la trouvent en temps linéaire
- L’algorithme quickselect est rapide en moyenne, mais on peut utiliser l’algorithme median-of-medians pour se prémunir contre le pire des cas
- Dans les applications réelles, un choix de pivot aléatoire est le plus souvent suffisamment efficace
- Un algorithme similaire est introselect, utilisé dans la bibliothèque standard C++
1 commentaires
Commentaires Hacker News