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 #8, Informatik 1 im Wintersemester 2024/25

(EN google-translate)

(PL google-translate)

Themen

  1. Rekapitulation der letzten Übung mit einem einfachen Neuronalen Netz
  2. Einführung zu Optimierungsverfahren
  3. Implementierung eines modifizierten Gradientenverfahrens

1 Rekapitulation der letzten Übung mit einem einfachen Neuronalen Netz

siehe hier unten: 04_WS2024_25/02_INF_day_by_day/07_INF_Fr_22_11_2024

2 Einführung zu Optimierungsverfahren

  1. Was sind Heuristiken?
  2. Wie arbeitet ein Gradientenverfahren bei der Optimierung?
  3. Was sind genetische Algorithmen und wann kommen sie zum Einsatz?
  4. Was ist das Backpropagation-Verfahren?

Was sind Heuristiken? -- 50_Simulationstechnik/06_Optimierung/02_Heuristiken

Wie arbeitet ein Gradientenverfahren bei der Optimierung? -- 50_Simulationstechnik/06_Optimierung/01_Gradientenverfahren

Was sind genetische Algorithmen und wann kommen sie zum Einsatz? -- 50_Simulationstechnik/07_Genalgorithmus

Was ist das Backpropagation-Verfahren? -- 67_Echtzeitsysteme/08_NeuronaleNetze/02_Backpropagation

3 Implementierung eines modifizierten Gradientenverfahrens

Mit Hilfe eines modifizierten Gradientenverfahrens lassen sich bei einfachen Netzen in einem überwachten Lernprozess bessere Ergebnisse erzielen, als mit blossem Testen einer Folge von Zufallswerten.

Das nachfolgende Netz wurde mittels eines modifizierten Gradientenverfahrens als UND-Gatter belernt:

#include <iostream>
#include <math.h>
using namespace std;

double sigmoid(double d, double u)
{
    double x = 1.0/(1.0+exp(-d*u));
    return x;
}

double netz(double in0, double in1)
{
    //ineffizient, aber komfortabel, die Arrays jedesmal in der
    //Funktion neu zu erzeugen:
    double w[] = {7.605,-15.22,514.936,-312.267,-806.885,403.354};    
    double out[] = {0.0,0.0,0.0};    
    double in[] = {0.0,0.0};
    double D=0.1;

    in[0] = in0;
    in[1] = in1;

    //Berechnung der Ausgänge der Eingangsneuronenschicht:
    out[0] = sigmoid(D,in[0]*w[0]+in[1]*w[1]);
    out[1] = sigmoid(D,in[0]*w[2]+in[1]*w[3]);
    
    //Berechnung der Ausgänge der Ausgangsneuronenschicht:
    out[2] = sigmoid(D,out[0]*w[4]+out[1]*w[5]);

    return out[2];
}

int main(void)
{
    cout<<"Test einer NN, das wie ein logisches UND-Gatter reagiert:"<<endl;
    cout<<"0 | 0 liefert "<<netz(0.0,0.0)<<endl;
    cout<<"0 | 1 liefert "<<netz(0.0,1.0)<<endl;
    cout<<"1 | 0 liefert "<<netz(1.0,0.0)<<endl;
    cout<<"1 | 1 liefert "<<netz(1.0,1.0)<<endl;

    return 0;
}

Code 0-1: Mittels modifiziertem Gradientenverfahren als UND-Gatter belerntes Neuronales Netz.

Test einer NN, das wie ein logisches UND-Gatter reagiert:
0 | 0 liefert 1.72757e-09
0 | 1 liefert 5.26648e-07
1 | 0 liefert 4.33762e-07
1 | 1 liefert 1

Code 0-2: Ausgabe des obigen Programms.

Ausgangspunkt ist ein kleines Neuronales Netz:

Neuronales Netz.

Bild 0-1: Neuronales Netz.

Nun soll ein Lernverfahren implementiert werden, damit das Netz lernt, eine bestimmte Aufgabe zu erledingen, hier ist das die Funktionalität eines UND-Gatters:

Prinzip des überwachten Lernens.

Bild 0-2: Prinzip des überwachten Lernens.

Nachfolgendes Belern-Programm wird im Unterricht Schritt für Schritt entwickelt:
#include <iostream>
#include <math.h>
#include<cstdlib>
#include<ctime>
#define N 6
using namespace std;

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

double sigmoid(double d, double u)
{
    double x = 1.0/(1.0+exp(-d*u));
    return x;
}

double w[] = {1.0,1.0,1.0,1.0,1.0,1.0};    
double out[] = {0.0,0.0,0.0};    
double in[] = {0.0,1.0};

double netz1(double in0, double in1)
{
    in[0]=in0;
    in[1]=in1;

    //Berechnung der Ausgänge der Eingangsneuronenschicht:
    out[0] = sigmoid(0.1,in[0]*w[0]+in[1]*w[1]);
    out[1] = sigmoid(0.1,in[0]*w[2]+in[1]*w[3]);
    
    //Berechnung der Ausgänge der Ausgangsneuronenschicht:
    out[2] = sigmoid(0.1,out[0]*w[4]+out[1]*w[5]);

    return out[2];
}

double fehler()
{
    double f0 = netz1(0.0,0.0);
    double f1 = netz1(0.0,1.0);
    double f2 = netz1(1.0,0.0);
    double f3 = 1.0 - netz1(1.0,1.0);

    return sqrt(f0*f0+f1*f1+f2*f2+f3*f3);
}

int main(void)
{
    srand(static_cast<unsigned int>(time(0)));

    for(int i=0;i<N;i++)
       w[i]=zufall();

    double err = fehler();
    for(int D=0;D<10000000;D++)
    {
        int inx = rand()%N;
        double wmerk=w[inx];

        //Modifiziertes Gradientenverfahren:
        if(rand()%2==0)
           w[inx]+=0.001;
        else
           w[inx]-=0.001;

        //Alternativ, Zufallsgewichte testen:
        //w[inx]=zufall();

        double errneu = fehler();

        if(errneu>err)
        {
            w[inx]=wmerk;                   
        }
        else
        {
            if(errneu<err)
                cout<<"err="<<err<<endl;

            err=errneu;
        }
    }

    //Bestes Array mit den Gewichten ausgeben,
    //um das fertige trainierte Netz in einem anderen Programm
    //nutzen zu können:
    for(int i=0;i<N;i++)
        cout<<w[i]<<",";
    cout<<endl;

    return 0;
}

Code 0-3: Netz und Lernverfahren: Modifiziertes Gradientenverfahren.

Im Untericht entstanden:
Fehler des Netzes berechnen, das sich wie ein UND-Gatter verhalten soll.
#include <iostream>
#include <math.h>
using namespace std;

double sigmoid(double d, double u)
{
    double x = 1.0/(1.0+exp(-d*u));
    return x;
}

int main(void)
{
    double otest[] = {0.0,0.0,0.0,0.0};

    double w[] = {-51.82,99.98,-16.26,6.34,45.94,-100.0};    
    double out[] = {0.0,0.0,0.0};    
    double in[] = {0.0,1.0};
    double D=0.1;
    //Berechnung der Ausgänge der Eingangsneuronenschicht:
    in[0]=0.0;
    in[1]=0.0;
    out[0] = sigmoid(D,in[0]*w[0]+in[1]*w[1]);
    out[1] = sigmoid(D,in[0]*w[2]+in[1]*w[3]);    
    //Berechnung der Ausgänge der Ausgangsneuronenschicht:
    out[2] = sigmoid(D,out[0]*w[4]+out[1]*w[5]);
    otest[0] = out[2];

    in[0]=1.0;
    in[1]=0.0;
    out[0] = sigmoid(D,in[0]*w[0]+in[1]*w[1]);
    out[1] = sigmoid(D,in[0]*w[2]+in[1]*w[3]);    
    //Berechnung der Ausgänge der Ausgangsneuronenschicht:
    out[2] = sigmoid(D,out[0]*w[4]+out[1]*w[5]);
    otest[1] = out[2];

    in[0]=0.0;
    in[1]=1.0;
    out[0] = sigmoid(D,in[0]*w[0]+in[1]*w[1]);
    out[1] = sigmoid(D,in[0]*w[2]+in[1]*w[3]);    
    //Berechnung der Ausgänge der Ausgangsneuronenschicht:
    out[2] = sigmoid(D,out[0]*w[4]+out[1]*w[5]);
    otest[2] = out[2];

    in[0]=1.0;
    in[1]=1.0;
    out[0] = sigmoid(D,in[0]*w[0]+in[1]*w[1]);
    out[1] = sigmoid(D,in[0]*w[2]+in[1]*w[3]);    
    //Berechnung der Ausgänge der Ausgangsneuronenschicht:
    out[2] = sigmoid(D,out[0]*w[4]+out[1]*w[5]);
    otest[3] = out[2];

    // Quadratischer Fehler: Wurzel aus { Summe (soll-ist)^2..}
    double err = sqrt( (0.0-otest[0])*(0.0-otest[0]) + 
                       (0.0-otest[0])*(0.0-otest[0]) + 
                       (0.0-otest[0])*(0.0-otest[0]) + 
                       (1.0-otest[0])*(1.0-otest[0]) 
                                                        );
    cout<<"Fehler dieses Netzes: "<<err<<endl;

    return 0;
}#include <iostream>
#include <math.h>
using namespace std;

double sigmoid(double d, double u)
{
    double x = 1.0/(1.0+exp(-d*u));
    return x;
}

int main(void)
{
    double otest[] = {0.0,0.0,0.0,0.0};

    double w[] = {-51.82,99.98,-16.26,6.34,45.94,-100.0};    
    double out[] = {0.0,0.0,0.0};    
    double in[] = {0.0,1.0};
    double D=0.1;
    //Berechnung der Ausgänge der Eingangsneuronenschicht:
    in[0]=0.0;
    in[1]=0.0;
    out[0] = sigmoid(D,in[0]*w[0]+in[1]*w[1]);
    out[1] = sigmoid(D,in[0]*w[2]+in[1]*w[3]);    
    //Berechnung der Ausgänge der Ausgangsneuronenschicht:
    out[2] = sigmoid(D,out[0]*w[4]+out[1]*w[5]);
    otest[0] = out[2];

    in[0]=1.0;
    in[1]=0.0;
    out[0] = sigmoid(D,in[0]*w[0]+in[1]*w[1]);
    out[1] = sigmoid(D,in[0]*w[2]+in[1]*w[3]);    
    //Berechnung der Ausgänge der Ausgangsneuronenschicht:
    out[2] = sigmoid(D,out[0]*w[4]+out[1]*w[5]);
    otest[1] = out[2];

    in[0]=0.0;
    in[1]=1.0;
    out[0] = sigmoid(D,in[0]*w[0]+in[1]*w[1]);
    out[1] = sigmoid(D,in[0]*w[2]+in[1]*w[3]);    
    //Berechnung der Ausgänge der Ausgangsneuronenschicht:
    out[2] = sigmoid(D,out[0]*w[4]+out[1]*w[5]);
    otest[2] = out[2];

    in[0]=1.0;
    in[1]=1.0;
    out[0] = sigmoid(D,in[0]*w[0]+in[1]*w[1]);
    out[1] = sigmoid(D,in[0]*w[2]+in[1]*w[3]);    
    //Berechnung der Ausgänge der Ausgangsneuronenschicht:
    out[2] = sigmoid(D,out[0]*w[4]+out[1]*w[5]);
    otest[3] = out[2];

    // Quadratischer Fehler: Wurzel aus { Summe (soll-ist)^2..}
    double err = sqrt( (0.0-otest[0])*(0.0-otest[0]) + 
                       (0.0-otest[0])*(0.0-otest[0]) + 
                       (0.0-otest[0])*(0.0-otest[0]) + 
                       (1.0-otest[0])*(1.0-otest[0]) 
                                                        );
    cout<<"Fehler dieses Netzes: "<<err<<endl;

    return 0;
}

Code 0-4: Fehler des Netzes berechnen, das sich wie ein UND-Gatter verhalten soll.

Umsetzung des Netzes als Funktion, ebenfalls Fehler berechnen.
#include <iostream>
#include <math.h>
using namespace std;

double w[] = {-51.82,99.98,-16.26,6.34,45.94,-100.0};    
double out[] = {0.0,0.0,0.0};    
double in[] = {0.0,1.0};
double D=0.1;

double sigmoid(double d, double u)
{
    double x = 1.0/(1.0+exp(-d*u));
    return x;
}

double netz1(double in0, double in1)
{
    in[0]=in0;
    in[1]=in1;

    //Berechnung der Ausgänge der Eingangsneuronenschicht:
    out[0] = sigmoid(0.1,in[0]*w[0]+in[1]*w[1]);
    out[1] = sigmoid(0.1,in[0]*w[2]+in[1]*w[3]);
    
    //Berechnung der Ausgänge der Ausgangsneuronenschicht:
    out[2] = sigmoid(0.1,out[0]*w[4]+out[1]*w[5]);

    return out[2];
}

int main(void)
{
    double otest0 = netz1(0.0,0.0);
    double otest1 = netz1(0.0,1.0);
    double otest2 = netz1(1.0,0.0);
    double otest3 = netz1(1.0,1.0);
    double err = sqrt( (0.0-otest0)*(0.0-otest0)+
                       (0.0-otest1)*(0.0-otest1)+
                       (0.0-otest2)*(0.0-otest2)+
                       (1.0-otest3)*(1.0-otest3)
                                                    );
    cout<<"Fehler dieses Netzes: "<<err<<endl;
    return 0;
}

Code 0-5: Umsetzung des Netzes als Funlktion, ebenfalls Fehler berechnen.

3. Schritt: Auch die Fehlerberechnung als Funktion
#include <iostream>
#include <math.h>
using namespace std;

double w[] = {-51.82,99.98,-16.26,6.34,45.94,-100.0};    
double out[] = {0.0,0.0,0.0};    
double in[] = {0.0,1.0};
double D=0.1;

double sigmoid(double d, double u)
{
    double x = 1.0/(1.0+exp(-d*u));
    return x;
}

double netz1(double in0, double in1)
{
    in[0]=in0;
    in[1]=in1;

    //Berechnung der Ausgänge der Eingangsneuronenschicht:
    out[0] = sigmoid(0.1,in[0]*w[0]+in[1]*w[1]);
    out[1] = sigmoid(0.1,in[0]*w[2]+in[1]*w[3]);
    
    //Berechnung der Ausgänge der Ausgangsneuronenschicht:
    out[2] = sigmoid(0.1,out[0]*w[4]+out[1]*w[5]);

    return out[2];
}

double berechneFehler()
{
    double otest0 = netz1(0.0,0.0);
    double otest1 = netz1(0.0,1.0);
    double otest2 = netz1(1.0,0.0);
    double otest3 = netz1(1.0,1.0);
    double err = sqrt( (0.0-otest0)*(0.0-otest0)+
                       (0.0-otest1)*(0.0-otest1)+
                       (0.0-otest2)*(0.0-otest2)+
                       (1.0-otest3)*(1.0-otest3)
                                                    );
    return err;
}

int main(void)
{
    cout<<"Fehler dieses Netzes: "<<berechneFehler()<<endl;
    return 0;
}

Code 0-6: 3. Schritt: Auch die Fehlerberechnung als Funktion

4. Schritt: Implementierung des modifizierten Gradientenverfahrens
#include <iostream>
#include <math.h>
#include<cstdlib>
#include<ctime>
#define N 6
using namespace std;
using namespace std;

double w[] = {-51.82,99.98,-16.26,6.34,45.94,-100.0};    
double out[] = {0.0,0.0,0.0};    
double in[] = {0.0,1.0};
double D=0.1;

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

double sigmoid(double d, double u)
{
    double x = 1.0/(1.0+exp(-d*u));
    return x;
}

double netz1(double in0, double in1)
{
    in[0]=in0;
    in[1]=in1;

    //Berechnung der Ausgänge der Eingangsneuronenschicht:
    out[0] = sigmoid(0.1,in[0]*w[0]+in[1]*w[1]);
    out[1] = sigmoid(0.1,in[0]*w[2]+in[1]*w[3]);
    
    //Berechnung der Ausgänge der Ausgangsneuronenschicht:
    out[2] = sigmoid(0.1,out[0]*w[4]+out[1]*w[5]);

    return out[2];
}

double berechneFehler()
{
    double otest0 = netz1(0.0,0.0);
    double otest1 = netz1(0.0,1.0);
    double otest2 = netz1(1.0,0.0);
    double otest3 = netz1(1.0,1.0);
    double err = sqrt( (0.0-otest0)*(0.0-otest0)+
                       (0.0-otest1)*(0.0-otest1)+
                       (0.0-otest2)*(0.0-otest2)+
                       (1.0-otest3)*(1.0-otest3)
                                                    );
    return err;
}

int main(void)
{
    srand(static_cast<unsigned int>(time(0)));

    for(int i=0;i<N;i++)
        w[i] = zufall();

    cout<<"Fehler dieses Netzes: "<<berechneFehler()<<endl;
    double err = berechneFehler();
    for(int durchgang=0; durchgang<1000000; durchgang++)
    {
        //1. zufällig ein Gewicht auswählen, das geändert werden soll:
        int index = rand()%N;
        double wmerker = w[index];
        //2. Gewicht größer oder kleiner werden lassen
        if(rand()%2==0)
             w[index]+=0.001;
        else
             w[index]-=0.001;    
        //3. Prüfen, ob das Netz besser geworden ist.
        double err_neu = berechneFehler();

        //4. wenn es besser geworden ist, so lassen, sonst Änderung rückgängig machen.
        if(err_neu<=err)
        {
            if(err_neu<err)
                 cout<<err_neu<<endl;
            err = err_neu;         
        }
        else
        {
            w[index] = wmerker;
        }
    }

    cout<<"Optimierte Gewichte: "<<endl;
    for(int i=0;i<N;i++)
        cout<<w[i]<<","<<endl;

    cout<<"Optimierte Gewichte: "<<endl;
    for(int i=0;i<N;i++)
        cout<<w[i]<<","<<endl;

    cout<<"00=>"<<netz1(0.0,0.0)<<endl;
    cout<<"01=>"<<netz1(0.0,1.0)<<endl;
    cout<<"10=>"<<netz1(1.0,0.0)<<endl;
    cout<<"11=>"<<netz1(1.0,1.0)<<endl;


    return 0;
}

Code 0-7: 4. Schritt: Implementierung des modifizierten Gradientenverfahrens

...
0.0757367
0.0757367
0.0757367
0.0757367
0.0757354
0.0757354
0.0757354
0.0757354
0.0757354
0.075734
Optimierte Gewichte: 
-82.867,
161.897,
-15.634,
7.167,
83.118,
-171.254,
00=>0.0120472
01=>0.0393765
10=>0.0491103
11=>0.959649

Code 0-8: Beispielausgabe.

***** ÜBUNGSAUFGABEN *****
Aufgabe 1 -- Schnee fällt herab....
#include <iostream> //nötig für cin cout
#include <cstring> // nötig für strcpy()
#include <unistd.h> // nötig für usleep
#define ZEILEN 14
using namespace std;
int main(void)
{
    char feld[][22] = {
       //..........x..........
       //012345678901234567890
        "*     *    *    *    ",
        "                     ",
        "*   *    *    *     *",
        "          *          ",
        "                     ",
        "  *        *         ",
        "                     ",
        "      *      *      *",
        "*       *        *  *",
        "                     ",
        "            *       *",
        "                     ",
        "                     ",
        "   *              *  "
    };

    int startzeile=0;
    int countdown=10;
    while(true)
    {
        cout<<"\033c"; //Reset Terminal
        for(int i=ZEILEN;i>=0;i--)
            cout<<feld[(i+startzeile)%ZEILEN]<<endl;
        startzeile++; //Reihenfolge wandern lassen
        usleep(300000);// 300000 Mikrosekunden "schlafen"
        countdown--;
        if(countdown<=0)
            break;
    }
    return 0;
}

Code 0-9: schneefall.cpp

  • Testen und analysieren Sie obiges Programm.
  • Variieren Sie die Geschwindigkeit, mit der "die Schneeflocken" fallen.
  • Ergänzen Sie einen Programmteil, der zählt, wieviele Schneeflocken während der Laufzeit des Programms am Boden angekommen sind. Lassen Sie diese Anzahl ausgeben.

Musterlösung, Schneeflocken zählen

#include <iostream> //nötig für cin cout
#include <cstring> // nötig für strcpy()
#include <unistd.h> // nötig für usleep
#define ZEILEN 14
using namespace std;
int main(void)
{
    char feld[][22] = {
       //..........x..........
       //012345678901234567890
        "*     *    *    *    ",
        "                     ",
        "*   *    *    *     *",
        "          *          ",
        "                     ",
        "  *        *         ",
        "                     ",
        "      *      *      *",
        "*       *        *  *",
        "                     ",
        "            *       *",
        "                     ",
        "                     ",
        "   *              *  "
    };

    int startzeile=0;
    int countdown=10;
    int flockenanzahl=0;
    while(true)
    {
        cout<<"\033c"; //Reset Terminal
        for(int i=ZEILEN;i>=0;i--)
            cout<<feld[(i+startzeile)%ZEILEN]<<endl;

        for(int i=0;i<21;i++)
        {
             if(feld[startzeile%ZEILEN][i]=='*')
             {
                  flockenanzahl++;
             }
        }             

        startzeile++; //Reihenfolge wandern lassen
        usleep(300000);// 300000 Mikrosekunden "schlafen"
        countdown--;
        if(countdown<=0)
            break;
    }

    cout<<"Anzahl der Flocken, die am Boden angekommen sind: "<<flockenanzahl<<endl;

    return 0;
}


Code 0-10: Musterlösung, Schneeflocken zählen.

Aufgabe 2 -- Schneeflocken fangen
  • Ein Frosch (Eintrag "F") versucht mit seiner Zunge einzelne Schneeflocken zu fangen.
  • Helfen Sie ihm, indem Sie eine günstigere Abfolge seiner Bewegungen im Array wechsel[] vorgeben. Erlaubte Einträge sind dort: {-1,0,1}.
  • Ergänzen Sie das Programm so, dass nach Ablauf des Countdowns die Anzahl der erreichten Punkte (gefangene Schneeflocken) ausgegeben wird.
#include <iostream> //nötig für cin cout
#include <cstring> // nötig für strcpy()
#include <unistd.h> // nötig für usleep
#define ZEILEN 14
using namespace std;
int main(void)
{
    char feld[][22] = {
       //..........x..........
       //012345678901234567890
        "*     *    *    *    ",
        "                     ",
        "*   *    *    *     *",
        "          *          ",
        "                     ",
        "  *        *         ",
        "                     ",
        "      *      *      *",
        "*       *        *  *",
        "                     ",
        "            *       *",
        "                     ",
        "                     ",
        "   *              *  "
    };

    int startzeile=0;
    int countdown=10;
    int position=10;
    int score=0;
    int wechsel[] = {0,0,0,-1,-1,-1,1,1,0,0};

    while(true)
    {
        cout<<"c"; //Reset Terminal
        for(int i=ZEILEN;i>=0;i--)
            cout<<feld[(i+startzeile)%ZEILEN]<<endl;

        for(int i=0;i<position;i++)
            cout<<" ";
        cout<<"F"<<endl;
        
        if(feld[startzeile%ZEILEN][position]=='*')
            score++;

        position = position + wechsel[countdown-1];

        startzeile++; //Reihenfolge wandern lassen
        usleep(300000);// 300000 Mikrosekunden "schlafen"
        countdown--;

        cout<<"SCORE:"<<score<<" COUNTDOWN:"<<countdown<<endl;

        if(countdown<=0)
            break;
    }
    return 0;
}

Code 0-11: schneefall2.cpp

Aufgabe 3 -- Regelbasiertes Handeln

Implementieren Sie die nachfolgenden Verhaltensregeln für den Frosch in der Programmvariante weiter unten und überprüfen Sie, welche Punktezahl Sie einmal mit und ohne Ihre Regeln erreichen:

  • Wenn unmittelbar zwei Zeilen über dem Frosch eine Schneeflocke ist (Stern), soll der Frosch ("F") sich nicht bewegen.
  • Ist da keine, aber eine Position weiter links, so soll er sich eine Position nach links bewegen.
  • Ist da keine, aber eine Position weiter rechts, so soll er sich eine Position nach rechts bewegen.
  • Ist nichts von alledem erfüllt, soll sich der Frosch zur Mitte hin bewegen.
#include <iostream> //nötig für cin cout
#include <cstring> // nötig für strcpy()
#include <unistd.h> // nötig für usleep
#define ZEILEN 14
using namespace std;
int main(void)
{
    char feld[][22] = {
       //..........x..........
       //012345678901234567890
        "*     *    *    *    ",
        "                     ",
        "*   *    *    *     *",
        "          *          ",
        "                     ",
        "  *        *         ",
        "                     ",
        "      *      *      *",
        "*       *        *  *",
        "                     ",
        "            *       *",
        "                     ",
        "                     ",
        "   *              *  "
    };

    int startzeile=0;
    int countdown=300;
    int position=10;
    int score=0;

    while(true)
    {
        cout<<"\033c"; //Reset Terminal
        for(int i=ZEILEN;i>=0;i--)
            cout<<feld[(i+startzeile)%ZEILEN]<<endl;

        for(int i=0;i<position;i++)
            cout<<" ";
        cout<<"F"<<endl;
        
        if(feld[startzeile%ZEILEN][position]=='*')
            score++;

        //Implementieren Sie in diesem Bereich Ihre Verhaltensregeln des Frosches:        
    
        // ...

        //ENDE Implementieren Sie in diesem Bereich Ihre Verhaltensregeln des Frosches:

        startzeile++; //Reihenfolge wandern lassen
        usleep(30000);// 300000 Mikrosekunden "schlafen"
        countdown--;

        cout<<"SCORE:"<<score<<" COUNTDOWN:"<<countdown<<endl;

        if(countdown<=0)
            break;
    }
    return 0;
}

Code 0-12: schneefall3.cpp


Hinweise:



Struktur if / else if / else:

if(...)
{
}
else if(...)
{
}
else if(...)
{
}
...
else
{
}

Inhalt des Arrays zwei Zeilen direkt über dem Frosch:
char inhalt = feld[(startzeile+1)%ZEILEN][position];


Code 0-13: Hinweise zu Aufgabe 3.

Musterlösung zu Aufgabe 3:

//Implementieren Sie in diesem Bereich Ihre Verhaltensregeln des Frosches:        
        	if(feld[(startzeile+1)%ZEILEN][position]=='*')
			position = position;
		else if(feld[(startzeile+1)%ZEILEN][position+1]=='*')
			position = position+1;
		else if(feld[(startzeile+1)%ZEILEN][position-1]=='*')
			position = position-1;
		else
		{
			if(position>10)
				position = position-1;
			else if (position<10)
				position = position+1;
			else
				position = position;
		}
        //ENDE Implementieren Sie in diesem Bereich Ihre Verhaltensregeln des Frosches:

Code 0-14: Musterlösung zu Aufgabe 3 (Nur das, was ergänzt wurde).

Aufgabe 4 -- Neuronales Netz
  • Man könnte sich das Verhalten des Frosches aus Aufgabe 3 auch auf ein Neuronales Netz (NN) übertragen denken.
  • In dem Fall hätte das NN als Input drei Eingänge, die jeweils 1 wären, wenn die drei benachbarten Positionen zwei Zeilen über dem Frosch einen * beinhalten und ansonsten 0.
  • Das NN könnte einen Ausgang besitzten.
  • Wenn der Ausgang nahe 0.5 wäre, würde der Frosch bleiben wo er ist, nahe 1 würde er eine Positon nach rechts, nahe 0 eine nach links wandern.
  • Das Verhalten des NN könnte natürlich ein anderes, vielleicht sogar besseres sein, als das aus Aufgabe 3.
  • Bei nachfolgender Implementierung wurde ein NN mit vier Eingangsneuronen, keiner Zwischenschicht und einem Ausgangsneuron verwendet.
  • Skizzieren Sie Struktur des beschriebenen Neuronalen Netzes (NN).
  • Nachfolgender Quelltext optimiert das NN zur Steuerung des Frosches.
  • Alle Ausgaben des Spiels wurden aus Performancegründen auskommentiert.
  • Es ist bereits vollständig und nach einigen Starts sollten Sie Netzwerkgewichte erhalten, die einen Score von 87 ermöglichen.
  • Sorgen Sie nun dafür, dass die Gewichte des besten Netzes ausgegeben werden und schreiben Sie ein Testprogramm, das wie bei den Beispielen oben das Fallen der Schneeflocken und das Bewegen des Frosches zeigt, wobei die Bewegung des Frosches von dem besten gefundenen Neuronalen Netz gesteuert werden soll.
#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 ZEILEN 14
#define N 16
using namespace std;

double w[16]; // 3*4+4 = 16    
double out[] = {0.0,0.0,0.0,0.0,0.0};    
double in[] = {0.0,0.0,0.0};
double D=0.1;
double zufall()
{
    return (double)(rand()%10000)*0.02-100.0; // Wertebereich: +/-99.99
}

double sigmoid(double d, double u)
{
    double x = 1.0/(1.0+exp(-d*u));
    return x;
}

double netz2(double in0, double in1, double in2)
{
    in[0]=in0;
    in[1]=in1;
    in[2]=in2;

    //Berechnung der Ausgänge der Eingangsneuronenschicht:
    out[0] = sigmoid(0.1,in[0]*w[0]+in[1]*w[1]+in[2]*w[2]);
    out[1] = sigmoid(0.1,in[0]*w[3]+in[1]*w[4]+in[2]*w[5]);
    out[2] = sigmoid(0.1,in[0]*w[6]+in[1]*w[7]+in[2]*w[8]);
    out[3] = sigmoid(0.1,in[0]*w[9]+in[1]*w[10]+in[2]*w[11]);
    
    //Berechnung der Ausgänge der Ausgangsneuronenschicht:
    out[4] = sigmoid(0.1,out[0]*w[12]+out[1]*w[13]+out[2]*w[14]+out[3]*w[15]);

    return out[2];
}

int main(void)
{
    srand(static_cast<unsigned int>(time(0)));
    //srand(2); //Immer die gleichen "Zufallsgewichte"
    
    for(int i=0;i<N;i++)
        w[i] = zufall();

    int BESTSCORE = 0; 

 for(int DURCHLAUF=0;DURCHLAUF<10000;DURCHLAUF++)
 {
    char feld[][22] = {
       //..........x..........
       //012345678901234567890
        "*     *    *    *    ",
        "                     ",
        "*   *    *    *     *",
        "          *          ",
        "                     ",
        "  *        *         ",
        "                     ",
        "      *      *      *",
        "*       *        *  *",
        "                     ",
        "            *       *",
        "                     ",
        "                     ",
        "   *              *  "
    };

    int startzeile=0;
    int countdown=300;
    int position=10;
    int score=0;

    //Optimierungsprozess Teil 1:
        int index = rand()%N;
        double wmerker = w[index];
        //2. Gewicht größer oder kleiner werden lassen
        w[index]=zufall();
    //ENDE Optimierungsprozess Teil 1.

    while(true)
    {
      /*
        cout<<"\033c"; //Reset Terminal
        for(int i=ZEILEN;i>=0;i--)
            cout<<feld[(i+startzeile)%ZEILEN]<<endl;

        for(int i=0;i<position;i++)
            cout<<" ";
        cout<<"F"<<endl;
      */
        if(feld[startzeile%ZEILEN][position]=='*')
            score++;


        double input[] = {0.0,0.0,0.0}; 
        if(feld[(startzeile+1)%ZEILEN][position]=='*')
            input[1]=1.0;
        if(position>0 && feld[(startzeile+1)%ZEILEN][position-1]=='*')
            input[0]=1.0;
        if(position<20 && feld[(startzeile+1)%ZEILEN][position+1]=='*')
            input[2]=1.0;

        double output = netz2(input[0],input[1],input[2]);

        if(output<0.25 && position>0)
              position--;                   
        else if(output>0.75 && position<20)
              position++;                   
         

        if(position>=21)
            position=0;

        startzeile++; //Reihenfolge wandern lassen
//        usleep(30000);// 300000 Mikrosekunden "schlafen"
        countdown--;

//        cout<<"SCORE:"<<score<<" COUNTDOWN:"<<countdown<<endl;

        if(countdown<=0)
            break;
    }
    //cout<<"DURCHLAUF:"<<DURCHLAUF<<" SCORE:"<<score<<endl;

    //Optimierungsprozess Teil 2:
        //3. Prüfen, ob das Netz besser geworden ist.
        if(score>=BESTSCORE)
        {
            if(score>BESTSCORE)
                cout<<"DURCHLAUF:"<<DURCHLAUF<<" SCORE:"<<score<<endl;
            BESTSCORE=score;
        }
        else
        {
            w[index] = wmerker;
        }    
    //ENDE Optimierungsprozess Teil 2

  }//for DURCHLAUF
  return 0;
}

Code 0-15: schneefall4.cpp

Aufgabe 5
  • Wie Aufgabe 3, aber es soll ein Verhalten implementiert werden, bei dem der Frosch den Schneeflocken ausweicht.
  • Wie Aufgabe 4, aber es soll ein Neuronales Netz mit einem Verhalten implementiert und belernt werden, bei dem der Frosch den Schneeflocken ausweicht.
  • sudo apt-get install libncurses5-dev libncursesw5-dev
  • sudo apt-get update --fix-missing