Un modo migliore di O (n ^ 2) per attraversare un dizionario di dizionari.

2

Ho un dizionario di dizionari che devo attraversare per trovare due record con vari parametri di corrispondenza. Ho due foreach loop per fare ciò che è O (n ^ 2). Sto cercando l'ispirazione per trovare un modo migliore per farlo utilizzando i tasti per migliorare la ricerca.

Semplice esempio di seguito.

modifica: ogni record contiene molti valori che vorrei provare ad abbinare. Qui di seguito sto cercando una transazione BUY e SELL nei record. Vorrei anche abbinare lo stesso cliente e la stessa posizione, che sono le chiavi nel dizionario dei record. È possibile che il dizionario abbia solo valori di transazione "BUY". Il dizionario dei record contiene anche diversi tipi di valore, come double e int.

  • < "uniqueRecordIdABC", < "Transaction", "BUY" & gt ;, < "Location", "Store1" & gt ;, < "Client", "Bob" > >
  • < "uniqueRecordIdDEF", < "Transaction", "SELL" & gt ;, < "Location", "Store2" & gt ;, < "Client", "Bob" > >

Ciò che ho pensato di fare è rendere l'unicoRecordKey qualcosa che potrei analizzare, migliorando la ricerca. IE: 1: ACQUISTO: Store1: Bob, 2: VENDO: Store2: Bob, ma non sono sicuro se ci sia un vantaggio reale a riguardo.

Esempio:

Dictionary<string, Dictionary<string, object>> records

foreach (var item in records)
{
    Dictionary<string, object> record = item.Value;   
    string transaction1 = Convert.ToString(record["Transaction"]);
    string name1 = Convert.ToString(record["Client"]);
    string location1 = Convert.ToString(record["Location"]);

    foreach (var item2 in records)
    {
        Dictionary<string, object> record2 = item2.Value;
        string transaction2 = Convert.ToString(record["Transaction"]);
        string name2 = Convert.ToString(record["Client"]);
        string location2 = Convert.ToString(record["Location"]);

        if ((transaction1 == "BUY" && transaction2 == "SELL" || transaction1 == "SELL" && transaction2 == "BUY") &&  name1 == name2 && location1 == location2)
        {
        }
    }
}
    
posta flannelbeard 10.11.2015 - 18:04
fonte

3 risposte

4
{"id1": {"Transaction": "BUY",  "Location": "Store1", "Client": "Bob"}}
{"id2", {"Transaction": "SELL", "Location": "Store2", "Client": "Bob"}}

I would also want to maybe match for the same customer and same location, which are keys in the record dictionary.

Con questa struttura dati, dovrai effettivamente scorrere tutte le voci per poter trovare gli elementi corrispondenti. Potresti essere in grado di migliorarlo un po 'costruendo una seconda struttura mentre iterate su di essa per prima in modo che invece di O (n 2 ) ottieni qualcosa di più simile a O (n log n). Ma questo sarà quanto di meglio si otterrà con questa struttura.

Quello che devi fare è indicizzarlo efficacemente su altre porzioni di dati e risolverle.

{"Client": "Bob", "Buys": [ "id1" ], "Sells": [ "id2" ] }

E ora puoi cercare in una tabella hash o in un dizionario (qualunque sia la tua lingua li chiami) in O (1) tirando su solo Bob. Quindi puoi fare quello che vuoi con gli elenchi Buys and Sells.

Allo stesso modo, faresti cose simili con i Negozi. Ma devi creare la struttura dati corrispondente che corrisponda alle query che vuoi fare contro di essa.

Potresti voler considerare che se stai arrivando al punto di voler essere in grado di eseguire queste query sui dati, potresti dover passare al "prossimo livello" di archiviazione dei dati e metterli in un rapporto Banca dati. Questo ti dà il codice che è progettato per eseguire queste query molto velocemente e ti libera dalla preoccupazione di "oh, ora ho bisogno di costruire questa struttura altra per essere in grado di fare la domanda per "chi sono tutti i clienti in qualche negozio?" in un tempo ragionevole ".

    
risposta data 10.11.2015 - 20:20
fonte
2

Un modo è dividerli in due elenchi separati (uno per gli acquisti e uno per le vendite), ordinarli per nome e posizione, quindi eseguire una semplice unione.

Ad esempio:

var buyList = records.Where(Convert.ToString(records["Transaction"]) == "BUY")
    .OrderBy(Convert.ToString(records["Client"]))
    .ThenBy(Convert.ToString(records["Location"]));
var sellList = records.Where(Convert.ToString(records["Transaction"]) == "SELL")
    .OrderBy(Convert.ToString(records["Client"]))
    .ThenBy(Convert.ToString(records["Location"])); 

Ora puoi fare un algoritmo di fusione standard, passando in rassegna i due elenchi in tandem. Vedi il mio articolo Unire le sequenze ordinate per l'idea di base.

Questo trasforma la tua O (N ^ 2) in qualcosa come m log m + n log n + 3N , dove m e n sono rispettivamente il numero di acquisti e di vendite, e N è il numero totale di transazioni. Tuttavia, richiede O (N) spazio extra per gli elenchi temporanei.

    
risposta data 10.11.2015 - 20:29
fonte
0

Un algoritmo O (N) potrebbe essere quello di costruire un nuovo dizionario in cui la chiave è formata dai valori che stai cercando di cercare.

Dictionary<string, Dictionary<string, object>> records;
Dictionary<Tuple<string,string,string>, Dictionary<string,object>> lookup;

foreach (var record in records.Values)
{
    var key = new Tuple<string,string,string>(record["Client"], record["Location"],record["Transaction"]);
    lookup[key] = record; // this assumes that the composite key is unique
}

foreach(var kvp in lookup)
{
    var key = kvp.Key;
    var altKey = new Tuple<string,string,string>(key.Item1,key.Item2,FlipTransaction(key.Item3));
    if (lookup.ContainsKey(altKey))
    {
        // lookup[altKey] contains the paired transaction.
    }
}

FlipTransaction è una funzione che restituisce "Acquista" se il suo argomento è "Vendi" o viceversa.

Hai un ciclo O (N) per creare il nuovo dizionario di ricerca e un loop O (N) per scorrere il suo contenuto.

Inoltre, come l'algoritmo nella domanda questo troverà ogni coppia due volte.

    
risposta data 10.11.2015 - 20:53
fonte

Leggi altre domande sui tag