begin process at 2008 05 16 21:54:53
1 173 770 membres
577 nouveaux aujourd'hui
13 973 membres club

Vous ne trouvez pas de réponse à votre problème ? Alors posez la question dans le forum.
Souvenez-vous qu'il n'y a jamais de question bête, mais rester dans l'ignorance parce que l'on n'ose pas poser une question, ça c'est une erreur !

PRIORITY QUEUE


Information sur la source

Catégorie :Maths & Algorithmes Source .NET ( DotNet ) Classé sous : queue, priority, algorithme, arbre, binaire Niveau : Débutant Date de création : 10/05/2007 Date de mise à jour : 13/05/2007 11:13:22 Vu / téléchargé: 5 222 / 86

Note :
8,5 / 10 - par 2 personnes
8,50 / 10

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

Commentaire sur cette source (18)
Ajouter un commentaire et/ou une note


Description

Priority Queue C#

J'imagine que tout le monde connait la class Queue du framework qui permet de faire des piles FIFO.
J'ai cherché une PriorityQueue dans le framework, mais je n'ai rien trouvé à ce sujet... Voici donc une classe générique TRES simple qui met cette fonctionnalité en place.

Les Enqueues et les Dequeues se font dans une complexité d'ordre O(log n) grâce à un arbre binaire, ce qui est assez rapide.
Une priority queue peut être utile quand certains éléments doivent absolument passer avant les autres. Par exemple, un prof qui veut imprimer un document avant des étudiants (si y'a 100 fichiers d'étudiants dans la liste d'attentes, le prof passera devant tout le monde :D).

Suite à la discussion dans les commentaires de cette source, j'ajoute que cet algorithme n'est peut-être pas le meilleur au niveau des performances (ceci dit on a quand même un O(log n) ce qui est très bien) mais il reste (comparé à d'autres solutions plus rapide) très simple à mettre en place. De plus, la place prise en mémoire est très faible (un tableau de n élément suffit).

Source

  • using System;
  • using System.Collections.Generic;
  • using System.Text;
  • /*************************************************************************************
  • *
  • * PriorityQueue
  • * Author : Bidou (http://www.csharpfr.com/auteurdetail.aspx?ID=13319)
  • * Blog : http://blogs.developpeur.org/bidou/
  • * Date : May 2007
  • *
  • *************************************************************************************/
  • namespace PriorityQueue
  • {
  • /// ---------------------------------------------------------------------------------
  • /// <summary>
  • /// PriorityQueue.
  • ///
  • /// Note: Insertion are done in O(log n)
  • /// Note: Removal are done in O(log n)
  • /// </summary>
  • /// <typeparam name="T"> The type to store in the priority queue. This type needs to
  • /// implement the IComparable interface. </typeparam>
  • /// ---------------------------------------------------------------------------------
  • public class PriorityQueue<T> where T : IComparable<T>
  • {
  • private List<InnerItem> _innerList = new List<InnerItem>();
  • private int _innerIndex = -1;
  • /// ---------------------------------------------------------------------------------
  • /// <summary>
  • /// Enqueue a new item.
  • /// </summary>
  • /// <param name="item"> The item to enqueue. </param>
  • /// ---------------------------------------------------------------------------------
  • public void Enqueue(T item)
  • {
  • this._innerList.Add(new InnerItem(++this._innerIndex, item));
  • if (this._innerList.Count > 1) this.UpHeap(); // Reorder the tree if needed
  • }
  • /// ---------------------------------------------------------------------------------
  • /// <summary>
  • /// Dequeue.
  • /// </summary>
  • /// <returns> Return the first item in the queue. </returns>
  • /// ---------------------------------------------------------------------------------
  • public T Dequeue()
  • {
  • T first = this.Peek();
  • int count = this._innerList.Count - 1;
  • this._innerList[0] = this._innerList[count];
  • this._innerList.RemoveAt(count);
  • this._innerIndex--;
  • this.DownHeap();
  • return first;
  • }
  • /// ---------------------------------------------------------------------------------
  • /// <summary>
  • /// Get the first item in the queue.
  • /// </summary>
  • /// <returns> The first item. </returns>
  • /// ---------------------------------------------------------------------------------
  • public T Peek()
  • {
  • return this._innerList.Count > 0 ? this._innerList[0].Item : default(T);
  • }
  • /// ---------------------------------------------------------------------------------
  • /// <summary>
  • /// Reorder the tree (down->up).
  • /// </summary>
  • /// ---------------------------------------------------------------------------------
  • private void UpHeap()
  • {
  • int current = this._innerList.Count - 1;
  • int parent = (current - 1) / 2;
  • while (this._innerList[current].Item.CompareTo(this._innerList[parent].Item) < 0)
  • {
  • this.SwitchElements(current, parent);
  • current = parent;
  • parent = (current - 1) / 2;
  • }
  • }
  • /// ---------------------------------------------------------------------------------
  • /// <summary>
  • /// Reorder the tree (up->down).
  • /// </summary>
  • /// ---------------------------------------------------------------------------------
  • private void DownHeap()
  • {
  • int best = 0; int current = -1;
  • int left = 0; int right = 0;
  • while(best != current)
  • {
  • current = best;
  • left = 2 * best + 1;
  • right = left + 1;
  • if (this._innerList.Count > left)
  • {
  • int x = this._innerList[best].Item.CompareTo(this._innerList[left].Item);
  • if (x > 0 || (x == 0 && this._innerList[left].Index < this._innerList[best].Index)) best = left;
  • }
  • if (this._innerList.Count > right)
  • {
  • int x = this._innerList[best].Item.CompareTo(this._innerList[right].Item);
  • if (x > 0 || (x == 0 && this._innerList[right].Index < this._innerList[best].Index)) best = right;
  • }
  • if(best != current) this.SwitchElements(best, current);
  • }
  • }
  • /// ---------------------------------------------------------------------------------
  • /// <summary>
  • /// Switch two elements
  • /// </summary>
  • /// <param name="item1"> The first element. </param>
  • /// <param name="item2"> The second element. </param>
  • /// ---------------------------------------------------------------------------------
  • private void SwitchElements(int item1, int item2)
  • {
  • InnerItem item = this._innerList[item1];
  • this._innerList[item1] = this._innerList[item2];
  • this._innerList[item2] = item;
  • }
  • /// ---------------------------------------------------------------------------------
  • /// <summary>
  • /// Hold the element (add an index to keep FIFO order with same keys).
  • /// </summary>
  • /// ---------------------------------------------------------------------------------
  • private class InnerItem
  • {
  • private T _item = default(T);
  • private int _index = 0;
  • /// ---------------------------------------------------------------------------------
  • /// <summary>
  • /// Represents an item (a Node in the tree).
  • /// </summary>
  • /// <param name="index"> The index of the Node. </param>
  • /// <param name="item"> The item. </param>
  • /// ---------------------------------------------------------------------------------
  • public InnerItem(int index, T item)
  • {
  • this._index = index;
  • this._item = item;
  • }
  • /// ---------------------------------------------------------------------------------
  • /// <summary>
  • /// Get or set the item.
  • /// </summary>
  • /// ---------------------------------------------------------------------------------
  • public T Item
  • {
  • get { return this._item; }
  • set { this._item = value; }
  • }
  • /// ---------------------------------------------------------------------------------
  • /// <summary>
  • /// Get or set the index of the item.
  • /// </summary>
  • /// ---------------------------------------------------------------------------------
  • public int Index
  • {
  • get { return this._index; }
  • set { this._index = value; }
  • }
  • }
  • }
  • }
using System;
using System.Collections.Generic;
using System.Text;

/*************************************************************************************
 *
 *  PriorityQueue
 *  Author : Bidou (http://www.csharpfr.com/auteurdetail.aspx?ID=13319)
 *  Blog   : http://blogs.developpeur.org/bidou/
 *  Date   : May 2007
 * 
 *************************************************************************************/

namespace PriorityQueue
{
    /// ---------------------------------------------------------------------------------
    /// <summary>
    /// PriorityQueue.
    /// 
    /// Note: Insertion are done in O(log n)
    /// Note: Removal are done in O(log n) 
    /// </summary>
    /// <typeparam name="T"> The type to store in the priority queue. This type needs to
    /// implement the IComparable interface. </typeparam>
    /// ---------------------------------------------------------------------------------
    public class PriorityQueue<T> where T : IComparable<T>
    {
        private List<InnerItem> _innerList = new List<InnerItem>();
        private int _innerIndex = -1;

        /// ---------------------------------------------------------------------------------
        /// <summary>
        /// Enqueue a new item.
        /// </summary>
        /// <param name="item"> The item to enqueue. </param>
        /// ---------------------------------------------------------------------------------
        public void Enqueue(T item)
        {
            this._innerList.Add(new InnerItem(++this._innerIndex, item));
            if (this._innerList.Count > 1) this.UpHeap(); // Reorder the tree if needed
        }

        /// ---------------------------------------------------------------------------------
        /// <summary>
        /// Dequeue.
        /// </summary>
        /// <returns> Return the first item in the queue. </returns>
        /// ---------------------------------------------------------------------------------
        public T Dequeue()
        {
            T first = this.Peek();
            int count = this._innerList.Count - 1;
            this._innerList[0] = this._innerList[count];
            this._innerList.RemoveAt(count);
            this._innerIndex--;
            this.DownHeap();
            return first;
        }

        /// ---------------------------------------------------------------------------------
        /// <summary>
        /// Get the first item in the queue.
        /// </summary>
        /// <returns> The first item. </returns>
        /// ---------------------------------------------------------------------------------
        public T Peek()
        {
            return this._innerList.Count > 0 ? this._innerList[0].Item : default(T);
        }

        /// ---------------------------------------------------------------------------------
        /// <summary>
        /// Reorder the tree (down->up).
        /// </summary>
        /// ---------------------------------------------------------------------------------
        private void UpHeap()
        {
            int current = this._innerList.Count - 1;
            int parent = (current - 1) / 2;
            while (this._innerList[current].Item.CompareTo(this._innerList[parent].Item) < 0)
            {
                this.SwitchElements(current, parent);
                current = parent;
                parent = (current - 1) / 2;
            }
        }

        /// ---------------------------------------------------------------------------------
        /// <summary>
        /// Reorder the tree (up->down).
        /// </summary>
        /// ---------------------------------------------------------------------------------
        private void DownHeap()
        {
            int best = 0; int current = -1;
            int left = 0; int right = 0;

            while(best != current)
            {
                current = best;
                left = 2 * best + 1;
                right = left + 1;

                if (this._innerList.Count > left)
                {
                    int x = this._innerList[best].Item.CompareTo(this._innerList[left].Item);
                    if (x > 0 || (x == 0 && this._innerList[left].Index < this._innerList[best].Index)) best = left;
                }
                if (this._innerList.Count > right)
                {
                    int x = this._innerList[best].Item.CompareTo(this._innerList[right].Item);
                    if (x > 0 || (x == 0 && this._innerList[right].Index < this._innerList[best].Index)) best = right;
                }
                if(best != current) this.SwitchElements(best, current);
            }
        }

        /// ---------------------------------------------------------------------------------
        /// <summary>
        /// Switch two elements
        /// </summary>
        /// <param name="item1"> The first element. </param>
        /// <param name="item2"> The second element. </param>
        /// ---------------------------------------------------------------------------------
        private void SwitchElements(int item1, int item2)
        {
            InnerItem item = this._innerList[item1];
            this._innerList[item1] = this._innerList[item2];
            this._innerList[item2] = item;
        }

        /// ---------------------------------------------------------------------------------
        /// <summary>
        /// Hold the element (add an index to keep FIFO order with same keys).
        /// </summary>
        /// ---------------------------------------------------------------------------------
        private class InnerItem
        {
            private T _item = default(T);
            private int _index = 0;

            /// ---------------------------------------------------------------------------------
            /// <summary>
            /// Represents an item (a Node in the tree).
            /// </summary>
            /// <param name="index"> The index of the Node. </param>
            /// <param name="item"> The item. </param>
            /// ---------------------------------------------------------------------------------
            public InnerItem(int index, T item)
            {
                this._index = index;
                this._item = item;
            }

            /// ---------------------------------------------------------------------------------
            /// <summary>
            /// Get or set the item.
            /// </summary>
            /// ---------------------------------------------------------------------------------
            public T Item
            {
                get { return this._item; }
                set { this._item = value; }
            }

            /// ---------------------------------------------------------------------------------
            /// <summary>
            /// Get or set the index of the item.
            /// </summary>
            /// ---------------------------------------------------------------------------------
            public int Index
            {
                get { return this._index; }
                set { this._index = value; }
            }
        }
    }
}
Pour les "Membres Club", vous pouvez télécharger directement un fichier contenu dans le zip sans télécharger le zip en entier !

Télécharger le zip

13 mai 2007 11:13:22 :
Les éléments de même priorités restent classés dans un ordre FIFO. Quelques modifications dans la description du code.
  • signaler à un administrateur
    Commentaire de SharpMao le 11/05/2007 09:33:01

    Intéressant, mais pourquoi ne pas avoir utilisé une SortedList au lieu de la list. De cette manière, tu évites le tri manuel.

  • signaler à un administrateur
    Commentaire de Warny le 11/05/2007 10:31:27

    Je pense que tu t'embête beaucoup à classer tes élements.
    Une priorityqueue est avant tout une liste de queue dont certaines doivent être traités avant les autres.
    Tu peux dont faire une liste de queue et revoyer le premier élement de la première queue non vide.

  • signaler à un administrateur
    Commentaire de Bidou le 11/05/2007 11:58:23 administrateur CS

    SharpMao> Une SortedList est un dictionary trié, ce qui ne m'arrange pas trop pour ma liste générique (il faudrait forcé de dérivé d'une interface pour qu'on soit sûr que l'object contient par exemple une property Key). De plus, comme c'est un dictionary, il n'accepte pas les doublons (alors qu'une Priority queue, peut avoir des éléments avec une clef identique).

    Pour finir, si on utilise le tris automatique de la SortedList, on se retrouve certainement (pas vérifié) avec une complexité de l'ordre de O(n^2) voire O(n log n) alors que mon implémentation avec un arbre binaire (un heap en fait) travail en O(log n) ce qui est bien meilleur (on tend presque à avoir un temps constant pour une grosse quantité d'éléments).

    Warny> En utilisant ta manière, non seulement on a une place utilisée en mémoire beaucoup plus grande, mais en plus on a une complexité à coucher dehors (au moins O(n^2) je dirais au premier abord).

    J'espère ne pas m'être trompé dans mes calculs, mais ça devrait être correct.

  • signaler à un administrateur
    Commentaire de Warny le 11/05/2007 12:04:19

    Non, la complexité obtenu est en O(n). En fait, tu ne fais pas de classement.
    Si un document en priorité 1 doit passer forcement avant un document en priorité 2, tu mets ton document dans la file d'attente de priorité 1 et tu lis cette file en premier. Quand elle est vide tu lis la file de priorité 2 et ainsi de suite.
    L'écriture se fait toujours à la fin de la file et la lecture se fait toujours au début. Je ne classe donc jamais les élements.

  • signaler à un administrateur
    Commentaire de Bidou le 11/05/2007 12:07:42 administrateur CS

    Supposons que tu aies raisons... (j'ai pas vérifié mais je te fais confiance).
    Mon arbre binaire est toujours beaucoup beaucoup plus rapide avec O(log n) que ton O(n)... (en fait, incomparablement plus rapide pour des listes avec beaucoup d'éléments).

  • signaler à un administrateur
    Commentaire de Warny le 11/05/2007 12:14:20

    tu te trompe de calcul. O(log(n)) signifie que tu ne compare pas tous les élements.
    Comme tu fais un tri à bulle, tu compares tous les élements à l'ensemble des autres, ce qui te fait une complexité en O(n^2)
    La méthode de tri la plus rapide est le tri-fusion qui approche le O(n*log(n)) - c'est une moyenne -
    La méthode de classement (et plus de tri) la plus rapide et l'arbre de classement qui permet de ranger un élement dans un arbre en fonction de sa valeur et sa complexité est en O(n) et plus précisement x*n ou x est la longueur de la valeur à classer. C'est ce que j'utilise pour répondre à ce problème.

  • signaler à un administrateur
    Commentaire de Bidou le 11/05/2007 13:02:16 administrateur CS

    Le but est justement de ne pas tout comparer pour être plus rapide! Puisque les éléments sont triés dans un arbre binaire (et trié selon l'implémentation du CompareTo) on obtient une insertion triée (enqueue) en O(log n) et une supression (dequeue) en O(log n) également car on ne doit justement pas parcourir tous les éléments de l'arbre !

    Tu peux faire une recherche sur le net, tu verras qu'effectivement je ne te raconte pas de bêtise et qu'un enqueue et dequeue se font bien en O(log n) si on implémente un Heap (ce qui est, encore une fois, bien plus rapide que ton O(n) que tu proposes...)

  • signaler à un administrateur
    Commentaire de Warny le 11/05/2007 13:21:54

    je compte O(n) avec n étant le nombre d'élément que j'insère dans ma queue. Si je ne m'interresse qu'à 1 élement je suis en O(1) (je ne peux pas faire mieux).
    De la même façon, en moyenne en insertion pour 1 élement tu est en O(log(n)) ou n est le nombre d'éléments déjà présents dans ta queue. Si tu dois classer l'ensemble des éléments que tu insère, il faut compter au moins une fois chaque élément O(n) que tu classes O(log(n)). Soit au total O(n*log(n)).
    Pour récapituler :
    + pour 1 élement :
      - tri-insertion : O(log(n))
      - classement-arbre : O(1)
    + pour n élements :
      - tri-insertion : O(n*log(n))
      - classement-arbre : O(n)

  • signaler à un administrateur
    Commentaire de Bidou le 11/05/2007 13:37:43 administrateur CS

    Je ne comprend pas trop où tu veux en venir. La solution que je propose pour implémenter une PriorityQueue n'est pas révolutionnaire, c'est la méthode qui est recommandé (car la plus rapide). Je n'ai pas inventé l'algorithme HeapSort...

    Si tu insères sans trié, tu insères en O(1) mais il te faut O(n) pour faire sortir l'élément
    IN : O(1)
    OUT: O(n)

    Si tu insères trié, tu insères en O(n) mais il te faut O(1) pour faire sortir l'élément
    IN : O(n)
    OUT: O(1)

    Si tu utilises un Heap, tu insères en O(log n) et tu fais sortir l'élément en O(log n) également
    IN : O(log n)
    OUT: O(log n)


    Il est évident que la dernière solution est de loin la meilleure, car même si tu insères (respectivement sort) en O(1) tu vas sortir (respectivement insérer) en O(n). C'est à dire que tu sera un tout petit peu meilleur avec ton O(1) mais largement largué ensuite par ton O(n) (plus n sera élevé, plus tu seras largué)

    Exemple avec 1000 éléments:

    Ma solution:
    IN : ~O(10)
    OUT: ~O(10)

    la tienne:

    IN : O(1) respectivement O(1000)
    OUT: O(1000) respectivement O(1)

    T'es un tout petit peu meilleur avec O(1) par rapport à O(10), mais clairement très loin derrière pour O(1000) comparé à un O(10).
    Je ne sais pas si tu me suis ???

  • signaler à un administrateur
    Commentaire de Warny le 11/05/2007 14:07:57

    Mon principe est de faire une queue par niveau de priorité :
    Queue[0]
    Queue[1]
    Queue[2]
    Queue[3]
    Queue[4]
    ...
    Eventuellement créées dynamiquement (c'est pas la peine de créer 65535 queues si tu a ce nombre de niveaux de priorités) et détruites dynamiquement quand elles sont vides.
    Si tu as un niveau de priorité 4 à mettre, tu le mets dans la liste 4 soit O(1)
    Tu ne peux lire dans la liste 4 que si les listes précédente sont vides. Si c'est le cas ta complexité est... O(1). Tu prends toujours le premier élement de la première liste.
    Y a pas plus efficace.
    La question est pourquoi trier quand on peut classer ?

  • signaler à un administrateur
    Commentaire de SharpMao le 11/05/2007 14:16:38

    Hello,

    Pour les SortedList, tu as raison.
    Par contre, corrige-moi si je me trompe, mais cette méthode ne donne pas de queue FIFO avec des éléments de même priorité.

  • signaler à un administrateur
    Commentaire de Bidou le 11/05/2007 14:21:08 administrateur CS

    Warny> Imaginons que j'aie 1000 éléments, 500 clefs différentes. Tu as 500 queues à construire (dynamiquement si tu veux...). Le temps que tu construises tes 500 queues en boucles, j'ai déjà eu le temps de traiter quelques centaines de millieurs d'élément... Ma solution n'utilise effectivement qu'une List de taille n.

    SharpMao> Oui, tu as raison ;-)

  • signaler à un administrateur
    Commentaire de Warny le 11/05/2007 14:34:20

    C'est pas faux, mais dans les faits j'aurais difficilement plus de 5 à 10 niveaux de priorités.
    De plus si je suis dans le cas que tu décris et que je dois vraiment optimiser le classement, j'utiliserai un algorithme B-TREE et je serais toujours plus efficace.
    Pour l'instant le seul problème pour lequel je suis obligé d'utiliser un algo de classement, c'est quand je fais de la 3D et que je dois classer des polygones, parce que leur définition utilise plus d'une dimension.
    Dans le cas où mes élements n'ont qu'une dimension, je peux les classer. Dans le cas d'une queue à priorité, mon élément de priorité a rarement plus d'une dimension et mon algo, éventuellement classé en arbre, est toujours plus rapide.

  • signaler à un administrateur
    Commentaire de Bidou le 11/05/2007 15:03:32 administrateur CS

    Autre chose: comment tu fais pour ajouter une Queue dans ta liste de queues?
    Tu es obligé de vérifier si elle est existante ou pas, non? Donc tu dois itérer à travers toutes tes queues pour voir si elle existe? même chose quand tu en supprimes une?

  • signaler à un administrateur
    Commentaire de Warny le 11/05/2007 15:16:04

    Non, c'est pas la peine, va voir cet excelent article de wikipédia : http://fr.wikipedia.org/wiki/B-Arbre
    et notamment le lien de visualisation d'un b-arbre pour te faire une idée. C'est la méthode utilisée pour insérer les élements dans une base de données.

  • signaler à un administrateur
    Commentaire de Bidou le 11/05/2007 15:34:55 administrateur CS

    Euh j'ai pas trop le temps de regarder en détail pour le moment. Si tu as une classe générique qui s'occupe de ça, je serais curieux de faire quelques teste pour voir la différence entre les deux...

  • signaler à un administrateur
    Commentaire de Warny le 11/05/2007 15:59:11

    Je n'ai pas de classe toute faite, c'est peut-être l'occasion justement.

  • signaler à un administrateur
    Commentaire de Bidou le 13/05/2007 11:14:49 administrateur CS

    SharpMao> J'ai mis à jour pour que les éléments de même priorités restent dans un ordre FIFO.

Ajouter un commentaire

Appels d'offres

Pub



CalendriCode

Mai 2008
LMMJVSD
   1234
567891011
12131415161718
19202122232425
262728293031 

VS Express FR Gratuit !

VS Express en français et 100% gratuit !

Téléchargements

Logiciels à télécharger sur le même thème :

Boutique

Boutique de goodies CodeS-SourceS