2012-02-10 17 views
9

Ho seguito le istruzioni fornite in this question (la risposta di Jason) per scrivere il mio PriorityQueue<T> utilizzando un SortedList. Comprendo che il campo count all'interno di questa classe viene utilizzato per garantire priorità univoche e per mantenere l'ordine dell'accodamento tra la stessa priorità.Modificare la priorità in una coda di priorità personalizzata

Tuttavia, quando count raggiunge il valore massimo e I somma 1 ad esso, quest'ultimo ricomincia da 0, quindi la priorità delle voci successive sarà superiore alla priorità delle voci precedenti. Usando questo approccio avevo bisogno di un modo per "sicuro" azzerare il contatore count ... Infatti, supponiamo di avere il seguente stato della coda (nella priorità formato | contare | voce):

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

Ora supponiamo che il limite del contatore sia stato raggiunto, quindi devo resettarlo a 0: di conseguenza, il prossimo elemento inserito avrà il contatore 0: ad esempio, se inserisco un elemento con priorità uguale a 1, esso sarà inserito erroneamente prima 1 | 234 | D

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

Il problema della priorità può essere risolto implementando un mucchio: ho creato una classe Heap, poi ho usato Heap<KeyValuePair<TPriority, TElement> e personalizzato PriorityComparer al fine di risolvere gli elementi da TPriority. Dato TPriority come int e TElement come string, il PriorityComparer è la seguente:

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()); 

... 

UPDATE In questo modo (usando il PriorityComparer), sono riuscito ad implementare una coda di priorità. Ora vorrei aggiungere supporto per modificare il suo comportamento in fase di esecuzione, ovvero passare da FIFO a ordinamento prioritario e viceversa. Dal momento che la mia implementazione di coda con priorità ha un campo IComparer, penso che è sufficiente aggiungere una proprietà Comparer per modificare questo campo, come la seguente:

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

Nel frattempo ho pensato di adottare un approccio diverso: invece di usare un heap binario per gestire le priorità, potrei avvolgere code diverse (ogni coda si riferisce a una determinata priorità) come segue.

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. Come gestire la transizione da FIFO modalità-modalità priorità quando ci sono ancora elementi nella coda di default? Potrei copiarli nelle code di priorità in base alla priorità dell'oggetto ... Altre soluzioni migliori?
  2. Come gestire la transizione dalla modalità priorità a Modalità FIFO? In questo caso, avrei diverse code di priorità, che possono contenere elementi, ma non devono più gestirle in base alla priorità e nemmeno conoscere l'ordine di arrivo originale ...
  3. Come posso gestire la capacità dei diversi code?
  4. E le prestazioni delle suddette due soluzioni? Quale usa più memoria?
+0

La mia risposta è di qualche aiuto per il tuo problema? – JeremyK

risposta

1

MODIFICA: Hai cambiato le tue richieste con le tue modifiche. Sei passato dal fare una domanda a un nuovo approccio e fare una nuova domanda. Probabilmente dovrebbe aprire una nuova domanda per il tuo nuovo approccio, poiché questo è ora fonte di confusione su quale risposta/risposta è a quale domanda/commento. Credo che la tua domanda iniziale sull'ordinamento di priorità uguali sia stata esaudita.

È possibile utilizzare un lungo per consentire più valori. Alla fine otterrai sempre un fine, quindi dovrai utilizzare un nuovo modello per valori univoci o "ricontestare" gli elementi quando viene raggiunto il valore massimo (esegui il ciclo su ciascuno e reimposta il valore di conteggio univoco).

Forse utilizzare un GUID per ciascun articolo?

Guid.NewGuid()

EDIT:

Per aggiungere dopo la modifica: Se si desidera che il nuovo 1 da collocare dopo la corrente, nella sostituzione Confronta, restituire un maggiore di risultato (1) quando i valori sono uguali. In questo modo si verifica quanto segue:

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:

Se il secondo parametro il solo scopo di essere un valore unico, si può sempre usare una stringa e impostare il valore come stringhe numeriche invece. In questo modo non raggiungerai mai un limite, dovresti solo analizzare la stringa di conseguenza. Puoi utilizzare i principali valori alfa che rappresentano un nuovo set.

Non ho testato questo codice, solo un'idea su cosa si potrebbe fare.

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); 
} 

EDIT 3: Ripristina valori

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++; 
    } 
} 

Modifica 4: riguarda la seconda domanda:

Si potrebbe utilizzare una pila FIFO come si farebbe normalmente, ma hanno anche una lista di priorità che solo memorizza l'ID univoco degli articoli. Tuttavia, dovrai rimuovere l'elemento dall'elenco ogni volta che rimuovi dallo stack 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

Bene, 'SortedList' ordina automaticamente gli elementi usando un' IComparer': ho già creato questo _comparer_ e funziona ordinando per priorità e contatore (gli oggetti con la stessa priorità sono ordinati confrontando i loro contatori). Il problema è che ad un certo punto il contatore raggiunge il suo valore massimo e ricomincia da 0: qualsiasi elemento aggiunto prima del reset del contatore avrebbe un contatore maggiore e quindi apparirebbe come se fosse stato inserito dopo gli ultimi. Il 'Guid' non risolve il problema, perché è casuale, quindi potrebbe distorcere l'ordine di inserimento. – enzom83

+0

Sarebbe possibile allegare un parametro aggiuntivo ai dati? Cioè, se tu avessi un id da 0 a 255 (solo un esempio) potresti avere un idSecond da 0 a 255, dove prima verifichi l'id e poi l'idSecond? Quindi avresti tutto da 0,0 a 0,1 a ... a 255,255 che quadrerebbe i tuoi numeri consentiti? L'utilizzo di DUE long potrebbe sicuramente superare le tue esigenze, giusto? - Da un programmatore apprendista. – BlackVegetable

+1

@BlackVegetable: _Dovrebbe essere possibile aggiungere un parametro aggiuntivo ai dati? _ Se non ci sono alternative, penso che l'unico diritto sia quello di aggiungere un altro parametro, forse il lungo contenente la data e l'ora correnti. – enzom83

1

Si potrebbe "barare" e utilizzare BigInteger in modo da non "a corto di numeri". Questo naturalmente porta a un progressivo deterioramento delle prestazioni nel tempo, ma probabilmente non abbastanza significativo da contenerlo.

Combinalo con una coda di priorità heap e sei impostato!


Non cercare di "passaggio da FIFO a ordinamento prioritario e viceversa" - semplicemente mettere elementi in entrambe strutture di dati appropriati per l'attività (Queue e coda di priorità).

+0

Ho solo bisogno di "passare da FIFO all'ordinamento prioritario e viceversa". In questo modo, quando la coda si riempie oltre un certo limite, passa alla coda di priorità. Quindi, quando il livello di riempimento viene ridotto al di sotto di una certa soglia, passa a FIFO. – enzom83

+0

@ enzom83 Perché? Cosa stai cercando di realizzare? –

+0

Ho bisogno di controllare il flusso dei messaggi in uscita verso una connessione, quindi uso una coda per ogni connessione: se ci sono troppi messaggi in coda, probabilmente la connessione è lenta quindi potrei applicare una priorità ai messaggi in uscita. – enzom83

1

L'utilizzo di entrambi Queue e Priority Queue è quello che vorrei fare.
Ma se è necessario ...

Invece di una chiave utilizzare 2 tasti per un elemento.
La prima chiave priority sarà la priorità.
La seconda chiave time sarà un contatore che sarà come un timestamp.

Per il comportamento normale utilizzare la chiave priority.

Quando lo heap è pieno, HEAPIFY tramite la chiave time.
Quindi rimuovere n elementi necessari.
Ora HEAPIFY di nuovo con la chiave priority per tornare al comportamento normale.

+0

Quando lo heap è pieno, forse dovrei _re-accumularlo con la chiave _priority_ (non per ora) ... – enzom83

+0

È più un'idea giocosa, Coda + Priorità La coda è la strada da percorrere. –

+0

Penso che userò un heap: è sufficiente che ogni elemento includa la priorità, poiché i duplicati non sono un problema per l'heap (gli articoli che hanno lo stesso valore sono collocati nell'ordine di arrivo, non c'è bisogno di aggiungere altro campi). Non appena alcuni elementi (elementi 'N') vengono rimossi dalla coda, ri-ammucchio con nuove priorità uguali tra loro. – enzom83

Problemi correlati