Modo più efficiente per conservare una collezione mista di doppi e int
-
21-09-2019 - |
Domanda
Devo archiviare una raccolta di INT e doppi (che rappresentano dati nominali e apprezzati) in C ++. Potrei ovviamente conservarli tutti in un std::vector<double>
, ma questo sembra un po 'sbagliato e non ottiene i punti bonus estetici.
Potrei anche cucinare qualcosa in base al polimorfismo, ma ho anche bisogno che la collezione sia davvero efficiente: sia la memorizzazione e il recupero dei dati nella raccolta dovrebbe essere il più veloce possibile. Trovo difficile giudicare se una tale soluzione sarebbe al massimo efficiente.
Ho anche trovato Boost :: Variant, che potrebbe essere di aiuto qui.
Informazioni aggiuntive: il numero di elementi nella raccolta sarà piccolo (<100) e noto quando inizializza la raccolta.
Riassumendo: potrei ovviamente risolverlo in innumerevoli modi, ma non sono sicuro di quale sarebbe una buona soluzione quando (i) l'efficienza è davvero importante e (ii) voglio anche scrivere un codice un po 'carino. Qual è la mia scommessa migliore qui?
Modifica, ulteriori informazioni: La raccolta rappresenta una "riga" in un set di dati più ampio, i suoi elementi rappresentano i valori di alcune "colonne". Le proprietà delle righe sono note, quindi è noto che tipo di dati vengono archiviati in quale posizione. L '"efficienza" di cui sto parlando è principalmente l'efficienza del recupero del valore INT/doppio di una determinata colonna, sebbene anche un'impostazione rapida dei valori sia importante. Ho alcune funzioni che operano sui dati che devono recuperarli il più velocemente possibile. Esempio:
typedef std::vector<double> Row;
void doubleFun(Row const &row)
{
// Function knows there's always a double at index 0
double value = row[0];
...
}
void integerFun(Row const &row)
{
// Function knows there's always an integer at index 1
int value = row[1];
...
}
Dopo un po 'più di pensiero e lettura dei suggerimenti finora, sembra che solo archiviare colonne Int e doppie colonne in due vettori separati sia una soluzione solida. La collezione Row
Potrebbe quindi definire due diversi membri per il recupero di dati nominali e reali che le funzioni possono utilizzare.
Sto solo conservando come un vector<double>
va bene anche io immagino, ma dipende dalla velocità con cui è la conversione tra doppia e int (che è probabilmente piuttosto impressionante).
Mi dispiace per essere stato un po 'poco chiaro all'inizio, spero che sia più chiaro e ora e che io possa avere qualche più pensiero sulla questione.
Soluzione
L'ordinazione è un punto importante nel tuo contenitore?
In caso contrario:
class MyContainer
{
std::vector<double> doubles;
std::vector<int> ints;
push(double value) { doubles.push_back(value); }
push(int value) { ints.push_back(value); }
....
};
La parte dell'iteratore (per sfogliare l'intero contenitore) potrebbe essere un po 'più complicata ...
Altri suggerimenti
Perché non usare direttamente un vettore di doppio? Poiché i numeri interi possono essere convertiti in doppio senza perdita di precisione ... mi sembra la soluzione più semplice ed efficiente.
Ciò che resta da impostare (e non sono riuscito a capire dalla tua domanda) come puoi fare la differenza tra valori normali e reali. Il problema rimane aperto in qualsiasi soluzione che potresti scegliere.
È possibile utilizzare un tipo di unione e usarlo nel tuo vettore. Ma in tal caso dovresti avere un modo per sapere quali elementi del vettore dovrebbero essere trattati come INT e quali dovrebbero essere trattati come doppi. Per tenere traccia di quali sono INT e quali sono doppi potresti usare un bitset o qualcosa del genere.
Non sono sicuro se il tuo obiettivo sia evitare calcoli di punti galleggianti pesanti. In tal caso, il bitset può essere più efficiente. In caso contrario, e esatto INT Precision non è importante, allora potresti anche conservare tutti come doppi.
#include <vector>
#include <bitset>
union di
{
double d;
int i;
};
int main(int argc, char* argv[])
{
std::bitset<2> bitsetInts;
std::vector<di> v;
di e1;
e1.d = 3.9;
v.push_back(e1);
di e2;
e2.i = 3;
bitsetInts.set(1);
v.push_back(e2);
return 0;
}
Vorrei andare per il boost::variant
Soluzione, si adatta perfettamente alle tue esigenze.
C'è la tupla boost, che puoi usare se conosci i tipi al momento della compilazione. Ma se il numero di articoli è piccolo, in modo efficiente nel perdere 100 byte non dovrebbe essere un problema.