Teil von vector mit erweitereter Dimension?

T_55

Lieutenant
Registriert
Feb. 2013
Beiträge
638
Hallo,

ich bräuchte mal eine Denkstütze, es geht um C++.
Funktionen greifen auf Daten zu und welche das sein sollen wird per Index beim Aufruf der Funktion übermittelt.

Code:
void funk(std::vector<std::vector<int>> &daten, int index)
{
   // beispiel zugriff
   std::vector<int> ich_will_die_datenreihe = daten[index];
}

hier die Daten

Code:
std::vector<std::vector<int>> arr1;

Der Index des äusseren vectors gibt die Info auf welche Datenreihe zugegriffen werden soll. Die Datenreihe befindet sich im inneren vector. Soweit ok.

Jetzt aber sollen drei der Indexe des äusseren Vectors eine weitere übergeordnete Dimension bekommen.
daten[0] bis daten[7] sind wie oben. daten[8] bis daten[10] aber soll es jeweils nochmal in verschiedenen varianten geben also eine weitere äussere Dimension.

Wenn ich jetzt für alles ein 3D Array mache
Code:
std::vector<std::vector<<std::vector<int>>> arr2;
dann stimmt zwar alles für daten[8] bis daten[10] aber für daten[0] bis daten[7] ist die äusserste Dimension ja überflüssig.

Für 0-7 reicht folgendes da es keine Varianten gibt:
daten[0][datereihe] bis daten[7][datereihe]

Für 8-10 gibt es Varianten:
daten[variante][8][datereihe] bis daten[variante][10][datereihe]

Das heißt egal welche Variante per Index für 8-10 aktiviert ist, sind 0-7 immer identisch, es gibt eben nur eine Ausführung.

Wie bekomme ich das jetzt zusammen mit der Datenstruktur? Denn ohne 3D geht 8-10 nicht und mit 3D müsste ich ja X-Fach 0-7 speichern damit bei jeder Variante (die diese eigentlich nicht betrifft) auf die Werte zugegriffen werden kann.

Gruß
 
Mach dir halt ne Klasse / Struct mit zwei Membern :D

class sickStruck {
std::vector<<std::vector<int>> arr1;
std::vector<std::vector<<std::vector<int>>> arr2;
};

Spaß beiseite... das hört sich für mich komisch an. Mach lieber klare und einfache (und gerne auch mehrere) Datenstrukturen bevor du alles in einer hast, die dann total unübersichtlich wird. Genau wieder Code zur Weiterverarbeitung unangenehm wird.
 
Sobald ich es trennen würde, funktioniert das Indexprinzip aber nicht mehr was ich ja möchte von 0-10 :)
 
Mit Vektoren baust du zangsläufig immer n-dimensionale Quader
Dh du kannst zwar noch tricksen wenn du nur 8-10 brauchst und 0-7 nicht, indem du den Index "verschiebst" dh 8 liegt bei 0, 9 liegt bei 1 usw und du sorgst über klassen/structs und zugriffsfunktionen für diese indexverschiebung

Der beliebig flexible Ansatz um an jeder Stelle beliebig viele Lücken zu erlauben und nur die notwendige Speichermenge zu verwenden ist dann nich vectoren zu schachteln sondern stattdessen std::map oder std::unoredered_map
Key ist dann hier ein tupel x,y oder eben dein tripel x,y,z und value ist der Wert des innersten Vektors
Die unoredered_map erlaubt auch O(1) zugriff auf die Einträge falls es dich interessiert..
 
Danke das schaue ich mir mal an. Ich überlege auch gerade ob man sowas wie ein Zeiger oder Referenz Array 0-10 als Stellvertreterzugriff machen könnte und dann bei 0-7 auf die äussere Dimension fixiert und bei 8-10 auf die jeweils aktive zweite Dimension...
Ergänzung ()

Ich würde fast sagen das klappt sogar erstaunlich effektiv :)
Kann man so machen oder?

Code:
std::vector<std::vector<int>> arr1;
std::vector<std::vector<<std::vector<int>>> arr2;

std::vector<std::vector<int>> *zeiger_array[2]

zeiger_array[0] = &arr1;
zeiger_array[1] = &arr2[variante];

Jetzt kann man auf alles sauber per zeiger_array zugreifen.

Und wenn sich die variante ändert dann ändert man einfach entsprechend den Zeiger. So kann sogar alles getrennt rumliegen und trotzdem hat man alles in einer Liste.

Einziger Nachteil man muss zum abgreifen der Werte das *Sternchen vorstellen aber da die Werte eigentlich nur in Funktionen verarbeitet werden die sowieso den Wert per Referenz übergeben bekommen ist der Nachteil fast kein Nachteil.
Ergänzung ()

...

So dann der Aufruf:
Code:
funk(*zeiger_array[0], 0);

Dann nehm ich die ausbleibende Kritik mal zum Anlass zu glauben der Weg ist gut.
Am Ende ist man mit so einer Pointerlösung doch sehr fexibel, man kann eine Liste mit beliebig verschiedenen Datenstrukturen zusammenstellen sowie zwischendurch ändern und performancemäßig sollte es auch optimal sein. Schon fast ein bisschen KISS Prinzip.
 
Zuletzt bearbeitet:
Nö, KISS wäre einfach eine unordered_map, anstatt 3 mal vector zu nutzen, die auch noch unterschiedliche Dimensionen haben und voneinander abhängig sind.
 
Wie würde das aussehen, bräuchte ich dann nicht auch zwei maps um das unter zu bringen?
 
Nein, da du x, y und z zu einem key kombinieren kannst. Entweder mit einer passenden Datenstruktur oder mit ein klein wenig Mathe.
 
Die einen Daten bestehen aus yz und die anderen aus xyz. der index von y soll ja in der funktion mit übergeben werden damit die funktion einfach z nutzen kann. Wie steckt man in den key y und xy gleichzeitig wenn der Datentyp des Keys doch auf einen festgelegt ist?
 
Code:
        Dictionary<int, object> map = new Dictionary<int, object>();

        object Get(byte x, byte y, byte z)
        {
            var hash = GetHashCode(x, y, z);
            return map[hash];
        }

        void Set(byte x, byte y, byte z, object data)
        {
            var hash = GetHashCode(x, y, z);
            map[hash] = data;
        }

        int GetHashCode(byte x, byte y, byte z)
        {
            int hash = x << 16 | y << 8 | z;
            return hash;
        }

Das ist zwar jetzt C#, aber geht in C++ genau so.

Nachtrag: Wenn du die äußerste "Schicht" nicht brauchst, kannst du sie ja einfach auf 0 setzen.


Edit:

Noch einfacher per Überladung und definitiv Kollisionsfrei (ist es natürlich doch nicht - ist schon spät) (solltest du bei Nutzung der äußersten Schicht mal die 0 brauchen):

Code:
        Dictionary<int, object> map = new Dictionary<int, object>();

        object Get(byte x, byte y, byte z)
        {
            var hash = GetHashCode(x, y, z);
            return map[hash];
        }

        void Set(byte x, byte y, byte z, object data)
        {
            var hash = GetHashCode(x, y, z);
            map[hash] = data;
        }

        int GetHashCode(byte x, byte y, byte z)
        {
            int hash = x << 16 | y << 8 | z;
            return hash;
        }

        int GetHashCode(byte y, byte z)
        {
            int hash = y << 24 | z;
            return hash;
        }

Edit²:

Noch besser wäre es, wenn du beide dinge in 2 eigenständige maps machst - da es ja offensichtlich nicht die selbe Art von Daten sind - und dahingehend deine Logik anpasst.

Oder verrate uns was du darin speicherst, dann finden wir eventuell eine noch passendere Lösung.

Edit³:

Das sollte nun aber wirklich Kollisionsfrei sein:

Code:
        Dictionary<int, object> map = new Dictionary<int, object>();

        object Get(byte x, byte y, byte z)
        {
            var hash = GetHashCode(x, y, z);
            return map[hash];
        }

        object Get(byte y, byte z)
        {
            var hash = GetHashCode(y, z);
            return map[hash];
        }

        void Set(byte x, byte y, byte z, object data)
        {
            var hash = GetHashCode(x, y, z);
            map[hash] = data;
        }

        void Set(byte y, byte z, object data)
        {
            var hash = GetHashCode(y, z);
            map[hash] = data;
        }

        int GetHashCode(byte x, byte y, byte z)
        {
            return x << 16 | y << 8 | z;
        }

        int GetHashCode(byte y, byte z)
        {
            return 1 << 31 | y << 8 | z;
        }
 
Zuletzt bearbeitet:
Danke, um vorweg die Frage schon mal zu beantworten: Temperaturdaten
z index für die Temperatur-Reihen
y index für verschiedenen Intervalle (1min, 15min, etc)

ja und das nicht bei allen yz enthaltene x ist index für Rahmenbedingungen die teilweise existieren und teilweise nicht.

Von hier kommen zB die Daten http://www.soda-pro.com/web-services/radiation/helioclim-3-for-free
 
Willst du mal ein spezifisches Exemplar der Daten zeigen?

Wie willst du auf die Daten zugreifen?
Was machst du mit den Daten? (Durchsuchen, Iterieren (z.B. für eine Visualisierung oder Berechnung), ...)

Wenn du vorhast sie zu Durchsuchen würde sich wahrscheinlich ein Reverse-Index anbieten.

Deine Variante mit mehrdimensionalen Arrays ist zwar prinzipiell auch möglich, aber unschön (meiner Meinung nach) und praktisch überhaupt nicht flexibel.
 
Mit der unordered_map war es etwas fummeliger als ich erwartet habe (da man auch für standard-typen eine eigene std::hash Funktion implementieren muss), daher hab ich mir die Mühe gemacht hier ein lauffähiges Beispiel zu erstellen.
Abgeguckt habe ich hier: link
Da es mit dem tuple ganz schön nervt würde ich lieber ein eigenes struct/class als keyType verwenden aber so läufts.

Code:
#include <iostream>
#include <string>
#include <tuple>
#include <utility>
#include <unordered_map>

typedef std::tuple<std::string,int,char> keyType;

struct keyHash : public std::unary_function<keyType, std::size_t>
{
   std::size_t operator()(const keyType& k) const
   {
      return std::get<0>(k)[0] ^ std::get<1>(k) ^ std::get<2>(k);
   }
};

struct keyEqual : public std::binary_function<keyType, keyType, bool>
{
   bool operator()(const keyType& v0, const keyType& v1) const
   {
      return (
               std::get<0>(v0) == std::get<0>(v1) &&
               std::get<1>(v0) == std::get<1>(v1) &&
               std::get<2>(v0) == std::get<2>(v1)
             );
   }
};

struct valueType
{
   std::string x;
};

std::string keyToString(const keyType& key)
{
    std::string result = "";
    result += std::get<0>(key) + ",";
    result += std::to_string(std::get<1>(key)) + ",";
    result += std::get<2>(key);
    return result;
}

typedef std::unordered_map<keyType,valueType,keyHash,keyEqual> mapType;

int main()
{
   mapType myDB;
   // entry 1
   valueType d1;
   d1.x = "test entry 1";
   myDB[std::make_tuple("abc",1,'X')] = d1;

   // entry 2
   valueType d2;
   d2.x = "test entry 2";
   myDB[std::make_tuple("abc",2,'Y')] = d2;

   for (auto const& entry : myDB)
       std::cout << keyToString(entry.first) << " : " << entry.second.x << std::endl;

   return 0;
}

Output:
Man beachte, dass unordered_map nicht sortiert ist! (Wie der Name schon sagt :p)
Code:
abc,2,Y : test entry 2
abc,1,X : test entry 1
 
Zuletzt bearbeitet:
Zurück
Oben