2012-02-10 29 views
9

Seguí las instrucciones dadas en this question (la respuesta de Jason) para escribir mi PriorityQueue<T> usando SortedList. Entiendo que el campo count dentro de esta clase se usa para garantizar prioridades únicas y para preservar el orden de puesta en secuencia entre la misma prioridad.Cambiar la prioridad en una cola de prioridad personalizada

Sin embargo, cuando count alcanza su valor máximo y yo suma 1, este último comenzará de nuevo desde 0, por lo que la prioridad de los elementos posteriores sería mayor que la prioridad de los elementos anteriores. El uso de este enfoque que pudiera necesitar una manera de "seguridad" restablecer el contador count ... De hecho, supongo que tienen el siguiente estado de la cola (en el formato de prioridad | contar | elemento):

0 | 123 | A
0 | 345 | B
1 | 234 | C
2 | 200 | D

Ahora supongamos que ha llegado el límite del contador, por lo que tengo que restablecerlo a 0: como consecuencia, el siguiente elemento insertado tendrá contador 0: por ejemplo, si inserto un elemento con prioridad igual a 1, se insertará incorrectamente antes de 1 | 234 | D

0 | 123 | A
0 | 345 | B
1 | 000 | nuevo elemento
1 | 234 | C
2 | 200 | D

El problema de la prioridad puede ser resuelto mediante la implementación de un majano creé una clase Heap, entonces utilicé Heap<KeyValuePair<TPriority, TElement> y una costumbre PriorityComparer con el fin de ordenar los elementos por TPriority. Dada TPriority como int y TElement como string, la PriorityComparer es el siguiente:

public class MyComparer : IComparer<KeyValuePair<int, string>> 
{ 
    public int Compare(KeyValuePair<int, string> x, KeyValuePair<int, string> y) 
    { 
     return x.Key.CompareTo(y.Key); 
    } 
} 

... 

int capacity = 10; 
Heap<KeyValuePair<int, string>> queue; 
queue = new Heap<KeyValuePair<int, string>>(capacity, new PriorityComparer()); 

... 

ACTUALIZACIÓN De esta manera (con el PriorityComparer), he tenido éxito para implementar una cola de prioridad. Ahora me gustaría agregar soporte para modificar su comportamiento en el tiempo de ejecución, es decir, cambiar de FIFO a clasificación de prioridad y viceversa. Ya que mi aplicación de cola de prioridad tiene un campo IComparer, creo que es suficiente con añadir una propiedad Comparer a editar este campo, como los siguientes:

public IComparer 
{ 
    set 
    { 
     this._comparer = value; 
    } 
} 

Mientras tanto, yo pensé en tomar un enfoque diferente: en lugar de usar un montón binario para administrar las prioridades, podría ajustar diferentes colas (cada cola se refiere a una prioridad dada) de la siguiente manera.

public class PriorityQueue<T, int> 
{ 
    private Queue<T> _defaultQueue; 
    private bool _priority; 
    private SortedList<int, Queue<T>> _priorityQueues; 

    public PriorityQueue(int capacity) 
    { 
     this._defaultQueue = new Queue<T>(capacity); 
     this._priority = false; 
     this._priorityQueues = new SortedList<int, Queue<T>>(0); 
    } 

    public void PriorityEnable() 
    { 
     this._priority = true; 
    } 

    public void PriorityDisable() 
    { 

     this._priority = false; 
    } 

    public void Enqueue(T item) 
    { 
     if (this._priority) 
     { 
      // enqueue to one of the queues 
      // with associated priority 
      // ... 
     } 
     else this._defaultQueue.Enqueue(item); 
    } 

    public T Dequeue() 
    { 
     if (this._priority) 
     { 
      // dequeue from one of the queues 
      // with associated priority and 
      // return 
      // ... 
     } 
     return this._defaultQueue.Dequeue(); 
    } 
} 
  1. Cómo gestionar la transición de modo FIFO a modo de prioridad cuando todavía hay elementos en la cola por defecto? Podría copiarlos en las colas de prioridad basadas en la prioridad del artículo ... ¿Otras soluciones mejores?
  2. Cómo administrar la transición de modo de prioridad a Modo FIFO? En este caso, tendría varias colas de prioridad, que pueden contener elementos, pero ya no tienen que administrarlas de acuerdo con la prioridad y ni siquiera saber el orden de llegada original ...
  3. ¿Cómo puedo gestionar la capacidad de los diferentes colas?
  4. ¿Qué hay de las prestaciones de las dos soluciones anteriores? ¿Qué usa más memoria?
+0

¿Mi respuesta es útil para su problema? – JeremyK

Respuesta

1

EDITAR: Ha cambiado el tipo de lo que está pidiendo con sus ediciones. Pasó de hacer una pregunta a hacer un nuevo enfoque y hacer una nueva pregunta. Probablemente debería abrir una nueva pregunta para su nuevo enfoque, ya que esta es ahora confusa en cuanto a qué respuesta/respuesta es a qué pregunta/comentario. Creo que su pregunta original sobre la clasificación de prioridades iguales ha sido respondida.

Puede usar una longitud para permitir más valores. Siempre llegará a su fin con el tiempo, por lo que necesitaría usar un nuevo patrón para valores únicos o 'contar' los artículos cuando se alcance el máximo (recorra cada uno y reinicie el valor de conteo exclusivo).

¿Quizás use un GUID para cada artículo en su lugar?

Guid.NewGuid()

EDIT:

Para añadir después de su edición: Si desea que el nuevo 1 a colocarse después de la existente, en la anulación de comparación, devolver un mayor de resultado (1) cuando los valores son iguales. De esa manera, sucederá lo siguiente:

1 > 0, return greater (1), continue 
1 > 0, return greater (1), continue 
1 == 1, return greater (1), continue 
1 < 2, return less than (-1), insert 

EDIT 2:

Si el segundo parámetro sólo está destinado a ser un valor único, siempre se puede utilizar una cadena y establecer el valor como cadenas numéricas en su lugar. De esa forma nunca alcanzarás un tope, solo tendrías que analizar la cadena en consecuencia. Puede usar valores alfa principales que representan un nuevo conjunto.

No he probado este código, solo una idea de lo que podría hacer.

static string leadingStr = ""; 
static char currentChar = 'a'; 
static Int32 currentId = Int32.MinValue; 

static string getNextId() 
{ 
    if (currentId >= Int32.MaxValue) 
    { 
     currentId = Int32.MinValue; 
     if (currentChar >= 'z') 
     { 
      currentChar = 'a'; 
      leadingStr = leadingStr.Insert(0, "X"); 
     } 
     else 
      currentChar++; 
    } 
    else 
     currentId++; 

    return String.Format("{0}{1}-{2}", leadingStr, currentChar, currentId); 
} 

Datos 3: Restablecer Valores

static Int64 currentValue = Int64.MinValue; 
static void AddItem(object item) 
{ 
    if (currentValue == Int64.MaxValue) 
     RecountItems(); 

    item.counter = currentValue++; 
    SortedList.Add(item); 
} 

static void RecountItems() 
{ 
    currentValue = 0; 
    foreach (var item in SortedList) 
    { 
     item.counter = currentValue++; 
    } 
} 

Editar 4: Para su segunda pregunta:

se puede utilizar una pila FIFO como lo haría normalmente, pero también tienen una lista de prioridades que sólo almacena la identificación única de los artículos. Sin embargo, necesitarás quitar el elemento de la lista cada vez que lo elimines de la pila FIFO.

static Object RemoveNextFIFO() 
{ 
    if (fifoList.Count > 0) 
    { 
     var removedItem = fifoList[0]; 
     fifoList.RemoveAt(0); 
     RemoveItemFromPriority(removedItem); 
     return removedItem; 
    } 
} 

static void RemoveItemFromPriority(Object itemToRemove) 
{ 
    foreach (var counter in priorityQueue) 
    { 
     if (counter == itemToRemove.counter) 
     { 
      priorityQueue.remove(item); 
      break; 
     } 
    } 
} 

static Object RemoveFromFIFO(int itemCounter) 
{ 
    foreach (var item in fifoList) 
    { 
     if (item.counter == itemCounter) 
     { 
      fifoList.Remove(item); 
      return item; 
     } 
    } 
} 

static Object RemoveNextPriority() 
{ 
    if (priorityQueue.Count > 0) 
    { 
     var counter = priorityQueue.Pop(); 
     return RemoveFromFIFO(counter); 
    } 
} 
+0

Bueno, el 'SortedList' ordena automáticamente los elementos usando un' IComparer': ya creé este _comparer_ y funciona ordenando por prioridad y contador (los elementos con la misma prioridad se ordenan comparando sus contadores). El problema es que en algún punto el contador alcanza su valor máximo y comienza de nuevo desde 0: cualquier elemento agregado antes del reinicio el contador tendría un contador mayor y aparecerían como si se hubieran insertado después de los últimos. El 'Guid' no resuelve el problema, porque es aleatorio, por lo que podría distorsionar el orden de inserción. – enzom83

+0

¿Sería posible adjuntar un parámetro adicional a sus datos? Es decir, si tuvieras un id. De 0 a 255 (solo un ejemplo) podrías tener un IDSegundo de 0 a 255 donde primero verificas el id y luego el idSegundo. ¿Entonces tendría todo de 0,0 a 0,1 a ... a 255,255 que cuadraría sus números permitidos? Usar DOS largos seguramente excederá tus requisitos, ¿verdad? - De un aprendiz de programador. – BlackVegetable

+1

@BlackVegetable: _¿Será posible agregar un parámetro adicional a sus datos? _ Si no hay otra alternativa, creo que el único derecho es agregar otro parámetro, tal vez el largo que contiene la fecha y hora actual. – enzom83

1

Se podía "hacer trampa" y utilizar BigInteger por lo que nunca "agotado los números". Esto, por supuesto, conduce a un deterioro gradual del rendimiento en el tiempo, pero probablemente no lo suficientemente importante como para importar.

Combina eso con una cola de prioridad basada en heap y estás listo!


No trate de "cambio de FIFO a la clasificación de prioridad y viceversa" - simplemente poner los elementos en ambos estructuras de datos apropiadas para la tarea (Queue y cola de prioridad).

+0

Solo necesito "cambiar de FIFO a clasificación prioritaria y viceversa". De esta forma, cuando la cola se llena más allá de cierto límite, cambia a la cola de prioridad. Luego, cuando el nivel de llenado se reduce por debajo de un cierto umbral, pasa a FIFO. – enzom83

+0

@ enzom83 ¿Por qué? ¿Qué está tratando de lograr? –

+0

Necesito controlar el flujo de mensajes salientes a una conexión, así que uso una cola para cada conexión: si hay demasiados mensajes en la cola, probablemente la conexión es lenta, entonces podría aplicar una prioridad a los mensajes salientes. – enzom83

1

Usar tanto Queue como Priority Queue es lo que haría.
Pero si debe ...

En lugar de una tecla, use 2 teclas para un elemento.
La primera clave priority será la prioridad.
La segunda clave time será un contador que será como una marca de tiempo.

Para el comportamiento normal, use la clave priority.

Cuando el montón está lleno, HEAPIFY por la clave time.
Luego elimine n elementos necesarios.
Ahora HEAPIFY de nuevo con la clave priority para volver al comportamiento normal.

+0

Cuando el montón está lleno, tal vez debería _he-heapify_ por la tecla _priority_ (no por el tiempo) ... – enzom83

+0

Es más una idea lúdica, Queue + Priority Queue es el camino a seguir. –

+0

Creo que usaré un montón: es suficiente que cada elemento incluya la prioridad, ya que los duplicados no son un problema para el montón (los artículos que tienen el mismo valor se colocan en el orden de llegada, no es necesario agregar más campos). Tan pronto como algunos elementos (elementos 'N') se eliminen de la cola, vuelvo a heapify con nuevas prioridades iguales entre sí. – enzom83

Cuestiones relacionadas