J'ai besoin de calculer la médiane en cours d'exécution:
Entrée: , , vecteur .k ( x 1 , x 2 , … , x n )
Sortie: vecteur , où est la médiane de .y i ( x i , x i + 1 , … , x i + k - 1 )
(Pas de tricherie avec des approximations; je voudrais avoir des solutions exactes. Les éléments sont de grands entiers.)
Il existe un algorithme trivial qui maintient un arbre de recherche de taille ; la durée totale d'exécution est . (Ici, un "arbre de recherche" fait référence à une structure de données efficace qui prend en charge les insertions, les suppressions et les requêtes médianes en temps logarithmique.)
Cependant, cela me semble un peu stupide. Nous apprendrons efficacement toutes les statistiques de commande dans toutes les fenêtres de taille , pas seulement les médianes. De plus, cela n'est pas trop attrayant dans la pratique, surtout si est grand (les grands arbres de recherche ont tendance à être lents, la surcharge de consommation de mémoire n'est pas anodine, l'efficacité du cache est souvent médiocre, etc.).
Pouvons-nous faire quelque chose de nettement mieux?
Y a-t-il des limites inférieures (par exemple, l'algorithme trivial est-il asymptotiquement optimal pour le modèle de comparaison)?
Edit: David Eppstein a donné une belle borne inférieure pour le modèle de comparaison! Je me demande s'il est néanmoins possible de faire quelque chose d'un peu plus intelligent que l'algorithme trivial?
Par exemple, pourrions-nous faire quelque chose dans ce sens: diviser le vecteur d'entrée en parties de taille ; trier chaque partie (en gardant une trace des positions d'origine de chaque élément); puis utiliser le vecteur trié par morceaux pour trouver efficacement les médianes en cours d'exécution sans structures de données auxiliaires? Bien sûr, ce serait toujours , mais dans la pratique, le tri des tableaux a tendance à être beaucoup plus rapide que la maintenance des arbres de recherche.
Edit 2: Saeed voulait voir quelques raisons pour lesquelles je pense que le tri est plus rapide que les opérations d'arbre de recherche. Voici des repères très rapides, pour , :
- ≈ 8s: tri de vecteurs avec éléments chacun
- ≈ 10s: trier un vecteur avec éléments
- ≈ Années 80: insertions et suppressions dans une table de hachage de taille
- ≈ 390s: insertions et suppressions dans un arbre de recherche équilibré de taille
La table de hachage est là juste pour comparaison; il n'est d'aucune utilité directe dans cette application.
En résumé, nous avons presque un facteur 50 de différence dans les performances du tri par rapport aux opérations d'arborescence de recherche équilibrée. Et les choses empirent si nous augmentons .
(Détails techniques: Données = nombres entiers aléatoires de 32 bits. Ordinateur = un ordinateur portable moderne typique. Le code de test a été écrit en C ++, en utilisant les routines de bibliothèque standard (std :: sort) et les structures de données (std :: multiset, std :: unsorted_multiset). J'ai utilisé deux compilateurs C ++ différents (GCC et Clang), et deux implémentations différentes de la bibliothèque standard (libstdc ++ et libc ++). Traditionnellement, std :: multiset a été implémenté comme un arbre rouge-noir hautement optimisé.)