Erstellen von korrelierten Arrays



  • Hallo

    Du hast meine Frage richtig verstanden. Erstmal danke für diesen Verweis. ich werde mir das auf jedenfall aus Interesse ansehen. random_shuffle hat mich sehr abgeschreckt, auch weil ich noch keine Ahnung von Templates habe, aber ich möchte das noch erlernen.

    Zu dem Beispiel aus meiner Eingangsfrage, würdest du mir bitte da auch helfen wie das richtig gecoded wird? Ich werde solche Sache noch öfters brauchen und mir fehlen noch viele C++ Basics. Deswegen möchte ich das erlernen.

    Danke und Gruß
    cpp_Jungspund


  • Mod

    • 1D mit random shuffle und statischem Array:
    #include <iostream>
    #include <algorithm>
    #include <iterator>
    
    int main()
    {
      const int ADSIZE = 25; // Wenn das bei dir ohne const funktioniert hat, dann hast
                             // du eine unportable Spracherweiterung deines Compiler benutzt.
      int muster[ADSIZE] = {};
    
      int num_ones = 7;
      std::fill(muster, muster + num_ones, 1);
      std::random_shuffle(std::begin(muster), std::end(muster));
    
      for(auto i : muster)
        std::cout << i << ' ';
      std::cout << '\n';
    }
    
    • 2D:
    #include <iostream>
    #include <algorithm>
    #include <iterator>
    
    int main()
    {
      const int ADSIZE = 25;
      const int MAX_MUSTER = 3;
      int muster[MAX_MUSTER][ADSIZE] = {};
    
      int num_ones = 21;
      std::fill(*muster, *muster + num_ones, 1);
      std::random_shuffle(*std::begin(muster), *std::end(muster));
    
      for(const auto &i : muster)
        {
          for(auto j : i)
            std::cout << j << ' ';
          std::cout << '\n';
        }
    }
    
    • Oder etwas allgemeiner:
    std::fill(std::begin(*std::begin(muster)), std::begin(*std::begin(muster)) + num_ones, 1);
      std::random_shuffle(std::begin(*std::begin(muster)), std::begin(*std::end(muster)));
    
    • Dann kann man nämlich auch andere Container nutzen, ohne den Code zu ändern (Das ist der große Vorteil von Templates: sie funktionieren mit allen Typen!):
    #include <iostream>
    #include <algorithm>
    #include <iterator>
    #include <array>
    
    int main()
    {
      const int ADSIZE = 25;
      const int MAX_MUSTER = 3;
      std::array<std::array<int, ADSIZE>, MAX_MUSTER> muster = {{}};
    
      int num_ones = 21;
      std::fill(std::begin(*std::begin(muster)), std::begin(*std::begin(muster)) + num_ones, 1);
      std::random_shuffle(std::begin(*std::begin(muster)), std::begin(*std::end(muster)));
    
      for(const auto &i : muster)
        {
          for(auto j : i)
            std::cout << j << ' ';
          std::cout << '\n';
        }
    }
    
    • Oder wir sparen uns das unnötige Füllen mit der nötigen Menge an Nullen und Einsen mit anschließendem Mischen und machen stattdessen Mathematik. Das heißt, wir erzeugen gleich die richtige Verteilung ohne Umschweife (und ich hoffe stark, dass ich mich nicht verrechnet habe, sonst wäre das echt peinlich 🙂 ):
    #include <iostream>
    #include <algorithm>
    #include <iterator>
    #include <array>
    #include <cstdlib>
    
    struct zero_one_generator
    {
      int num_ones;
      int total_size;
      int num_generated;
      zero_one_generator(int num_ones, int total_size): num_ones(num_ones), total_size(total_size), num_generated(0) {}
      int operator()()
      {
        double prob = static_cast<double>(num_ones)/(total_size - num_generated++);
        if (static_cast<double>(rand()) / RAND_MAX < prob)
          {
            num_ones--;
            return 1;
          }
        else
          {
            return 0;
          }
      }
    };
    
    int main()
    {
      const int ADSIZE = 25;
      const int MAX_MUSTER = 3;
      std::array<std::array<int, ADSIZE>, MAX_MUSTER> muster;
    
      int num_ones = 21;
      std::generate(std::begin(*std::begin(muster)), std::begin(*std::end(muster)), zero_one_generator(num_ones, ADSIZE*MAX_MUSTER));
    
      for(const auto &i : muster)
        {
          for(auto j : i)
            std::cout << j << ' ';
          std::cout << '\n';
        }
    }
    

    Mag zwar etwas umständlicher aussehen, aber der Code macht dafür gleich das was man möchte, anstatt Umwege zu gehen.

    • Wenn es aus irgendwelchen Gründen ganz ohne jede Templates gehen soll (an denen aber eigentlich gar nichts schwieriges oder geheimnisvolles ist), wird der Code unschöner:
    #include <iostream>
    #include <cstdlib>
    
    struct zero_one_generator
    {
      int num_ones;
      int total_size;
      int num_generated;
      zero_one_generator(int num_ones, int total_size): num_ones(num_ones), total_size(total_size), num_generated(0) {}
      int operator()()
      {
        double prob = static_cast<double>(num_ones)/(total_size - num_generated++);
        if (static_cast<double>(rand()) / RAND_MAX < prob)
          {
            num_ones--;
            return 1;
          }
        else
          {
            return 0;
          }
      }
    };
    
    int main()
    {
      const int ADSIZE = 25;
      const int MAX_MUSTER = 3;
      int muster[MAX_MUSTER][ADSIZE];
    
      int num_ones = 21;
      zero_one_generator generator(num_ones, MAX_MUSTER*ADSIZE);
      for(int i = 0; i < MAX_MUSTER; ++i)
        {
          for(int j = 0; j < ADSIZE; ++j)
            {
              muster[i][j] = generator();
            }
        }
    
      for(int i = 0; i < MAX_MUSTER; ++i)
        {
          for(int j = 0; j < ADSIZE; ++j)
            std::cout << muster[i][j] << ' ';
          std::cout << '\n';
        }
    }
    
    • Oder wenn's sogar ohne (selbst definierte) Klassen sein muss:
    #include <iostream>
    #include <cstdlib>
    
    int main()
    {
      const int ADSIZE = 25;
      const int MAX_MUSTER = 3;
      int muster[MAX_MUSTER][ADSIZE];
    
      int num_ones = 21;
      int num_generated = 0;
      int total_size = MAX_MUSTER*ADSIZE;
      for(int i = 0; i < MAX_MUSTER; ++i)
        {
          for(int j = 0; j < ADSIZE; ++j)
            {
              double prob = static_cast<double>(num_ones)/(total_size - num_generated++);
              if (static_cast<double>(rand()) / RAND_MAX < prob)
                {
                  num_ones--;
                  muster[i][j] = 1;
                }
              else
                {
                  muster[i][j] = 0;
                }
    
            }
        }
    
      for(int i = 0; i < MAX_MUSTER; ++i)
        {
          for(int j = 0; j < ADSIZE; ++j)
            std::cout << muster[i][j] << ' ';
          std::cout << '\n';
        }
    }
    


  • Dann kann man nämlich auch andere Container nutzen

    Gibt es außer int[][] und std::array<std::array<>> noch einen Container, der diese Art von Iterierung unterstützt?

    Ach ja: random_shuffle wurde ersetzt durch shuffle.


  • Mod

    wx++ schrieb:

    Dann kann man nämlich auch andere Container nutzen

    Gibt es außer int[][] und std::array<std::array<>> noch einen Container, der diese Art von Iterierung unterstützt?

    Jeder STL-konforme Container, der eine zusammenhängende 2D-Struktur darstellt. Ein Beispiel wäre ein vector<array>, aber natürlich auch irgendwelche selbst geschriebenen Container oder Containeradapter (z.B. ein 2D-Wrapper um einen vector), solange sie die nötigen Teile der STL-Schnittstelle umsetzen.

    Der Knackpunkt ist natürlich das "zusammenhängend", weil das bei den meisten 2D-Containern nicht gegeben ist. Aber da dies das herausragende Feature eines 2D-Arrays ist, vermutete ich mal, dass diese Eigenschaft dem Threadersteller wichtig ist und dies daher keine große Einschränkung darstellt. Zumal die Alternative eher ekelig wird, zumindest wenn man alles in einem Rutsch machen möchte. Spontan würde ich da eine Art Iteratoradapter für schreiben.



  • Hi

    Vielen Dank für die Antworten. Ich werde mich jetzt hinsetzen und versuchen das zu verstehen.

    Grüße
    cpp_Jungspund



  • Nochmal Hallo

    Würdest du mir bitte bei folgendem Code von dir erklären was in der for Schleife mit der if-Schleife darinnen genau passiert? Der Code funktioniert ganz phantastisch, ich kapier nur nicht genau was du da berechnest und was die Variablen darin genau machen.

    #include <iostream>
    #include <cstdlib>
    
    int main()
    {
      const int ADSIZE = 25;
      const int MAX_MUSTER = 3;
      int muster[MAX_MUSTER][ADSIZE];
    
      int num_ones = 21;
      int num_generated = 0;
      int total_size = MAX_MUSTER*ADSIZE;
      for(int i = 0; i < MAX_MUSTER; ++i)
        {
          for(int j = 0; j < ADSIZE; ++j)
            {
              double prob = static_cast<double>(num_ones)/(total_size - num_generated++);
              if (static_cast<double>(rand()) / RAND_MAX < prob)
                {
                  num_ones--;
                  muster[i][j] = 1;
                }
              else
                {
                  muster[i][j] = 0;
                }
    
            }
        }
    
    }
    

    Vielen Dank für die Hilfe und Grüße
    cpp_Jungspund


  • Mod

    Obligatorischer Link: www.if-schleife.de

    Die Idee ist: Wir wissen, wie viele Einsen insgesamt gesetzt werden sollen. Wir wissen auch, wie viele Elemente wir insgesamt noch zu setzen haben (also die Anzahl Einsen und Nullen zusammen). Das heißt, die Wahrscheinlichkeit, eine Eins zu setzen ist eigentlich
    (Anzahl zu setzender Einsen)/(Gesamtzahl zu setzender Elemente)
    Wenn wir über den gesamten Bereich diese Wahrscheinlichkeit annehmen, haben wir aber das Problem, dass die Einsen zwar gleichmäßig verteilt werden, die gewünschte Anzahl zu setzender Einsen jedoch nur im Durchschnitt über viele Läufe erreicht wird. Wir wollen hier aber die genaue Zahl der Einsen in jedem Durchlauf garantiert haben und trotzdem sollen die Einsen gleichverteilt sein.

    Und hier kommt die Stelle, an der ich ehrlich gesagt nur intuitiv geschätzt (und getestet) habe, ohne es genau zu berechnen: Wenn wir bei jedem zu setzenden Element erneut berechnen, wie viele Einsen noch zu setzen sind (anhand der mitgezählten Anzahl der bereits gesetzten Einsen) und daraus die neue Wahrscheinlichkeit zum Setzen einer Eins berechnen (anhand der noch zu setzenden Elemente), dann sollte die resultierende Verteilung der Einsen immer noch gleichmäßig sein, aber die totale Anzahl der Einsen ist fest. Denn wenn wir am Anfang überdurchschnittlich viele Einsen gesetzt haben sollten, dann sinkt die Wahrscheinlichkeit weiterer Einsen, notfalls bis aus 0. Und umgekehrt kann, wenn noch viele Einsen fehlen, die Wahrscheinlichkeit zum Setzen einer Eins gegen 1 gehen.

    Genau das macht der Code. Wenn du Fragen zu konkreten Stellen im Code hast, dann stell auch konkrete Fragen. Ich werde dir nicht auf gut Glück erklären, was eine Schleife oder eine Division ist, bloß weil du keine genaue Frage stellst.

    num_ones ist die Anzahl noch zu setzender Einsen.
    num_generated ist die Gesamtzahl bereits gesetzter Elemente (Nullen und Einsen).
    total_size ist die Gesamtzahl aller zu setzenden Elemente.
    prob ist die Wahrscheinlichkeit, dass das aktuelle Element eine Eins werden soll



  • Das hat mir sehr weiter geholfen. Vielen Dank.

    Was mir dabei auffällt ist, dass die feste Zahl der 1er zwar so passt, es aber passieren kann, dass mehrere 1er in den gleichen Zeilen stehen. Ich frage mich wie man den Code abändern müsste, um in jeder Zeile auch nur eine 1 zu haben. Entsprechend muss die Zahl der 1er dann die Zahl der Zeilen sein.

    Das übergeordnete Ziel für mich ist ein Programm zu schreiben, welches steuern kann in welchem Maß die Zeilen orthogonal zu einander sind, spricht ob 1er in der gleichen Komponente stehen oder nicht.

    Meine Idee ist da noch irgendwie eine Schleife um den Code von dir herum zu bauen, der den Hammingabstand berechnet und dann eine 1 setzt oder 0. Ob das aber zielführend ist weiss ich nicht. Muss da noch mehr recherchieren.


  • Mod

    🙄

    Toll! Dann war ja jede Hilfe umsonst, denn du hast nicht nach dem gefragt, was du wolltest, sondern nach etwas völlig anderem, von dem du bloß dachtest, dass es dir eventuell helfen könnte. Weniger noch: Von dem du nicht einmal einen genauen Plan hattest, ob oder wie es dir weiter helfen könnte, bloß dass es irgendwie ähnliche Elemente enthält. Nun hast du nach viel verschwendeter Helferszeit also eine Lösung für ein Problem, das du nie hattest und die dir bei deinem eigentlichen Problem überhaupt nicht weiter hilft.

    In Fachkreisen nennt man so etwas übrigens ein XY-Problem



  • Es geht mir nicht nur um das Problem mit dem Maß für die Orthogonalität sondern ich will auch C++ allgemein erlernen und ich bin dir daher sehr dankbar für deine Erklärungen und den Code. Didaktisch im Sinne von C++ war das sehr wertvoll für mich, daher sehe ich das nicht als verschwendete Helferzeit sondern freue mich darüber, dass ich dazu lernen kann.


Anmelden zum Antworten