kramann.info
© Guido Kramann

Login: Passwort:










Archiv
2 SoSe2022
..2.1 MIK
..2.2 SRT
..2.3 HDL
..2.4 AUT
..2.5 SLE
2 WS2020_21
..2.1 RTS
....2.1.1 day_by_day
..2.2 IE
....2.2.1 day_by_day
..2.3 ES
..2.4 EFSEE
....2.4.1 day_by_day
..2.5 KF
....2.5.1 day_by_day
....2.5.2 Haikus
....2.5.3 Haikus_en
..2.6 CC
....2.6.1 day_by_day
2 WS2021_22
..2.1 RTS
....2.1.1 day_by_day
....2.1.2 Versuch002
....2.1.3 Versuch003
....2.1.4 Versuch004
....2.1.5 Versuch005
....2.1.99 Material
..2.2 FTS
....2.2.1 day_by_day
..2.3 ESY
....2.3.1 day_by_day
..2.4 INFmecha5
....2.4.1 day_by_day
..2.5 REGmecha5
....2.5.1 day_by_day
2 WS2023_24
..2.1 day_by_day_RST
..2.2 day_by_day_SRT
..2.3 day_by_day_FTS
..2.4 day_by_day_KF
3 SoSe2021
..3.1 STR
....3.1.1 day_by_day
..3.2 SLE
....3.2.1 day_by_day
..3.3 HDL
....3.3.1 day_by_day
..3.4 MIK
....3.4.1 day_by_day
3 SoSe2024
..3.1 Mik_21_03_2024
..3.2 Mik_04_04_2024
..3.3 Mik_11_04_2024
..3.4 Mik_18_04_2024
..3.5 Mik_25_04_2024
..3.6 Mik_02_05_2024
..3.7 Mik_16_06_2024
..3.8 Mik_23_05_2024
..3.9 Mik_30_05_2024
..3.10 Mik_05_06_2024
..3.11 Mik_13_06_2024
3 WS2022_23
..3.1 day_by_day_RST_3MB
..3.2 day_by_day_RT2_5MT
..3.3 day_by_day_EMB_7MT
..3.4 day_by_day_ABP_7MT
..3.5 day_by_day_FTS_MMB
..3.6 day_by_day_KF
4 SoSe2023
..4.2 RTS_day_by_day
..4.3 MIK_day_by_day
..4.4 AUT_day_by_day
..4.5 HDL_day_by_day
4 WS2024_25
..4.1 ING_day_by_day
....4.1.1 ING_Do_26_09_2024
....4.1.2 ING_Do_10_10_2024
....4.1.3 ING_Do_17_10_2024
....4.1.4 ING_Do_24_10_2024
....4.1.5 ING_Do_07_11_2024
....4.1.6 ING_Do_14_11_2024
....4.1.7 ING_Do_21_11_2024
....4.1.8 ING_Do_28_11_2024
....4.1.9 ING_Do_05_12_2024
....4.1.10 ING_Do_12_12_2024
....4.1.11 ING_Do_19_12_2024
..4.2 INF_day_by_day
....4.2.1 INF_Fr_27_09_2024
....4.2.2 INF_Fr_04_10_2024
....4.2.3 INF_FR_18_10_2024
....4.2.4 INF_Fr_25_10_2024
....4.2.5 INF_Fr_08_11_2024
....4.2.6 INF_Fr_15_11_2024
....4.2.7 INF_Fr_22_11_2024
....4.2.8 INF_Fr_29_11_2024
....4.2.9 INF_Fr_06_12_2024
....4.2.10 INF_Fr_13_12_2024
....4.2.11 INF_Fr_20_12_2024
....4.2.12 INF_Fr_10_01_2025
..4.3 FTS_day_by_day
....4.3.1 FTS_Mi_25_09_2024
....4.3.2 FTS_Mi_02_10_2024
....4.3.3 FTS_Mi_09_10_2024
....4.3.4 FTS_Mi_16_10_2024
....4.3.5 FTS_Mi_23_10_2024
....4.3.6 FTS_Mi_30_10_2024
....4.3.7 FTS_Mi_06_11_2024
....4.3.8 FTS_Mi_13_11_2024
....4.3.9 FTS_Mi_20_11_2024
....4.3.10 FTS_Mi_27_11_2024
....4.3.11 FTS_Mi_04_12_2024
....4.3.12 FTS_Mi_11_12_2024
....4.3.13 FTS_Mi_18_12_2024
6 Ing
..6.1 Bauplan
....6.1.1 Bootstick
....6.1.2 Xubuntu
....6.1.3 Webserver
....6.1.4 Hotspot
....6.1.5 Videostream
....6.1.6 Lampe
....6.1.7 Chassis
....6.1.8 Akku
....6.1.9 Motore
....6.1.10 Laborsteckboard
....6.1.11 Antriebstest
7 007
..7.1 Einschalten
..7.2 Vorversuche
kramann.info
© Guido Kramann

Login: Passwort:




Inhalte zur Vorlesungswoche #10, Informatik 1 im Wintersemester 2024/25

(EN google-translate)

(PL google-translate)

Thema: Implementierung einer genetischen Optimierung am Beispiel der Generierung von Rorschach-Bilder


https://de.wikipedia.org/wiki/Rorschachtest

  1. Generierung und Darstellung von gespiegelten Zufallsbildern
  2. Definition der Gene
  3. Definition der Fehlerfunktion
  4. Implementierung des genetische Algorithmus

Sollte noch Zeit bleiben: Kleine Beispiele zu allem, was bisher an Sprachmitteln in C/C++ durchgenommen wurde.


Zusammenstellung wichtiger besonderer für das Rorschachprojekt nützlicher Funktionen


//Grundgerüst:
#include <iostream> //nötig für cin cout
#include <cstring> // nötig für strcpy()
#include <unistd.h> // nötig für usleep
#include <math.h>   // nötig für sqrt()
#include <ctime>    // nötig für Initialisierung der Pseudozufallsfolge

using namespace std;
int main(void)
{
    return 0;
}

//Nützliche Funktionen:

srand(static_cast<unsigned int>(time(0))); //Initialisierung Zufallsprozess
srand(2); //Immer die gleichen "Zufallsgewichte"

//Zufallszahl erzeugen
double zufall()
{
    return (double)(rand()%10000)*0.02-100.0; // Wertebereich: +/-99.99
}

cout<<"\033c"; //Reset Terminal

usleep(300000);// 300000 Mikrosekunden "schlafen"

//Mehrere Texte bereitstellen:
char feld[][22] = {
       //..........x..........
       //012345678901234567890
        "*     *    *    *    ",
        "                     ",
        "*   *    *    *     *",
        "          *          ",
        "                     ",
        "  *        *         ",
        "                     ",
        "      *      *      *",
        "*       *        *  *",
        "                     ",
        "            *       *",
        "                     ",
        "                     ",
        "   *              *  "
    };


Code 0-1: Programmentwurf zur Erzeugung von Rorschachbildern

Problem 1: Bild erzeugen und darstellen:

#include <iostream> //nötig für cin cout
#include <cstring> // nötig für strcpy()
#include <unistd.h> // nötig für usleep
#include <math.h>   // nötig für sqrt()
#include <ctime>    // nötig für Initialisierung der Pseudozufallsfolge

using namespace std;
int main(void)
{
char feld[][11] = {
       //0123456789
        "          ",
        "  *       ",
        "          ",
        "     *    ",
        "          ",
        "          ",
        "   *      ",
        "          ",
        "     *    ",
        "          "
    };    

    for(int i=0;i<10;i++)
        cout<<feld[i]<<endl;


    return 0;
}

Code 0-2: Problem 1: Bild erzeugen und darstellen.

Problem 2: Definition der Fehlerfunktion

  1. Zusammenhang der Pixel
  2. Eine gewissen Mindest- und Maximalanzahl der Pixel (z.B. 30...60%)
  3. Zusammenhang mit der Symmetrieachse (also rechte Spalte bei Bildhälfte)

Problem 3: Implementierung des Genetischen Algorithmus

Teillösung: Gespiegelte Zufallsbilder und DNA:

#include <iostream> //nötig für cin cout
#include <cstring> // nötig für strcpy()
#include <unistd.h> // nötig für usleep
#include <math.h>   // nötig für sqrt()
#include <ctime>    // nötig für Initialisierung der Pseudozufallsfolge

#define SPALTEN 10
#define DNAANZAHL 100
#define PIXELZAHL (SPALTEN*SPALTEN)

using namespace std;

void zeigeBild(int bild[])
{
     int inx = 0;
     cout<<"c"; //Reset Terminal
     for(int i=0;i<SPALTEN;i++) //Zeilen des Bildes
     {
         for(int k=0;k<SPALTEN;k++) //Spalten des Bilden
         {
             if(bild[inx]==1)
                 cout<<'*';
             else
                 cout<<' ';

             inx++; //zählt 0..99 durch das eindimensional repräsentierte Bild (alle Pixel hintereinander)
         }
         //Spiegelung der Zeile
         //inx-=SPALTEN;
         inx--;
         for(int k=SPALTEN-1;k>=0;k--) //Spalten des Bilden
         {
             if(bild[inx]==1)
                 cout<<'*';
             else
                 cout<<' ';

             inx--; //zählt 0..99 durch das eindimensional repräsentierte Bild (alle Pixel hintereinander)
         }
         inx+=SPALTEN;
         cout<<endl;
     }
}

int main(void)
{
     srand(static_cast<unsigned int>(time(0))); //Initialisierung Zufallsprozess
     int dna[DNAANZAHL][PIXELZAHL];

     //100 Zufallsbilder erzeugen:
     for(int i=0;i<DNAANZAHL;i++)
     {
          for(int k=0;k<PIXELZAHL;k++)
          {
                dna[i][k]=rand()%2;
          }          
     }

    for(int i=0;i<DNAANZAHL;i++)
    {
        zeigeBild(dna[i]);
        usleep(3000000);// 300000 Mikrosekunden "schlafen"
    }
    return 0;
}

Code 0-3: Teillösung: Gespiegelte Zufallsbilder und DNA.

Screenshot.

Bild 0-1: Screenshot.

Teillösung 2: Berechnung der Fehlerfunktion

#include <iostream> //nötig für cin cout
#include <cstring> // nötig für strcpy()
#include <unistd.h> // nötig für usleep
#include <math.h>   // nötig für sqrt()
#include <ctime>    // nötig für Initialisierung der Pseudozufallsfolge

#define SPALTEN 10
#define DNAANZAHL 100
#define PIXELZAHL (SPALTEN*SPALTEN)

using namespace std;

double fehlerfunktion(int bild[])
{
   double err=0.0;

//1. Eine gewissen Mindest- und Maximalanzahl der Pixel (z.B. 30...60%)
   int anzahlPixel30Prozent = (PIXELZAHL*30)/100;
   int anzahlPixel60Prozent = (PIXELZAHL*60)/100;
   int anzahl = 0;
   for(int i=0;i<PIXELZAHL;i++)
       if(bild[i]==1)
           anzahl++;
   if(anzahl<anzahlPixel30Prozent)
       err+=(double)(anzahlPixel30Prozent-anzahl);
       //identisch zu err=err+(double)(anzahlPixel30Prozent-anzahl);
   if(anzahl>anzahlPixel60Prozent)
       err+=(double)(anzahl-anzahlPixel60Prozent);

//2. Zusammenhang mit der Symmetrieachse (also rechte Spalte bei Bildhälfte)
   anzahlPixel30Prozent = (SPALTEN*30)/100;
   anzahlPixel60Prozent = (SPALTEN*60)/100;
   anzahl = 0;
   for(int i=0;i<SPALTEN;i++)
       if(bild[SPALTEN-1 + SPALTEN*i]==1)
           anzahl++;
   if(anzahl<anzahlPixel30Prozent)
       err+=(double)(anzahlPixel30Prozent-anzahl);
       //identisch zu err=err+(double)(anzahlPixel30Prozent-anzahl);
   if(anzahl>anzahlPixel60Prozent)
       err+=(double)(anzahl-anzahlPixel60Prozent);
//3. Zusammenhang der Pixel
   //  Wie komme ich eine Spalte weiter?: index+1
   //  Wie komme ich eine Zeile  weiter?: index+SPALTEN
   int inx=0;
   for(int i=0;i<SPALTEN;i++)
   {
       for(int k=0;k<SPALTEN;k++)
       {
            //wenn nicht Rand, wenn Pixel gesetzt und linker Nachbar nicht, dann Fehler
            if(k>0 && bild[inx]>0 && bild[inx-1]==0)
                err+=10.0;
            //wenn nicht Rand, wenn Pixel gesetzt und rechter Nachbar nicht, dann Fehler
            if(k<SPALTEN-1 && bild[inx]>0 && bild[inx+1]==0)
                err+=10.0;
            //kein Nachbar oben
            if(i>0 && bild[inx]>0 && bild[inx-SPALTEN]==0)
                err+=5.0;
            //kein Nachbar unten
            if(i<((SPALTEN-1)*SPALTEN) && bild[inx]>0 && bild[inx+SPALTEN]==0)
                err+=5.0;
            inx++;
       }
   }
   return err;
}

void zeigeBild(int bild[])
{
     int inx = 0;
     cout<<"c"; //Reset Terminal
     for(int i=0;i<SPALTEN;i++) //Zeilen des Bildes
     {
         for(int k=0;k<SPALTEN;k++) //Spalten des Bilden
         {
             if(bild[inx]==1)
                 cout<<'*';
             else
                 cout<<' ';

             inx++; //zählt 0..99 durch das eindimensional repräsentierte Bild (alle Pixel hintereinander)
         }
         //Spiegelung der Zeile
         //inx-=SPALTEN;
         inx--;
         for(int k=SPALTEN-1;k>=0;k--) //Spalten des Bilden
         {
             if(bild[inx]==1)
                 cout<<'*';
             else
                 cout<<' ';

             inx--; //zählt 0..99 durch das eindimensional repräsentierte Bild (alle Pixel hintereinander)
         }
         inx+=SPALTEN;
         cout<<endl;
     }
}

int main(void)
{
     srand(static_cast<unsigned int>(time(0))); //Initialisierung Zufallsprozess
     int dna[DNAANZAHL][PIXELZAHL];

     //100 Zufallsbilder erzeugen:
     for(int i=0;i<DNAANZAHL;i++)
     {
          for(int k=0;k<PIXELZAHL;k++)
          {
                dna[i][k]=rand()%2;
          }          
     }

    for(int i=0;i<DNAANZAHL;i++)
    {
        zeigeBild(dna[i]);
        double err = fehlerfunktion(dna[i]);
        cout<<"err="<<err<<endl;
        usleep(300000);// 300000 Mikrosekunden "schlafen"
    }
    return 0;
}

Code 0-4: Teillösung 2: Berechnung der Fehlerfunktion.

Vollständige Lösung nach einigen Verbesserungen an den Optimierungsparametern
#include <iostream> //nötig für cin cout
#include <cstring> // nötig für strcpy()
#include <unistd.h> // nötig für usleep
#include <math.h>   // nötig für sqrt()
#include <ctime>    // nötig für Initialisierung der Pseudozufallsfolge

#define SPALTEN 15
#define DNAANZAHL 150
#define PIXELZAHL (SPALTEN*SPALTEN)

using namespace std;

double fehlerfunktion(int bild[])
{
   double err=0.0;

//1. Eine gewissen Mindest- und Maximalanzahl der Pixel (z.B. 30...60%)
   int anzahlPixel30Prozent = (PIXELZAHL*30)/100;
   int anzahlPixel60Prozent = (PIXELZAHL*60)/100;
   int anzahl = 0;
   for(int i=0;i<PIXELZAHL;i++)
       if(bild[i]==1)
           anzahl++;
   if(anzahl<anzahlPixel30Prozent)
       err+=(double)(anzahlPixel30Prozent-anzahl);
       //identisch zu err=err+(double)(anzahlPixel30Prozent-anzahl);
   if(anzahl>anzahlPixel60Prozent)
       err+=(double)(anzahl-anzahlPixel60Prozent);

//2. Zusammenhang mit der Symmetrieachse (also rechte Spalte bei Bildhälfte)
   anzahlPixel30Prozent = (SPALTEN*30)/100;
   anzahlPixel60Prozent = (SPALTEN*60)/100;
   anzahl = 0;
   for(int i=0;i<SPALTEN;i++)
       if(bild[SPALTEN-1 + SPALTEN*i]==1)
           anzahl++;
   if(anzahl<anzahlPixel30Prozent)
       err+=(double)(anzahlPixel30Prozent-anzahl);
       //identisch zu err=err+(double)(anzahlPixel30Prozent-anzahl);
   if(anzahl>anzahlPixel60Prozent)
       err+=(double)(anzahl-anzahlPixel60Prozent);
//3. Zusammenhang der Pixel
   //  Wie komme ich eine Spalte weiter?: index+1
   //  Wie komme ich eine Zeile  weiter?: index+SPALTEN
   int inx=0;
   for(int i=0;i<SPALTEN;i++)
   {
       for(int k=0;k<SPALTEN;k++)
       {
            //wenn nicht Rand, wenn Pixel gesetzt und linker Nachbar nicht, dann Fehler
            if(k>0 && bild[inx]>0 && bild[inx-1]==0)
                err+=1.0;
            //wenn nicht Rand, wenn Pixel gesetzt und rechter Nachbar nicht, dann Fehler
            if(k<SPALTEN-1 && bild[inx]>0 && bild[inx+1]==0)
                err+=1.0;
            //kein Nachbar oben
            if(i>0 && bild[inx]>0 && bild[inx-SPALTEN]==0)
                err+=1.0;
            //kein Nachbar unten
            if(i<((SPALTEN-1)*SPALTEN) && bild[inx]>0 && bild[inx+SPALTEN]==0)
                err+=1.0;
            inx++;
       }
   }

//4. Neue Idee: Abstand zum rechten Rand minimieren und zur Mitte
   inx=0;
   for(int i=0;i<SPALTEN;i++)
   {
       for(int k=0;k<SPALTEN;k++)
       {
            if(bild[inx]>0)
            {
                err+=(double)(SPALTEN-1-k)*0.03;
                if(i<SPALTEN/2)
                    err+=(double)(SPALTEN/2-i)*0.015;
                else
                    err+=(double)(i-SPALTEN/2)*0.015;
                
            }
            inx++;
       }
   }   

   return err;
}

void zeigeBild(int bild[])
{
     int inx = 0;
     cout<<"c"; //Reset Terminal
     for(int i=0;i<SPALTEN;i++) //Zeilen des Bildes
     {
         for(int k=0;k<SPALTEN;k++) //Spalten des Bilden
         {
             if(bild[inx]==1)
                 cout<<'*';
             else
                 cout<<' ';

             inx++; //zählt 0..99 durch das eindimensional repräsentierte Bild (alle Pixel hintereinander)
         }
         //Spiegelung der Zeile
         //inx-=SPALTEN;
         inx--;
         for(int k=SPALTEN-1;k>=0;k--) //Spalten des Bilden
         {
             if(bild[inx]==1)
                 cout<<'*';
             else
                 cout<<' ';

             inx--; //zählt 0..99 durch das eindimensional repräsentierte Bild (alle Pixel hintereinander)
         }
         inx+=SPALTEN;
         cout<<endl;
     }
}

int main(void)
{
     srand(static_cast<unsigned int>(time(0))); //Initialisierung Zufallsprozess
     int dna[DNAANZAHL][PIXELZAHL];

     //100 Zufallsbilder erzeugen:
     for(int i=0;i<DNAANZAHL;i++)
     {
          for(int k=0;k<PIXELZAHL;k++)
          {
                dna[i][k]=rand()%2;
          }           
     }

    cout<<"Genetische Optimierung"<<endl;
    double alle_fehler[DNAANZAHL];
    double ERR_BEST = 99999999999.99;
    for(int DURCHLAUF = 0; DURCHLAUF<500;DURCHLAUF++)
    {
       //Alle Fehler der i. Generation berechnen:       
       for(int i=0;i<DNAANZAHL;i++)
            alle_fehler[i] = fehlerfunktion(dna[i]);
       //Das beste 10tel finden
       //Bubblesort:
       int hilfsarray[PIXELZAHL];
       for(int i=0;i<DNAANZAHL*2;i++)
       {
             for(int k=1;k<DNAANZAHL;k++)
             {
                  if(alle_fehler[k]<=alle_fehler[k-1])  
                  {
                       double hilf = alle_fehler[k];
                       alle_fehler[k] = alle_fehler[k-1];
                       alle_fehler[k-1] = hilf;

                       //Arraytausch
                       for(int p=0;p<PIXELZAHL;p++)
                           hilfsarray[p]=dna[k][p];
                       for(int p=0;p<PIXELZAHL;p++)
                           dna[k][p]=dna[k-1][p];
                       for(int p=0;p<PIXELZAHL;p++)
                           dna[k-1][p]=hilfsarray[p];
                  }
             }
       }

       if(alle_fehler[0]<ERR_BEST)
       {
           cout<<"Durchlauf Nr."<<DURCHLAUF<<" Kleinster Fehler: "<<alle_fehler[0]<<endl;
           ERR_BEST = alle_fehler[0];
       }

       // 1/10 behalten und den Rest rekombinieren:
       int inx_einzehntel = DNAANZAHL/10;
       for(int i=inx_einzehntel;i<DNAANZAHL;i++)
       {
            //Auswahl zweier der besten:
            int i1 = rand()%inx_einzehntel;
            int i2 = (i1 + 1 + rand()%(inx_einzehntel-1))%inx_einzehntel;

            for(int k=0;k<PIXELZAHL;k++)
                if(rand()%2==0)
                    dna[i][k] = dna[i1][k];
                else
                    dna[i][k] = dna[i2][k];
 
       }
       // Mutationen
       for(int i=0;i<DNAANZAHL*5;i++)
       {
           int inx = inx_einzehntel + 1 + rand()%(DNAANZAHL-inx_einzehntel-1);
           int pix = rand()%PIXELZAHL;
           dna[inx][pix] = 1 - dna[inx][pix]; // 1=>0 0=>1
       }
    }

    cout<<"In 4 Sekunden erfolgt die Ausgabe aller optimierten Bilder:"<<endl;
    usleep(4000000);
    for(int i=DNAANZAHL-1;i>=0;i--)
    {
        zeigeBild(dna[i]);
        double err = fehlerfunktion(dna[i]);
        cout<<"err="<<err<<endl;
        usleep(30000);// 300000 Mikrosekunden "schlafen"
    }
    return 0;
}

Code 0-5: Vollständige Lösung nach einigen Verbesserungen an den Optimierungsparametern.

  • Einige Ergebnisse:
Generierte Rorschachfigur 1.

Bild 0-2: Generierte Rorschachfigur 1.

Generierte Rorschachfigur 2.

Bild 0-3: Generierte Rorschachfigur 2.

Generierte Rorschachfigur 3.

Bild 0-4: Generierte Rorschachfigur 3.

Generierte Rorschachfigur 4.

Bild 0-5: Generierte Rorschachfigur 4.

Generierte Rorschachfigur 5.

Bild 0-6: Generierte Rorschachfigur 5.

Generierte Rorschachfigur 6.

Bild 0-7: Generierte Rorschachfigur 6.