Forum: PC-Programmierung Fragen zur Rekursion


von Tobias Mehrl (Gast)


Lesenswert?

Hi Leute!

Ich hab heute ein Programm geschrieben, dass mir die Fakultät für 
unterschiedliche Werte mittles Rekursion berechnet. Hier der Code:
1
#include<iostream>
2
using namespace std;
3
4
5
6
int fakultaet(int n)
7
{
8
  if(n == 1)
9
  {
10
    return 1;
11
  }
12
  else
13
  {
14
    return n * fakultaet(n-1);
15
  }
16
}
17
18
19
20
int main()
21
{
22
  int n;
23
  cin >> n;
24
25
  cout << fakultaet(n) << endl;
26
27
28
29
system("pause");
30
return 0;
31
}

Der Code funktioniert zwar, aber ich weiß an einer Stelle nicht so genau 
warum.

Der Benutzer gibt in "n" z.B. die Zahl 4 ein. Dann springt das Programm 
zur Funktion "fakultaet". Jetzt gehts weiter in die if-Abfrage. Da n=4 
ist wird wohl während der Prüfung von n == 1 ein "falsch" rauskommen. Es 
geht weiter in den else-Zweig. Dort wird dann 4*fakultaet(4-1) 
zurückgegeben. Hier taucht nun schon meine erste Frage auf:

An welche Stelle im Code wird das "4*fakultaet(3)" zurückgegeben?

Da ich mich ja in der Funktion fakultaet befinde, wird der return-Wert, 
also 4*fakultaet(3), wieder in den main-Teil zurückgegeben, oder?

Tja, ab hier weiß ich dann schon nicht mehr so genau weiter. 
Normalerweise müsste sich doch die Funktion fakultaet wieder aufrufen. 
Nun aber doch mit einem n=n-1=4-1=3, oder? Aber wo wird da n-1 
berechnet? Wenn ich nach dem ersten return-Wert die Zeile in der cout 
steht näher anschaue, dann steht doch dann: cout << 
fakultaet(fakultaet(n-1)). Spätestens ab hier verlassen mich dann 
wirklich alle guten Geister!

Könnt ihr mir das erklären?

von Yalu X. (yalu) (Moderator)


Lesenswert?

Probier's mal anders herum:

Für n=1 kann man leicht sehen, dass die Funktion den richtigen Wert
zurückgibt, es wird nämlich nur der if-Zweig ausgeführt.

Was passiert bei n=2? Jetzt wird der else-Zweig ausgeführt und
n*fakultaet(n-1), also 2*fakultaet(1) zurückgegeben. Dass fakultaet(1)
richtig berechnet wird, haben wir ja oben schon gesehen.

Was passiert bei n=3? Jetzt wird 3*fakultaet(2) zurückgegeben. Dass
fakultaet(2) richtig berechnet wird, haben wir ja oben schon gesehen.

...

Ich würde übrigens if(n == 1) durch if(n == 0) ersetzen. Dann liefert
die Funktion auch für n=0 das richtige Ergebnis, das nach Definition 1
ist.

von Huch (Gast)


Lesenswert?

>Normalerweise müsste sich doch die Funktion fakultaet wieder aufrufen.

Das tut sie auch. Nämlich genau hier.
1
return n * fakultaet(n-1);

Das kennzeichnende ist, dass im Rumpf einer Funktion wieder ein Aufruf 
dieser Funktion erfolgt. Schreibt man einen Ausdruck mit einem 
Funktionsnamen und einer Parameterliste, so ist das ein Aufruf dieser 
Funktion. Insofern ist da kein Unterschied zu
1
 cout << fakultaet(n) << endl;

in main.

>Aber wo wird da n-1 berechnet?

Auch genau hier:
1
return n * fakultaet(n-1);

Bevor die Funktion aufgerufen wird, werden die Ausdrücke berechnet die 
sich in der Parameterliste befinden.

von andz (Gast)


Lesenswert?

Das ist das wunder der Rekursion...zeichnes dir mal genau schritt für 
schritt auf was passiert, erst vorwärts bis n==1, dann wieder rückwärts 
mit den ganzen rückgabewerten, dann wirds dir vlt klar

gruß

andz

von eskimo (Gast)


Lesenswert?

andz schrieb:
> Das ist das wunder der Rekursion...

Um Rekursion zu verstehen, muss man zunächst Rekursion verstehen.

von Karl H. (kbuchegg)


Lesenswert?

Tobias Mehrl schrieb:
> Hi Leute!
>
> Ich hab heute ein Programm geschrieben, dass mir die Fakultät für
> unterschiedliche Werte mittles Rekursion berechnet. Hier der Code:

>
> Der Code funktioniert zwar, aber ich weiß an einer Stelle nicht so genau
> warum.

Dann hast du es nicht selbst geschrieben :-)

2 Möglichkeiten:
* lass Rekursion noch links liegen.
  Das ist noch nichts für deinen Level

* Schreib dein Programm um, so dass es Ausgaben macht.
  So zb
1
#include<iostream>
2
using namespace std;
3
4
void doIndent( int n )
5
{
6
  for( int i = 0; i < n; ++i )
7
    cout << ' ';
8
}
9
10
int fakultaet(int n, int indent)
11
{
12
  doIndent( indent );
13
  cout << "Gesucht die Fakultaet von " << n << endl;
14
15
  if(n == 1)
16
  {
17
    doIndent( indent );
18
    cout << "das ist einfach, die ist 1" << endl;
19
    return 1;
20
  }
21
22
  else
23
  {
24
    doIndent( indent );
25
    cout << "dazu berechne ich die Fakultaet von " << n-1 << endl;
26
27
    int tmp = fakultaet(n-1, indent + 2);;
28
29
    doIndent( indent );
30
    cout << "also: Die Fakultaet von " << n-1 << " ist gleich " << tmp << endl;
31
    doIndent( indent );
32
    cout << "daher ist die Fakultaet von " << n << " gleich " << n << "*" << tmp << endl;
33
34
    tmp = n * tmp;
35
36
    doIndent( indent );
37
    cout << "das ergibt " << tmp << endl;
38
39
    return tmp;
40
  }
41
}
42
43
44
45
int main()
46
{
47
  int n;
48
  cin >> n;
49
50
  cout << fakultaet(n, 0) << endl;
51
52
system("pause");
53
return 0;
54
}

Jetzt erzählt dir das Programm, wann es was warum berechnet. Jede 
Einrückstufe im Output entspricht einem Aufruf der Funktion fakultaet

von Tobias Mehrl (Gast)


Lesenswert?

Doch, doch, das Programm ist meins. Man kann auch durch sukzessives 
Ausprobieren auf die Lösung solch, ja eigentlich einfacher "Probleme" 
kommen.

von Karl H. (kbuchegg)


Lesenswert?

Tobias Mehrl schrieb:

> Normalerweise müsste sich doch die Funktion fakultaet wieder aufrufen.
> Nun aber doch mit einem n=n-1=4-1=3, oder? Aber wo wird da n-1
> berechnet? Wenn ich nach dem ersten return-Wert die Zeile in der cout
> steht näher anschaue, dann steht doch dann: cout <<
> fakultaet(fakultaet(n-1)). Spätestens ab hier verlassen mich dann
> wirklich alle guten Geister!

Du hast den Mechanismus eines Funktionsaufrufs immer noch nicht 
verstanden.

wenn da steht

  return n * fakultaet(n-1);


dann wird n-1 AUSGEWERTET
n ist bei dir 4, also kommt da 3 raus.
Mit diesen 3 wird die Funktion fakultaet aufgerufen. Die Funktion 
rechnet dann was aus und liefert einen Wert.
Damit ist der Teil hier

  return n * fakultaet(n-1);
             **************

erledit. Was auch immer die Funktion für 3 zurück geliefert hat, es wird 
mit 4 (weil n ja 4 war) multipliziert und dieses Ergebnis, der 
Zahlenwert ist dann der Wert den dieser Funktionsaufruf zurückliefert.

Das was du dir hier zusammenreimst

> dann steht doch dann: cout << fakultaet(fakultaet(n-1)).

das findet nie statt!

Funktionen werden aufgerufen, indem die Werte für die Argumente bestimmt 
werden, mit diesen Werten wird dann die Funktion aufgerufen. Punkt.
Da gibt es keine Substitution, so wie das dein "dann steht doch da..." 
vermuten lässt.

von Karl H. (kbuchegg)


Lesenswert?

Tobias Mehrl schrieb:
> Doch, doch, das Programm ist meins. Man kann auch durch sukzessives
> Ausprobieren auf die Lösung solch, ja eigentlich einfacher "Probleme"
> kommen.

Dann bist du ein Wunderkind. Was ich ehrlich bezweifle. Denn im 
Regelfall kriegen 99% aller Neulinge eine Rekursion durch ausprobieren 
nicht hin.

von Huch (Gast)


Lesenswert?

> Man kann auch durch sukzessives Ausprobieren auf die Lösung solch, ja
> eigentlich einfacher "Probleme" kommen.

Nun ja. Eigentlich ist das kein "Problem".

Das eine ist, dass man wissen muss, das Ausdrücke auch Funktionsaufrufe 
enthalten können. Ob das eine Funktion sich selbst aufruft ist dann ein 
weiterer Aspekt der aus dem ersten folgt.

Dann, die Frage, wie mit Ausdrücken in Parameterklammern umgegangen 
wird. Die verhalten sich im Prinzip genaus wie Ausdrücke in main, nur 
das dann das Ergebnis an die Funktion übergeben wird.

von Tobias Mehrl (Gast)


Lesenswert?

@kbuchegg:

"Die Funktion rechnet dann was aus und liefert einen Wert."

Macht das Programm dann folgendes:

fak(4)=4*fak(3)=4*3*fak2)=4*3*2*fak(1)=4*3*2*1 = 24

Irgendwie muss ja dann aus fak(3)=3 folgen, damit im nächsten Schritt 
dann 4*3*fak(2) stehen kann, oder?

von Klaus W. (mfgkw)


Lesenswert?

Vielleicht hilft dieser Einwurf noch zum besseren Verständnis:

Ein Funktionsaufruf hat einen Satz Parameter (meist), sowie
einen Rückgabewert, sowie einen Satz seiner lokalen (automatischen,
nicht static) Variablen.

Dies zusammengenommen (Parameter, Rückgabewert, lokale Variablen)
gehört jetzt aber nicht statisch zu einer Funktion, sondern
existiert genau dann, wenn die Funktion aktiv ist, also aufgerufen
wurde und sich noch nicht beendet hat.
Ohne Funktionsaufruf keine Parameter, keine lokalen Variablen,
kein Rückgabewert.

Ruft sich eine Funktion jetzt aber selbst wieder auf, dann
existiert dieser ganze Satz mehrfach:
Ab dem ersten Aufruf von fakultaet() in main() einmal,
beim rekursiven Aufruf von fakultaet() aus in ein weiteres
fakultaet() existiert der Satz ein zweites Mal, bei einem
weiteren Aufruf eind drittes Mal usw..
Mit jedem Aufruf kommt ein solcher Satz dazu, mit jedem
return verschwindet wieder einer.

Ein solcher Satz heißt gelegentlich auch "Instanz einer
Funktion" oder "stack frame".
(Genau genommen gehört noch etwas mehr dazu, wie bspw.
die Rücksprungadresse und ein framepointer, aber die sieht
man nicht von C aus.)

Wirklich genutzt wird immer nur der letzte Satz, die
anderen liegen etwas abseits, bis die aktuelle
Funktion (-sintanz) sich beendet.

Mit dieser Vorstellung bewaffnet kann man sich tatsächlich
mit einem Blatt Papier und einem Stift durch fakultaet(4)
zeilenweise durcharbeiten und alle Parameter, Variablen
und Rückgabewerte aufzeichnen.
Sehr lehrreich!

von Karl H. (kbuchegg)


Lesenswert?

Tobias Mehrl schrieb:
> @kbuchegg:
>
> "Die Funktion rechnet dann was aus und liefert einen Wert."
>
> Macht das Programm dann folgendes:
>
> fak(4)=4*fak(3)=4*3*fak2)=4*3*2*fak(1)=4*3*2*1 = 24

Darauf läuft es raus.
Aber so wird das nicht ausgerechnet.

Lass das Programm laufen, dass ich dir gepimpt habe. Das erzählt dir, 
wie es arbeitet

>
> Irgendwie muss ja dann aus fak(3)=3 folgen, damit im nächsten Schritt
> dann 4*3*fak(2) stehen kann, oder?

Du gehst wie ein Mathematiker ran, der Ausdrücke substituiert.
Genau das passiert NICHT!


+--- fak(4) = 4*fak(3)
|
|    +--- Neuer Funktionsaufruf für fak(3)
|    +--- fak(3) = 3*fak(2)
|    |
|    |   +--- Neuer Funktionsaufruf für fak(2)
|    |   +--- fak(2) = 2*fak(1)
|    |   |
|    |   |   +---     Neuer Funtionsaufruf für fak(1)
|    |   |   +---     fak(1) = 1
|    |   |
|    |   +--- zurück beim Problem fak(2). fak(1) ist jetzt bekannt,
|    |   +--- nämlich 1
|    |   +--- daher
|    |   +--- fak(2) = 2*fak(1) = 2 * 1 = 2
|    |
|    +--- zurück beim Problem fak(3)
|    +--- fak(3) = 3*fak(2), fak(2) ist bekannt, nämlich 2
|    +--- daher fak(3) = 3*fak(2) = 3 * 2 = 6
|
+---   zurück beim Problem fak(4)
+---   fak(4) = 4*fak(3), fak(3) ist bekannt, nämlich 6
+---   daher fak(4) = 4*fak(3) = 4 * 6 = 24

von Tobias Mehrl (Gast)


Lesenswert?

Ich hab jetzt mal dein gepimptes Programm ausgeführt. Ich hab mir das 
jetzt angeschaut und hab dazu noch eine Frage: Woher weiß dein bzw. ja 
auch mein Programm, dass, die Fakultät(1)=1 ist? Kommt das durch die 
if-Abfrage? Ist an der Stelle dann n=1?

von Klaus W. (mfgkw)


Lesenswert?

ja

Wobei die Frage jetzt kein klares Zeichen dafür ist, daß du es beim 
Schreiben verstanden hattest :-)

von Karl H. (kbuchegg)


Lesenswert?

Noch ein anderer Ansatz

Stell dir vor du könntest dich Klonen. Beliebig oft.

Ihr alle stellt euch nebeneinaner auf.

Und da ihr alle Klone seid, verfügt ihr alle über dasselbe Wissen.
Nämlich:
* Die Fakultät von 1 sei gleich 1
* Um die Fakultät einer Zahl n zu berechnen, muss die Fakultät von
  n-1 bekannt sein. Die wird dann mit n multipliziert.

Nur wie kriegt man die Fakultät von n-1?
Ganz einfach. Du hast einen Klon neben dir stehen, den fragst du ganz 
einfach.

Ich frage also den ersten in der Kette:
Was ist die Fakultät von 4?
Der erste Tbias denkt ein wenig nach und entscheidet, dass er dazu die 
Fakultät von 3 kennen müsste. Die weiß er nicht, aber er weiß wen er 
fragen kann. Nämlich den Tobias rechts neben ihm.
Also fragt er ihn (Tobias#2): Was ist die Fakultät von 3?
Der denkt wieder nach und beschliesst, den Tobias neben sich, Tobias#3 
um die Fakultät von 2 zu fragen
Tobias#3 hält sich wieder an die Regel, dass er die Fakultät nur 
berechnen kann, wenn er die Fakultät von 1 kennen würde. Also fragt er 
Tabias#4 darum: Was ist die Fakultät von 1?

Tobias#4 kramt in seinen Regeln und findet raus, dass fak(1) gleich 1 
ist. Heureka, er hat eine Lösung!
Die teilt er auch sofort Tobias#3 mit, den der hatte ihn ja nach fak(1) 
gefragt.
Tobias#3 hatte die Aufgabe fak(2) zu berechnen und dazu brauchte er 
fak(1). Den Wert hat er mitlerweile von Tobias#4 bekommen. Nälich 1. 
Hoch erfreut kann er jetzt mit seiner Regel die fak(2) ausrechnen und 
das Ergebnis nach links weiterflüstern. Denn Tobias#2 hatte ihn ja um 
diese Fakultät gefragt.

Tobias#2 bekommt jetzt also die Information, dass die Fakultät von 2 
gleich 2 sei. Das passt ihm gut, den damit kann er seine Aufgabe lösen, 
fak(3) zu berechnen. 2*3 gleich 6 und das wiederrum gibt er an seinen 
linken Nachbarn weiter, Tobas, der ihn ja darum gefragt hatte.

Der erste Tobias in der Kette, kennt jetzt die Fakultät von 3 und kann 
damit die Fakultät von 4 berechnen. 4 * 6 gleich 24

Laut und stolz verkündert der daher das Ergebnis: 24

von Karl H. (kbuchegg)


Lesenswert?

Tobias Mehrl schrieb:
> Ich hab jetzt mal dein gepimptes Programm ausgeführt. Ich hab mir das
> jetzt angeschaut und hab dazu noch eine Frage: Woher weiß dein bzw. ja
> auch mein Programm, dass, die Fakultät(1)=1 ist? Kommt das durch die
> if-Abfrage? Ist an der Stelle dann n=1?

Wie würdest du umgangssprachlich

  if(n == 1)
  {
    return 1;
  }

interpretieren, wenn n den Wert innerhalb einer Funktion enthält, für 
den die Funktion die Fakultät berechnen soll?

Heck. Das steht doch genauso auch in deinem Programm. Ich hab doch 
nichts anderes gemacht, als in dein Programm noch ein paar 
Ausgabeanweiszungen einzubauen.
ok dazu musste ich das

    return n*fakultaet(n-1);

in einzelne Schritte zerlegen, damit ich zwischen die Schritte 
Textausgaben reinpflanzen kann. Aber im Grunde ist es immer noch dein 
Programm, so wie du das geschrieben hast!

Ob ich schreibe

   return n*fakultaet(n-1);

oder
   int tmp = n*fakultaet(n-1);
   return tmp;

das ändert doch nichts!

Und wenn ich letzters dann auch noch auseinandernehme

   int fak = fakultaet(n-1);
   int tmp = n * fak;
   return tmp;

dann ist das immer noch dasselbe. Nur dass ich jetzt Ausgabeanweisungen 
dazwischen reinklenmmen kann.

von Tobias Mehrl (Gast)


Lesenswert?

Also, ich muss euch wirklich bewundern, dass ihr mit so belä******* 
Leuten wie z.B. so lange gedult habt. Eure Beispiele sind echt super. 
Ich denke auch, dass ich das jetzt für die Fakultät auch verstanden 
habe, aber ich weiß, wenn jetzt ein anderes Problem zur Rekursion kommt, 
dann kack ich wieder genauso ab wie bei den ganzen anderen Aufgabe die 
ich hier schon gestellt habe...

von Karl H. (kbuchegg)


Lesenswert?

Tobias Mehrl schrieb:
> Also, ich muss euch wirklich bewundern, dass ihr mit so belä*******
> Leuten wie z.B. so lange gedult habt. Eure Beispiele sind echt super.
> Ich denke auch, dass ich das jetzt für die Fakultät auch verstanden
> habe, aber ich weiß, wenn jetzt ein anderes Problem zur Rekursion kommt,
> dann kack ich wieder genauso ab wie bei den ganzen anderen Aufgabe die
> ich hier schon gestellt habe...

Na dann, musst du üben.

Die Fibonacci Zahlen definieren sich so
Fib(0) = 0
Fib(1) = 1
Fib(n) = Fib(n-1) + Fib(n-2)    ; für n > 1

Schreiben sie ein rekursives Programm zum Berechnen von Fibonacci Zahlen 
und berechnen sie Fib(6). Es genügt wenn das Programm die Zahlen einfach 
nur berechnet. Optimierungen sind nicht notwendig.

von Tobias Mehrl (Gast)


Lesenswert?

Danke für dein Beispiel.

Ich werds gleich mal probieren, ok?

von Tobias Mehrl (Gast)


Lesenswert?

Hm, also das hab ich jetzt ziemlich gut gelöst bekommen:

Mein Code:
1
#include<iostream>
2
using namespace std;
3
4
5
int fib(int n)
6
{
7
  if(n == 0)
8
  {
9
    return 0;
10
  }
11
12
  if(n == 1)
13
  {
14
    return 1;
15
  }
16
  else
17
  {
18
  return fib(n-1) + fib(n-2);
19
  }
20
}
21
22
23
int main()
24
{
25
  int n;
26
  cin >> n;
27
28
  cout << fib(n);
29
30
31
system("pause");
32
return 0;
33
}


Hast du noch ein Beispiel? Vielleicht in der Art nur etwas schwieriger?

von Huch (Gast)


Lesenswert?

>Hast du noch ein Beispiel? Vielleicht in der Art nur etwas schwieriger?

Grösster gemeinsamer Teiler?

ggt(a,0) = a
ggt(a,b) = ggt(b, a mod b) für b != 0

von Tobias Mehrl (Gast)


Lesenswert?

So, ich denke, der Code sollte den GgT Berechnen:
1
#include<iostream>
2
using namespace std;
3
4
5
int ggt(int a, int b)
6
{
7
  if(b == 0)
8
  {
9
    return a;
10
  }
11
  else
12
  {
13
  return ggt(b, (a % b));
14
  }
15
}
16
17
18
int main()
19
{
20
  int a, b;
21
  cin >> a;
22
  cin >> b;
23
24
  cout << ggt(a,b);
25
26
27
system("pause");
28
return 0;
29
}

von Tobias Mehrl (Gast)


Lesenswert?

Ich hab noch eine Aufgabe selber gefunden:

Ich soll nun durch Rekursive Programmierung mit dem Grundsatz "Teile und 
Herrsche" eine Min-Max-Suche durchführen. Ich darf ein short Array mit 
fest definierter Länge selbst mit unterschiedlichen Zahlen belegen. Aus 
diesem Array soll ich dann den kleinsten und größten Wert herausfinden. 
Den Min-Wert und den Max-Wert soll ich dann in einem Long ausgeben. In 
diesem Long soll in den oberen 16Bit der Max-Wert und in den unteren 
16Bit soll der Min-Wert stehen. Ich hab hier mal angefangen zu 
programmieren:
1
#include<iostream>
2
using namespace std;
3
4
5
6
//minmax
7
long minmax(short array[], int len)
8
{
9
  short teil1[3], teil2[3];
10
  
11
  cout << "Teil1: ";
12
13
  for(int i=0; i<3; i=i+1)
14
  {
15
    teil1[i] = array[i];
16
    cout << teil1[i];
17
  }
18
19
  cout << endl << "Teil2: ";
20
21
  for(int j=3; j<6; j=j+1)
22
  {
23
    teil2[j-3] = array[j];
24
    cout << teil2[j-3];
25
  }
26
27
  minmax(teil1, teil2);
28
}
29
30
31
32
33
int main()
34
{
35
    short array[6] = {4,3,7,2,1,6};
36
    int len = 6;
37
    long ergebnis;
38
    
39
40
    minmax(array, len);
41
42
43
44
system("pause");
45
return 0;
46
}

Das Problem ist nur, dass ich jetzt nicht mehr weiter weiß, wie ich 
meine Funktion (besser gesagt, die for-Schleifen) so "flexibel" mache, 
dass sie mir dann auch noch "teilen" wenn die Größe des array nur noch 
bei 3 ist...

von Karl H. (kbuchegg)


Lesenswert?

Tobias Mehrl schrieb:
> Hm, also das hab ich jetzt ziemlich gut gelöst bekommen:

OK.

> Hast du noch ein Beispiel? Vielleicht in der Art nur etwas schwieriger?

Es gibt noch jede Menge. Das Problem ist nur: es darf auch nicht zu 
schwer sein.

Aber da du ja mit Arrays auf Kriegsfuss stehst :-) wie wärs damit:

Es gibt da ein 2D Array mit einem Labyrinth. Jedes Element im Labyrinth 
wird durch einen Code gekennzeichnet

#  ist eine Mauer
M  ist die Maus
K  ist der Käse


richte dir ein 2D char Array [10][10] ein mit diesem Inhalt


[code]
    0123456789
 0  ##########
 1  # #  #K  #
 2  # # #### #
 3  #   #    #
 4  # # # ####
 5  ### # ## #
 6  #   #    #
 7  ## ##### #
 8  #        #
 9  ##########
[code]

Die Maus steht an Position [1][1], also hier
   0123456789
 0  ##########
 1  #M#  #K  #
 2  # # #### #
 3  #   #    #
 4  # # # ####
 5  ### # ## #
 6  #   #    #
 7  ## ##### #
 8  #        #
 9  ##########
[code]

schreiben sie ein rekursives Programm, das die Frage beantwortet: Wie 
kommt die Maus zum Käse?

Die Maus geht nach einer einfachen Strategie vor

   * an einem Feld mit einer Mauer kann ich nicht sein
   * bin ich am Feld mit dem Käse, Hurra! ich melde den
     Erfolg an die aufrufende Funktion
   * probiere es nach links, rechts, oben, unten
     Führt ein Schritt in diese Richtung nicht zum Erfolg
     dann wird die jeweils nächste Richtung probiert.
     Gibt es keine Richtung mehr, dann melde der aufrufenden
     Funktion, dass ich kein Glück hatte.

Die rekursive Funktion hat diese Signatur

   int  SchrittNach( int x, int y )
   {
      ....
   }

Der Return Wert ist die Rückmeldung, ob dieser Schritt zum Käse führt.
0 ... kein Erfolg, so gehts nicht zum Käse
1 ... Erfolg, Käse gefunden!

Als Ausgabe genügt es, die Feldnummern der betretenen Felder auszugeben, 
wobei es keine Rolle spielt, wenn diese Feldnummern in umgekehrter 
Reihenfolge ausgegeben werden in der die Maus eigentlich gehen müsste.

Wenn im obigen die Ausgabe lautet

[code]
    0123456789
 0  ##########
 1  #M#  #K  #
 2  # # #### #
 3  #   #    #
 4  # # # ####
 5  ### # ## #
 6  #   #    #
 7  ## ##### #
 8  #        #
 9  ##########
[code]

 1 7
 1 8
 2 8
 3 8
 3 7
 3 6
 3 5
 4 5
 5 5
 6 5
 6 6
 6 7
 6 8
 7 8
 8 8
 8 7
 8 6
 8 5
 8 4
 8 3
 7 3
 6 3
 6 4
 5 4
 4 4
 4 3
 3 3
 3 2
 3 1
 2 1
 1 1

von Tobias Mehrl (Gast)


Lesenswert?

Also ich werd dann mal die Maus machen:

Ich mach jetzt mal das hier:

"richte dir ein 2D char Array [10][10] ein mit diesem Inhalt"

von Karl H. (kbuchegg)


Lesenswert?

Tobias Mehrl schrieb:

> Das Problem ist nur, dass ich jetzt nicht mehr weiter weiß, wie ich
> meine Funktion (besser gesagt, die for-Schleifen) so "flexibel" mache,
> dass sie mir dann auch noch "teilen" wenn die Größe des array nur noch
> bei 3 ist...

Du hast dir kein rekursives Prinzip für deine Aufgabe überlegt.

Bei dieser Art Aufgaben lautet der Ansatz immer:

Wann ist das Problem besonders einfach?
Wie kann ich mit der Kentnis der Lösung eines etwas einfacheren Problems 
mein etwas schwierigere Problem lösen?

Nun in einem Array den kleinsten/größten Wert zu finden ist dann 
besonders einfach, wenn das Array nur 1 Element groß ist. Denn dann ist 
dieses 1 Element offensichtlich gleichzeitig sowohl das kleinste als 
auch das größte Element in diesem Array (mit Länge 1)

Das ist der sog. "Trivialfall"

Der schwierigere Fall sieht so aus:
Gegeben 1 Array mit Länge n
Dieses Array kann ich in 2 Teile teilen, zb in der Mitte
Ich hab also 2 Teilarrays von 0 bis n/2 (m Elemente) und von n/2 +1 bis 
n (k Elemente)
1
    |<--------- n ------->|
2
 
3
    * * * * * * * * * * * *
4
5
    |<--- m -->|<-- k --->|

Wenn ich nun Minimum und Maximum von m
als auch Minimum und Maximum von k kennen würde

DANN kann ich auch ganz leicht Minimum und Maximum von n ausrechnen.
Denn min(n)  = min( min(m), min(k) )
und  max(n)  = max( max(m), max(k) )

mit anderen Worten das Minimum von n ist der kleinere der beiden Werte: 
Minimum von m  und  Minimum von k
und das Maximum von n ist der größere der beiden Werte: Maximum von m 
und Maximum von k

Der Charm rekursiver Lösungen besteht darin, dass meistens nicht viel 
gerechnet wird :-) Man kennt eine triviale Lösung und ein Rezept, wie 
man den 'Schwierigkeitsgrad' sukzessive zurückdrehen kann sowie ein 
Rezept wie man das schwierigere Problem mithilfe des leichteren Problems 
lösen kann.

Bei der Fakultät war der Trivialfall
"Die Fakultät von 1 ist 1"

das Rezept um das schwierige Problem zu vereinfachen, ist nicht die 
Fakultät von n zu berechnen sondern die Fakultät von n-1.
Kennt man die Lösung für n-1 dann kann man damit das 'schwierigere' 
Problem, die Fakultät von n zu berechnen, lösen.

"Die Fakultät von n ist n mal die Fakultät von n-1"

Und da das rekursiv passiert, wird dadurch sukzessive das Problem immer 
einfacher, bis man irgendwann beim Trivialfall angelangt ist. Dann geht 
alles rückwärts und mit dem zweiten Teil des Rezepts baut sich dann aus 
den einfachen Lösungen rückwärts die schwierigere Lösung auf.

von Tobias Mehrl (Gast)


Lesenswert?

Kannst du mir sagen was an diesem Code falsch ist?
1
#include<iostream>
2
using namespace std;
3
4
5
6
int main()
7
{
8
9
10
  int i, j;
11
    char labyrinth[10][10];
12
  
13
  labyrinth[10][10] = {{"##########"},{"# #  #K  #"},{"# # #### #"},{"#   #    #"},{"# # # ####"},{"### # ## #"},{"#   #    #"},{"## ##### #"},{"#        #"},{"##########"}};
14
15
16
  for(i=0; i<10; i=i+1)
17
  {
18
    cout << endl;
19
    for(j=0; j<10; j=j+1)
20
    {
21
      cout << labyrinth[i][j];
22
    }
23
  }
24
25
26
system("pause");
27
return 0;
28
}

von Martin (Gast)


Lesenswert?

> Ich denke auch, dass ich das jetzt für die Fakultät auch verstanden
> habe, aber ich weiß, wenn jetzt ein anderes Problem zur Rekursion kommt,
> dann kack ich wieder genauso ab wie bei den ganzen anderen Aufgabe die
> ich hier schon gestellt habe...Beitrag melden | Bearbeiten | Löschen |

Hallo,

ich habe noch von einer anderen rekursive Rechenvorschrift gehört, die 
heißt 'Ackermann-Funktion'. Dabei kackt allerdings der Computer ab, 
sobald man etwas größere Zahlen einsetzt ...

Gruß,
Martin


ack (0,m) = m + 1
ack (n, 0) = ack (n-1, 1)
ack (n, m) = ack (n-1, ack (n, m-1))

von Huch (Gast)


Lesenswert?

Kannst Du vielleicht erstmal schreiben, wie Du darauf kommst, dass an 
dem Code was falsch ist?

von Tobias Mehrl (Gast)


Lesenswert?

Weil mir beim compilieren Vs 2010 das hier ausspuckt:

Fehler  1  error C2059: Syntaxfehler: '{'  c:\users\tobias 
mehrl\desktop\aufgabe2\aufgabe2\aufgabe2.cpp  12  1  aufgabe2
Fehler  2  error C2143: Syntaxfehler: Es fehlt ';' vor '{' 
c:\users\tobias mehrl\desktop\aufgabe2\aufgabe2\aufgabe2.cpp  12  1 
aufgabe2
Fehler  3  error C2143: Syntaxfehler: Es fehlt ';' vor '}' 
c:\users\tobias mehrl\desktop\aufgabe2\aufgabe2\aufgabe2.cpp  12  1 
aufgabe2
Fehler  4  error C2143: Syntaxfehler: Es fehlt ';' vor ',' 
c:\users\tobias mehrl\desktop\aufgabe2\aufgabe2\aufgabe2.cpp  12  1 
aufgabe2
Fehler  5  error C2143: Syntaxfehler: Es fehlt ';' vor ',' 
c:\users\tobias mehrl\desktop\aufgabe2\aufgabe2\aufgabe2.cpp  12  1 
aufgabe2
Fehler  6  error C2059: Syntaxfehler: ','  c:\users\tobias 
mehrl\desktop\aufgabe2\aufgabe2\aufgabe2.cpp  12  1  aufgabe2
Fehler  7  error C2059: Syntaxfehler: 'for'  c:\users\tobias 
mehrl\desktop\aufgabe2\aufgabe2\aufgabe2.cpp  15  1  aufgabe2
Fehler  8  error C2143: Syntaxfehler: Es fehlt ')' vor ';' 
c:\users\tobias mehrl\desktop\aufgabe2\aufgabe2\aufgabe2.cpp  15  1 
aufgabe2
Fehler  9  error C2143: Syntaxfehler: Es fehlt ';' vor '<' 
c:\users\tobias mehrl\desktop\aufgabe2\aufgabe2\aufgabe2.cpp  15  1 
aufgabe2
Fehler  10  error C4430: Fehlender Typspezifizierer - int wird 
angenommen. Hinweis: "default-int" wird von C++ nicht unterstützt. 
c:\users\tobias mehrl\desktop\aufgabe2\aufgabe2\aufgabe2.cpp  15  1 
aufgabe2
Fehler  11  error C4430: Fehlender Typspezifizierer - int wird 
angenommen. Hinweis: "default-int" wird von C++ nicht unterstützt. 
c:\users\tobias mehrl\desktop\aufgabe2\aufgabe2\aufgabe2.cpp  15  1 
aufgabe2
Fehler  12  error C2086: 'int i': Neudefinition  c:\users\tobias 
mehrl\desktop\aufgabe2\aufgabe2\aufgabe2.cpp  15  1  aufgabe2
Fehler  13  error C2059: Syntaxfehler: ')'  c:\users\tobias 
mehrl\desktop\aufgabe2\aufgabe2\aufgabe2.cpp  15  1  aufgabe2
Fehler  14  error C2143: Syntaxfehler: Es fehlt ';' vor '{' 
c:\users\tobias mehrl\desktop\aufgabe2\aufgabe2\aufgabe2.cpp  16  1 
aufgabe2
Fehler  15  error C2447: '{': Funktionsheader fehlt - Parameterliste im 
alten Stil?  c:\users\tobias 
mehrl\desktop\aufgabe2\aufgabe2\aufgabe2.cpp  16  1  aufgabe2
Fehler  16  error C4430: Fehlender Typspezifizierer - int wird 
angenommen. Hinweis: "default-int" wird von C++ nicht unterstützt. 
c:\users\tobias mehrl\desktop\aufgabe2\aufgabe2\aufgabe2.cpp  25  1 
aufgabe2
Fehler  17  error C2365: "system": Erneute Definition; vorherige 
Definition war "Funktion".  c:\users\tobias 
mehrl\desktop\aufgabe2\aufgabe2\aufgabe2.cpp  25  1  aufgabe2
Fehler  18  error C2440: 'Initialisierung': 'const char [6]' kann nicht 
in 'int' konvertiert werden  c:\users\tobias 
mehrl\desktop\aufgabe2\aufgabe2\aufgabe2.cpp  25  1  aufgabe2
Fehler  19  error C2059: Syntaxfehler: 'return'  c:\users\tobias 
mehrl\desktop\aufgabe2\aufgabe2\aufgabe2.cpp  26  1  aufgabe2
Fehler  20  error C2059: Syntaxfehler: '}'  c:\users\tobias 
mehrl\desktop\aufgabe2\aufgabe2\aufgabe2.cpp  27  1  aufgabe2
Fehler  21  error C2143: Syntaxfehler: Es fehlt ';' vor '}' 
c:\users\tobias mehrl\desktop\aufgabe2\aufgabe2\aufgabe2.cpp  27  1 
aufgabe2
Fehler  22  error C2059: Syntaxfehler: '}'  c:\users\tobias 
mehrl\desktop\aufgabe2\aufgabe2\aufgabe2.cpp  27  1  aufgabe2
  23  IntelliSense: Es wurde ein Ausdruck erwartet.  c:\users\tobias 
mehrl\desktop\aufgabe2\aufgabe2\aufgabe2.cpp  12  23  aufgabe2

von Rolf M. (rmagnus)


Lesenswert?

Tobias Mehrl schrieb:
> Kannst du mir sagen was an diesem Code falsch ist?
1
> labyrinth[10][10] = {{"##########"},{"# #  #K  #"},{"# # #### #"},{"#   #    #"},{"# # # ####"},{"### # ## #"},{"#   #    #"},{"## ##### #"},{"#        #"},{"##########"}};

Seufz

von Tobias Mehrl (Gast)


Lesenswert?

Ich will eine 10x10 Feld. Also schreib ich bei der Deklaration:

char labyrinth[10][10];

Das ist doch nicht falsch, oder etwa schon wieder?

von abc (Gast)


Lesenswert?

Rolf Magnus schrieb:
> /Seufz/
Ja.

@Tobias Mehrl (bandchef)
Wolltest du dir nicht ein C-Buch kaufen? So langsam wird das wirklich 
nervig mit deinen ewigen Grundlagenfragen welche in jedem Buch 
beantwortet werden!!! Jetzt postet du schon wieder eine solche Anfrage 
obwohl die letzte ( Beitrag "Array als Parameter in einer Funktion" ) 
noch nicht geklärt ist.

WIR SIND NICHT DAZU DA DIR DIE GRUNDLAGEN VON C ZU ERKLÄREN!!!

Und unterschätz die Sache mit dem Arbeitgeber nicht die ich im 
Array-Thread ansprach, das Internet vergisst nichts!

von Tobias Mehrl (Gast)


Lesenswert?

Aber warum funktioniert dann das hier?
1
#include<iostream>
2
3
using namespace std;
4
5
int main()
6
{
7
  char maze[20][21] = {
8
    "####################",
9
    "# # # # ###   # #  #",
10
    "# # # # #   # # # ##",
11
    "#   # # # ### # #  #",
12
    "### # # #   #   ## #",
13
    "#   # #   # # #    #",
14
    "# ### # ##### # # ##",
15
    "# #   # # # ### #  #",
16
    "# # ###   #  #  ## #",
17
    "#   # ### ## # #   #",
18
    "### # # # #  ### # #",
19
    "# # ###      # # # #",
20
    "#   # # ###### # # #",
21
    "# ###     #    # # #",
22
    "# #   ### # ## ### #",
23
    "#   # # # # #    # #",
24
    "### # # ### # #### #",
25
    "#     # # #        #",
26
    "# #####  F  # #### #",
27
    "####################"
28
  };
29
 
30
    return 0;
31
 }

von Huch (Gast)


Lesenswert?

Nun. Die Fehlermeldung bezieht sich ja nicht auf die Deklaration, oder?

von Karl H. (kbuchegg)


Lesenswert?

Tobias Mehrl schrieb:
> Aber warum funktioniert dann das hier?

Tobias Mehrl schrieb:
> Ich will eine 10x10 Feld. Also schreib ich bei der Deklaration:
>
> char labyrinth[10][10];
>
> Das ist doch nicht falsch, oder etwa schon wieder?

Das ist richtig.

Aber:

Arrays kann man nicht zuweisen.
Das hier
1
labyrinth[10][10] = {{"##########"},{"# #  #K  #"},{"# # #### #"},{"#   #    #"},{"# # # ####"},{"### # ## #"},{"#   #    #"},{"## ##### #"},{"#        #"},{"##########"}};

ist der Versuch einer Zuweisung, wenn ich mir die [10][10] mal großzügig 
wegdenke.

Arrays können initialisiert werden!
1
#include<iostream>
2
using namespace std;
3
4
int main()
5
{
6
  int i, j;
7
  char labyrinth[10][10] = {
8
       {"##########"},
9
       {"# #  #K  #"},
10
       {"# # #### #"},
11
       {"#   #    #"},
12
       {"# # # ####"},
13
       {"### # ## #"},
14
       {"#   #    #"},
15
       {"## ##### #"},
16
       {"#        #"},
17
       {"##########"}
18
  };
19
20
21
  for(i=0; i<10; i=i+1)
22
  {
23
    cout << endl;
24
    for(j=0; j<10; j=j+1)
25
    {
26
      cout << labyrinth[i][j];
27
    }
28
  }
29
30
31
system("pause");
32
return 0;
33
}

Siehst du. Und genau aus dem Grund bin ich ehrlich gesagt unglücklich 
damit, dass du dich an Rekursionen versuchst.
Du hast noch so viel, was viel wichtiger wäre. Rekursionen sind etwas, 
wenn man nach dem ersten Jahr eine gewisse Grundsicherheit erlangt hat.
Dazu gehört auch der Unterschied zwischen Initialisierung und Zuweisung.
Dazugehört noch jede Menge anderes Zeug. Massenhaft.
zb Parameter passing
zb Strukturen
zb komplexere Datenstrukturen
zb File Handling
zb dynamisches Speichermanagement

von Huch (Gast)


Lesenswert?

Genau. Kannst drei Worte Japanisch und willst schon Haikus schreiben. So 
geht das nicht.

von Klaus W. (mfgkw)


Lesenswert?

Karl heinz Buchegger schrieb:
> Es gibt da ein 2D Array mit einem Labyrinth.

Ich hatte mal an der Uni (ca. 1984 oder 1985) eine ähnliche Aufgabe:
Gegeben ist ein Labyrinth von der Standardeingabe mit Leerzeichen,
man durch kann, und * für Mauern.
Am oberen und unteren Ende ist je eine Öffnung, und gesucht ist ein
Weg, als Zusatzaufgabe der kürzeste Weg.

Dafür habe ich den Lösungsquelltext noch hier liegen, siehe
http://mfgkw.dyndns.org/lab.jpg

Was die Sache etwas spannender macht ist die Tatsache, daß es
da ohne Rekursion gehen musste :-)

von Tobias Mehrl (Gast)


Lesenswert?

Also gut Leute. Nochmal von vorne:

Der Code funktioniert nicht:
1
#include<iostream>
2
using namespace std;
3
4
int main()
5
{
6
  int i, j;
7
  char labyrinth[10][10] = {
8
       "##########",
9
       "# #  #K  #",
10
       "# # #### #",
11
       "#   #    #",
12
       "# # # ####",
13
       "### # ## #",
14
       "#   #    #",
15
       "## ##### #",
16
       "#        #",
17
       "##########"
18
  };
19
20
21
system("pause");
22
return 0;
23
}
24
25
26
system("pause");
27
return 0;
28
}


Warum geht dann aber das hier?
1
#include<iostream>
2
using namespace std;
3
4
int main()
5
{
6
  char labyrinth[20][21] = {
7
    "####################",
8
    "# # # # ###   # #  #",
9
    "# # # # #   # # # ##",
10
    "#   # # # ### # #  #",
11
    "### # # #   #   ## #",
12
    "#   # #   # # #    #",
13
    "# ### # ##### # # ##",
14
    "# #   # # # ### #  #",
15
    "# # ###   #  #  ## #",
16
    "#   # ### ## # #   #",
17
    "### # # # #  ### # #",
18
    "# # ###      # # # #",
19
    "#   # # ###### # # #",
20
    "# ###     #    # # #",
21
    "# #   ### # ## ### #",
22
    "#   # # # # #    # #",
23
    "### # # ### # #### #",
24
    "#     # # #        #",
25
    "# #####  F  # #### #",
26
    "####################"
27
  };
28
29
30
system("pause");
31
return 0;
32
}


Aber warum geht dann das hier? da ist doch verdammt nochmal nix anders 
wie bei meinem code mit dem kleineren array!

von Karl H. (kbuchegg)


Lesenswert?

Tobias Mehrl schrieb:

> Aber warum geht dann das hier? da ist doch verdammt nochmal nix anders
> wie bei meinem code mit dem kleineren array!

Schau dir deinen Code noch einmal genau an!

zb wieviele
  system("pause");
zählst du in deinem Code?

Wieviele Klammern { gehen auf und wieviele } gehen zu ?

von abc (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:
> Dazugehört noch jede Menge anderes Zeug. Massenhaft.
> zb Parameter passing
> zb Strukturen
> zb komplexere Datenstrukturen
> zb File Handling
> zb dynamisches Speichermanagement

und Wissen rund um Strings wie der letzte Codeschnipsel beweist.

Oh, wenn bloss alle Leute soviel Geduld wie der Herr Moderator hätten, 
die Welt würde viel gewinnen. Respekt!

von Tobias Mehrl (Gast)


Lesenswert?

Die 2 system...

sind durch copy&paste reingekommen

von Rolf M. (rmagnus)


Lesenswert?

In C++ paßt "##########" nicht in ein Array aus 10 char. Du brauchst 11, 
weil ein String immer mit einem '\0' abgeschlossen wird.
In C könntest du so ein Array trotzdem mit "##########" initialisieren. 
Dann würde das \0 einfach weggelassen werden. In C++ ist das aber nicht 
erlaubt, und du hast ja hier C++.

von Karl H. (kbuchegg)


Lesenswert?

Tobias Mehrl schrieb:
> Die 2 system...
>
> sind durch copy&paste reingekommen

Grrr

1
  char labyrinth[20][21] = {
2
    "####################",

Zähl doch mal, wieviele # hier in einem String sind und dann vergleichst 
du mit der Arraydeklaration. Vielleicht klingelts dann.

Ich kann mir aber ehrlich gesagt nicht wirklich vorstellen, dass in so 
einem Fall die Fehlermeldung des Compilers wahnsinnig kryptisch am 
eigentlichen Problem vorbeiredet.

von Huch (Gast)


Lesenswert?

Meine Güte. Jetzt reichts aber. Du kannst ja nicht mal den Unterschied 
zwischen zwei Texten erkennen.

von Klaus W. (mfgkw)


Lesenswert?

> Oh, wenn bloss alle Leute soviel Geduld wie der Herr Moderator hätten,
> die Welt würde viel gewinnen. Respekt!

Nein, innerhalb einer Generation würde die Welt untergehen, weil
keiner mehr etwas selber macht.
Weil nicht jeder so geduldig ist, dauert es etwas länger.

Also, wenn die Welt untergeht, weiß ich welcher Moderator hier schuld
ist :-)

von Tobias Mehrl (Gast)


Lesenswert?

Ja, ihr habt recht. Ich weiß jetzt woran ich wieder gescheiter bin.

Ich hab einmal ein 10x10 und einmal 20x20 Feld. Da nun in C++ 10 bzw. 20 
chars nicht in ein 20 Elemente array reinpasst muss man eben schreiben:

array[10][11]

bzw.

array[20][21]

Ich bin dann off. Mir reichts für heute. Bis morgen dann, dann kann ich 
euch wieder nerven... :-)

von Yalu X. (yalu) (Moderator)


Lesenswert?

Klaus Wachtler schrieb:
> Dafür habe ich den Lösungsquelltext noch hier liegen, siehe
> http://mfgkw.dyndns.org/lab.jpg

Jetzt machst du's Tobias aber sehr leicht. Er braucht deinen geposteten
Code ja nur noch durch den Fortran-nach-C++-Konverter zu schicken, der
sicher auch automatisch erkennt, dass man den Algorithmus rekursiv
darstellen kann.

;-) SCNR

von Klaus W. (mfgkw)


Lesenswert?

Nicht auf allen Karten ist der oben aufgedruckte Text lesbar,
bei einigen müsste man sich die Löcher anschauen und den Text
rekonstruieren.
Er hat also doch noch etwas zu kodieren...

von Purzel H. (hacky)


Lesenswert?

Toll, diese Rekursionen... kann denn jemand die Fakultaet einer reelen, 
resp komplexen Zahl rechnen ?

Duck und weg

von Klaus W. (mfgkw)


Lesenswert?

Gammafunktion, gar kein Thema...

von Karl H. (kbuchegg)


Lesenswert?

Klaus Wachtler schrieb:

> Dafür habe ich den Lösungsquelltext noch hier liegen, siehe
> http://mfgkw.dyndns.org/lab.jpg

Ja, ja
      IMPLICIT INTEGER (A-Z)

und auch ein
      COMMON ....

immer wieder ein Quell der Freude in Fortran :-)

Könnte mich in den A... beissen, dass ich all meine Lochkarten vor 
Jahren entsorgt habe. Eine einzige ist übrig geblieben und von der weiß 
ich nicht genau wo sie ist.

von Tobias Mehrl (Gast)


Lesenswert?

Hi Leute!

Zitat von kbuchegg:

"Die Maus geht nach einer einfachen Strategie vor

   * an einem Feld mit einer Mauer kann ich nicht sein
   * bin ich am Feld mit dem Käse, Hurra! ich melde den
     Erfolg an die aufrufende Funktion
   * probiere es nach links, rechts, oben, unten
     Führt ein Schritt in diese Richtung nicht zum Erfolg
     dann wird die jeweils nächste Richtung probiert.
     Gibt es keine Richtung mehr, dann melde der aufrufenden
     Funktion, dass ich kein Glück hatte.

Die rekursive Funktion hat diese Signatur

   int  SchrittNach( int x, int y )
   {
      ....
   }

Der Return Wert ist die Rückmeldung, ob dieser Schritt zum Käse führt.
0 ... kein Erfolg, so gehts nicht zum Käse
1 ... Erfolg, Käse gefunden!"


Ich bin jetzt soweit, dass ich das Array ja mittlerweile richtig mit dem 
Labyrinth intialisiert habe. Die oben schon gezeigt Funktion hab ich 
schon mal so Lückenhaft in meinen Code implementiert. Jetzt hab doch 
noch einige Probleme damit. Ich weiß jetzt irgendwie nicht, wie bzw. mit 
welchen Parametern ich die Funktion aufrufen soll...

Ich stell mir grad das so vor. Die Funktion soll prüfen, ob über, links, 
rechts bzw. unten ein # ist. Soweit ist das doch richtig?

Ich hab mir jetzt mal die Variablen x bzw. y mit jeweils einer 1 
initialisert weil ja die Maus bei 1,1 startet. Die Funktion soll nun 
rekursiv die weiter oben genannten Fälle überprüfen, nicht wahr?

Mein Code sieht jetzt schon mal so aus:
1
#include<iostream>
2
using namespace std;
3
4
5
6
int SchrittNach(int x, int y)
7
{
8
  if(((x-1) == '#') || ((y-1) == '#'))
9
  {
10
    SchrittNach(x+1, y+1);
11
  }
12
  else
13
  {
14
    if(((x+1) == '#') || ((y+1) == '#'))
15
    {
16
      SchrittNach(x-1, y-1);
17
    }
18
  }
19
return 1;
20
}
21
22
23
24
25
int main()
26
{
27
  int x, y;
28
  char labyrinth[10][11] = {
29
       "##########",
30
       "# #  #K  #",
31
       "# # #### #",
32
       "#   #    #",
33
       "# # # ####",
34
       "### # ## #",
35
       "#   #    #",
36
       "## ##### #",
37
       "#        #",
38
       "##########"
39
  };
40
41
42
  x = 1;
43
  y = 1;
44
45
  cout << SchrittNach(x, y);
46
47
48
system("pause");
49
return 0;
50
}

Aber irgendwie weiß ich jetzt da schon mal wieder nicht mehr weiter, wie 
ich da jetzt das else formulieren soll. Außerdem bin ich mir auch nicht 
sicher, ob das schon alle möglichen Fäll abdeckt, aber wahrscheinlich 
nicht.

von Tobias Mehrl (Gast)


Lesenswert?

Hm, ich hab mir jetzt nochmal meine obige Funktion durchgeschaut und 
dabei festgestellt, dass das was ich da geschrieben hab vielleicht doch 
nicht so intellegient war. Hier mal noch mein neuer Versuch (diesmal 
aber nur die Funktion an sich; die Ausgabe is ja dann wieder was 
anderes):
1
int SchrittNach(int x, int y)
2
{
3
  if((x-1) == '#')
4
  {
5
    SchrittNach(x+1, y);
6
  }
7
  else
8
  {
9
    if((x+1) == '#')
10
    {
11
      SchrittNach(x-1, y);
12
    }
13
  }
14
  return 1;
15
16
  if((y-1) == '#')
17
  {
18
    SchrittNach(x, y+1);
19
  }
20
  else
21
  {
22
    if((y+1) == '#')
23
    {
24
      SchrittNach(x, y-1);
25
    }
26
  }
27
  return 1;
28
}

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Anstatt zu prüfen wo eine Wand ist und dann blind in die andere Richtung 
(möglicherweise gegen eine andere) zu laufen. würde es wohl helfen wenn 
du schaust wo noch frei ist!

Ansonsten wie wäre es mit ausprobieren?
Auch müsste man meiner Meinung nach eine Art "besucht" Flag mitführen um 
nicht eine endlosrekursion zu erzeugen (so etwas ist dann auch keine 
Rekursion mehr sonder nennt sich Backtracking, bei einer Rekursion hat 
man idR eine Funktion die idealerweise beweisbar (streng) monoton ist)

von Daniel -. (root)


Lesenswert?

Karl heinz Buchegger schrieb:
> DANN kann ich auch ganz leicht Minimum und Maximum von n ausrechnen.
> Denn min(n)  = min( min(m), min(k) )
> und  max(n)  = max( max(m), max(k) )
>
> mit anderen Worten das Minimum von n ist der kleinere der beiden Werte:
> Minimum von m  und  Minimum von k
> und das Maximum von n ist der größere der beiden Werte: Maximum von m
> und Maximum von k
>
> Der Charm rekursiver Lösungen besteht darin, dass meistens nicht viel
> gerechnet wird :-) Man kennt eine triviale Lösung und ein Rezept, wie
> man den 'Schwierigkeitsgrad' sukzessive zurückdrehen kann sowie ein
> Rezept wie man das schwierigere Problem mithilfe des leichteren Problems
> lösen kann.

hört sich wie das Optimalitätskriterium von Bellman an.
Und da heute nahezu jeder 4 Kerne CPUs besitzt (hust ... :-)
bietet sich OpenMP task pragma für die weitere Programmverfeinerung an.

von Karl H. (kbuchegg)


Lesenswert?

Es sieht zwar auf den ersten Blick ungewohnt aus, aber ich würde es so 
angehen:

Die Funktion SchrittNach soll als Returnwert eine Aussage darüber 
liefern, ob dieser Schritt zum Ziel führt.

Gut.

Wann führt er nicht zum Ziel?

Wenn der Schritt in eine Mauer führen würde oder wenn keiner der von 
dieser Position weiter erreichbaren Schritte zum Ziel führt.

Und genauso würde ich das auch programmieren
1
int SchrittNach(int x, int y)
2
{
3
  // Hurra! Da liegt er ja
4
  //
5
  if( labyrinth[x][y] == 'K' )
6
    return TRUE;
7
8
  // Käse nicht da, aber laufen wir in eine Mauer? Wenn ja
9
  // dann kann dieser Schritt nicht Teil der Lösung sein
10
  if( labyrinth[x][y] = '#' )
11
    return FALSE;
12
13
  // OK. Die Maus steht im freien Feld und der Käse ist auch nicht da
14
  // Das bedeutet: dieser SChritt könnte Teil der Lösung sein, wenn
15
  // es in einer Richtung zum Käse geht
16
  //
17
  // allerdings hinterlässt sich die Maus eine Markierung, dass sie schon
18
  // mal hier war, damit sie dasselbe Feld nicht noch einmal besucht. Wenn
19
  // sie schon mal hier war, dann gehts hier auch nicht zum Käse
20
  if( labyrinth[x][y] == '.' )
21
    return FALSE;
22
23
  labyrinth[x][y] = '.';
24
25
  //
26
  // probiers nach rechts
27
  if( SchrittNach[x+1][y] )
28
    return TRUE;
29
30
  // rechts wahr wohl nichts, probiers nach links
31
  if( SchrittNach[x-1][y] )
32
    return TRUE;
33
34
  // rechts links war nichts, nach oben
35
  if( SchrittNach[x][y-1] )
36
    return TRUE;
37
38
  // bleibt nur noch nach unten
39
  if( SchrittNach[x][y+1] )
40
    return TRUE;
41
42
  // in keiner der 4 Richtungen gehts zum Käse
43
  // Schade. Damit steht auch fest, dass dieses Feld nicht auf
44
  // dem Lösungsweg liegen kann. Mach die Markierung wieder weg
45
46
  labyrinth[x][y] = ' ';
47
48
  return FALSE;
49
}

sollte nicht so weit daneben sein.
Nebeneffekt: Die hinterlassenen Markierungen zeigen auch gleich den Weg 
an :-)

Aber ich merke, dass die Aufgabe gemein war. Das ist eine Sonderform 
einer Rekursion, die man 'Backtracking' nennt. Das Prinzip: Eine Lösung 
finden, indem man geordnet alle Möglichkeiten durchprobiert. Und genau 
das tut die Maus, sie probiert alle 4 Richtungen aus, welche davon zum 
Ziel führt.

Wie schon gesagt: leg das ad acta. Das ist alles viel zu schwer für 
deinen momentanen Stand. Das hat alles noch keinen Sinn. Da gibt es 
wichtigere Dinge

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

1
if( SchrittNach[x+1][y] )
soll wohl eher heißen
1
if( SchrittNach(x+1, y) )
oder kann man in C jetzt auch schon Funktionen als Array ansprechen? o.O

von Karl H. (kbuchegg)


Lesenswert?

Danke für die Korrektur.
Sitz hier im Dunkeln auf einem fremden Rechner mit einer flachen 
$#+$§"?# Tastatur und vertipp mich dauernd. Da hab ich nicht drauf 
geachtet :-)

von Karl H. (kbuchegg)


Lesenswert?

@Tobias

Aber wenn du mit Arrays üben willst :-)


Wir haben eine Kammer, in der sind Elementarteilchen. Die Kammer ist 10 
mal 10 Felder groß

     0   1   2   3   4   5   6   7   8   9
   +---+---+---+---+---+---+---+---+---+---+
0  |   |   |   |   |   |   |   |   |   |   |
   +---+---+---+---+---+---+---+---+---+---+
1  |   |   |   |   |   |   |   |   |   |   |
   +---+---+---+---+---+---+---+---+---+---+
2  |   |   | T |   |   |   |   |   |   |   |
   +---+---+---+---+---+---+---+---+---+---+
3  |   |   |   |   |   |   |   |   |   |   |
   +---+---+---+---+---+---+---+---+---+---+
4  |   |   |   |   |   |   |   |   |   |   |
   +---+---+---+---+---+---+---+---+---+---+
5  |   |   |   |   |   |   |   | T |   |   |
   +---+---+---+---+---+---+---+---+---+---+
6  |   |   |   |   |   |   |   |   |   |   |
   +---+---+---+---+---+---+---+---+---+---+
7  |   | T |   |   |   |   |   |   |   |   |
   +---+---+---+---+---+---+---+---+---+---+
8  |   |   |   |   |   |   |   |   |   |   |
   +---+---+---+---+---+---+---+---+---+---+
9  |   |   |   |   |   |   |   | T |   |   |
   +---+---+---+---+---+---+---+---+---+---+

UM rauszukriegen, wo die Teilchen sitzen, schickt man ein Testteilchen 
an einer (vom Benutzer vorgegebenen) Stelle am Rand in dieses Array.
Dieses Test-Teilchen wandert immer geradlinig.
Allerdings gilt:

* trifft es frontal auf ein T so wird er dort reflektiert, dreht also
  seine Richtung um.

  Ein in die Zeile 2 eingespeistes Teilchen legt also diesen Weg zurück 
...

     0   1   2   3   4   5   6   7   8   9
   +---+---+---+---+---+---+---+---+---+---+
0  |   |   |   |   |   |   |   |   |   |   |
   +---+---+---+---+---+---+---+---+---+---+
1  |   |   |   |   |   |   |   |   |   |   |
   +---+---+---+---+---+---+---+---+---+---+
2  | * | * | * | T |   |   |   |   |   |   |
   +---+---+---+---+---+---+---+---+---+---+
3  |   |   |   |   |   |   |   |   |   |   |
   +---+---+---+---+---+---+---+---+---+---+
4  |   |   |   |   |   |   |   |   |   |   |
   +---+---+---+---+---+---+---+---+---+---+
5  |   |   |   |   |   |   |   | T |   |   |
   +---+---+---+---+---+---+---+---+---+---+
6  |   |   |   |   |   |   |   |   |   |   |
   +---+---+---+---+---+---+---+---+---+---+
7  |   | T |   |   |   |   |   |   |   |   |
   +---+---+---+---+---+---+---+---+---+---+
8  |   |   |   |   |   |   |   |   |   |   |
   +---+---+---+---+---+---+---+---+---+---+
9  |   |   |   |   |   |   |   | T |   |   |
   +---+---+---+---+---+---+---+---+---+---+

  .. und kommt wieder in Zeile 2/links wieder heraus.

* Ein Test-Teilchen welches neben (über/unter/rechts/links) einem
  Teilchen ankommt, wird um 90° abgelenkt.
  Ein in die Zeile 1 eingespeistes Teilchen legt also diesen Weg
  zurück

     0   1   2   3   4   5   6   7   8   9
   +---+---+---+---+---+---+---+---+---+---+
0  |   |   | * |   |   |   |   |   |   |   |
   +---+---+---+---+---+---+---+---+---+---+
1  | > | * | * |   |   |   |   |   |   |   |
   +---+---+---+---+---+---+---+---+---+---+
2  |   |   |   | T |   |   |   |   |   |   |
   +---+---+---+---+---+---+---+---+---+---+
3  |   |   |   |   |   |   |   |   |   |   |
   +---+---+---+---+---+---+---+---+---+---+
4  |   |   |   |   |   |   |   |   |   |   |
   +---+---+---+---+---+---+---+---+---+---+
5  |   |   |   |   |   |   |   | T |   |   |
   +---+---+---+---+---+---+---+---+---+---+
6  |   |   |   |   |   |   |   |   |   |   |
   +---+---+---+---+---+---+---+---+---+---+
7  |   | T |   |   |   |   |   |   |   |   |
   +---+---+---+---+---+---+---+---+---+---+
8  |   |   |   |   |   |   |   |   |   |   |
   +---+---+---+---+---+---+---+---+---+---+
9  |   |   |   |   |   |   |   | T |   |   |
   +---+---+---+---+---+---+---+---+---+---+

wenn es auf seinem weiteren Weg wieder auf andere Teilchen trifft, dann 
geht das Spiel natürlich weiter. zb in Zeile 3

     0   1   2   3   4   5   6   7   8   9
   +---+---+---+---+---+---+---+---+---+---+
0  |   |   |   |   |   |   |   |   |   |   |
   +---+---+---+---+---+---+---+---+---+---+
1  |   |   |   |   |   |   |   |   |   |   |
   +---+---+---+---+---+---+---+---+---+---+
2  |   |   |   | T |   |   |   |   |   |   |
   +---+---+---+---+---+---+---+---+---+---+
3  | > | * | * |   |   |   |   |   |   |   |
   +---+---+---+---+---+---+---+---+---+---+
4  |   |   | * |   |   |   |   |   |   |   |
   +---+---+---+---+---+---+---+---+---+---+
5  |   |   | * |   |   |   |   | T |   |   |
   +---+---+---+---+---+---+---+---+---+---+
6  |   |   | * | * | * | * | * |   |   |   |
   +---+---+---+---+---+---+---+---+---+---+
7  |   | T |   |   |   |   | * |   |   |   |
   +---+---+---+---+---+---+---+---+---+---+
8  |   |   | * | * | * | * | * |   |   |   |
   +---+---+---+---+---+---+---+---+---+---+
9  |   |   | v |   |   |   |   | T |   |   |
   +---+---+---+---+---+---+---+---+---+---+

Das Teilchen kommt also in Spalte 2/unten wieder raus.

* trifft ein Teilchen auf eine Lücke zwischen den Teilchen, dann geht es 
einfach durch die Lücke durch und wird nicht abgelenkt

     0   1   2   3   4   5   6   7   8   9
   +---+---+---+---+---+---+---+---+---+---+
0  |   |   |   |   |   |   |   | * |   |   |
   +---+---+---+---+---+---+---+---+---+---+
1  |   |   |   |   |   |   |   | * |   |   |
   +---+---+---+---+---+---+---+---+---+---+
2  |   |   | T |   |   |   |   | * |   |   |
   +---+---+---+---+---+---+---+---+---+---+
3  | > | * | * | * | * | * | * | * |   |   |
   +---+---+---+---+---+---+---+---+---+---+
4  |   |   | T |   |   |   |   |   | T |   |
   +---+---+---+---+---+---+---+---+---+---+
5  |   |   |   |   |   |   |   |   |   |   |

Hier kommt also das Teilchen, welche in Zeile 3/links eingespeist wurde, 
in der Spalte 7/oben wieder raus.

Zu schreiben ist ein Programm, welches dieses Verhalten simuliert und 
bestimmt, wo ein Testteilchen wieder rauskommt, wenn man es an einer 
bestimmten Zeile/Spalte oben/unten Links/rechts einspeist.
Die Art der Benutzereingabe ist dem Programmierer überlassen. Der 
Benutzer muss nur die Möglichkeit haben festzulegen, wo genau er sein 
Testteilchen einspeisen will. Dazu genügt zb eine Systematik

   u4    u für 'unten' (muss daher eine Spalte sein) und zwar die 4.
   o3    o wie oben
   r6    rechts, 6. Zeile
   l7    links, 7. Zeile

Fürs erst genügt es, wenn das Programm eine 'Grafik' ausgibt, in der der 
Weg des Testteilchens eingezeichnet ist und zwar für jeden einzelnen 
'Schritt' des Testteilchens. Die Verteilung der T in der Matrix kann als 
fix angenommen werden und wird vom Programmierer festgelegt (braucht 
also nicht eingebbar sein)

Eine 'schöne' Ausgabe mit Trennlinien, ähnlich obiger ASCII Grafik, ist 
anzustreben. Eintritt und Austrittspunkt sind mit frei zu wählenden 
Sonderzeichen zu markieren. Nach dem Austritt soll das Programm auch 
noch das Endergebnis ("Austritt bei .....") bekannt geben.


Sonderpunkte: Wie könnte man aus dieser Grundidee ein Spiel entwickeln?


Besonderes Augenmerk ist auf eine korrekte Behandlung der Randfelder zu 
legen. Ich werde mir das Programm dann sehr genau ansehen, ob das 
Programm irgendwo 'Out of Bounds' auf das Array zugreift.

von jungspund (Gast)


Lesenswert?

@ Karl heinz:

Hast du schonmal daran gedacht, eine Homepage zu machen mit solchen 
Programmier-Tutorials und -Aufgaben mit Lösungen und Erklärungen? =)
Würde ja schon fast reichen, wenn du das, was du hier schreibst sammeln 
würdest.
Finde das sehr interessant und lehrreich! Und deine Aufgaben sind immer 
interessant und klug ausgewählt!

von Tobias Mehrl (Gast)


Lesenswert?

Zitat:

"Wie schon gesagt: leg das ad acta. Das ist alles viel zu schwer für
deinen momentanen Stand. Das hat alles noch keinen Sinn. Da gibt es
wichtigere Dinge."


Ich hab mir gestern nach meinem letzten Eintrag nach fast ne Stunden 
darüber den Kopf zerbrechen, und war heute, ja schon fast, überglücklich 
als ich deine Lösung gesehen. Ich hab nämlich so von der Grundüberlegung 
her, das gleich im Kopf bzw. in Visual Studio gehabt wie du gepostet 
hast. Ich zeig euch hier mal meinen neuen Code bei dem ich noch ein 
Problem hab (natürlich sieht der jetzt ähnlich mit dem von kabuchegg 
aus, da ich ihn daran angepasst hab):
1
#include<iostream>
2
using namespace std;
3
4
5
6
int SchrittNach(int x, int y)
7
{
8
  char Labyrinth[10][11];
9
10
  if(Labyrinth[x][y] == 'K')
11
  return 1;
12
13
  if(Labyrinth[x][y] == '#')
14
  return 0;
15
16
  if(Labyrinth[x][y] == '.')
17
  return 0;
18
19
20
21
  if(SchrittNach(x-1, y))
22
    return 1;
23
24
  if(SchrittNach(x+1, y))
25
    return 1;
26
27
  if(SchrittNach(x, y-1))
28
    return 1;
29
30
  if(SchrittNach(x, y+1))
31
    return 1;
32
33
34
35
  if(Labyrinth[x][y] == ' ')
36
    return 0;
37
}
38
39
40
41
int main()
42
{
43
  int x, y;
44
  char Labyrinth[10][11] = {
45
       "##########",
46
       "# #  #K  #",
47
       "# # #### #",
48
       "#   #    #",
49
       "# # # ####",
50
       "### # ## #",
51
       "#   #    #",
52
       "## ##### #",
53
       "#        #",
54
       "##########"
55
  };
56
57
58
  x = 1;
59
  y = 1;
60
61
62
  SchrittNach(x, y);
63
64
65
system("pause");
66
return 0;
67
}

Ich hab jetzt nur noch das Problem, dass mir VS2010 folgende 
Fehlermeldung in Zeile 37 zurückgibt: "SchrittNach": Nicht alle 
Steuerelementpfade geben einen Wert zurück. Zeile 37 ist die Zeile mit 
der schließenden Klammer der Funktion. Ich verstehe die Fehlermeldung 
nicht. Ich hab bei der jeder if-Abfrage ein return. Warum gibt dann 
nicht alle Steuerelementpfade was zurück?

Wie kann ich das jetzt eigentlich ausgeben? Ich mein, ich möchte ja nun 
auch noch sehen, wie die Maus ihren Punkt hinterlässt und das M zum K 
wandert...

von abc (Gast)


Lesenswert?

> Warum gibt dann nicht alle Steuerelementpfade was zurück?
Wenn keine der Bedingungen in den ifs erfüllt ist gibt es keinen 
Rückgabewert. Programmlogik hab ich nicht geprüft.

Wär übrigens nett wenn du im "Array-als-Funktionsparameter"-Thread 
nochmal eine Rückmeldung gibst...

von Karl H. (kbuchegg)


Lesenswert?

Tobias Mehrl schrieb:

> der schließenden Klammer der Funktion. Ich verstehe die Fehlermeldung
> nicht.

>
>   if(Labyrinth[x][y] == ' ')
>     return 0;
> }

Und was wenn Labyrinth[x][y] eben nicht ' ' ist?
Was wird dann zurückgegeben?

> Ich hab bei der jeder if-Abfrage ein return.
> Warum gibt dann
> nicht alle Steuerelementpfade was zurück?

Weil es ja auch sein kann, dass kein einziges if zu einem return führt

> Wie kann ich das jetzt eigentlich ausgeben? Ich mein, ich möchte ja nun
> auch noch sehen, wie die Maus ihren Punkt hinterlässt und das M zum K
> wandert...

Indem du deine Ausgabefunktion an 'strategisch günstigen Stellen' 
aufrufst?

von Tobias Mehrl (Gast)


Lesenswert?

"Und was wenn Labyrinth[x][y] eben nicht ' ' ist?
Was wird dann zurückgegeben?"

Dann überspringt mein Programm momentan das return und macht beim 
nächsten if weiter. Ich will, aber das mir das Programm eine 1 
rückmeldet, wenn ' ' nicht erfüllt ist nicht wahr? Was mach ich dann? 
Ich denke ich sollte dann noch ein else an jedes if ranschreiben, die 
mir eben dann diese 1 zurückgibt, oder?

von abc (Gast)


Lesenswert?

>Ich denke ich sollte dann noch ein else an jedes if ranschreiben, die
>mir eben dann diese 1 zurückgibt, oder?

Nein. Denn dann würde die erste nicht erfüllte Bedingung die Funktion 
beenden, ohne das die anderen if überhaupt beachtet werden.
1
if(....)
2
  return 1;
3
4
if(....)
5
  return 0;
6
 //<-- Hier ist KEIN if
7
return 2;

gibt 2 zurück wenn keine der Bedingungen wahr ist.

von Klaus W. (mfgkw)


Lesenswert?

Es würde formal reichen, zum Schluß ein letztes return .. mit
irgendeinem zu nehmen.

Weil aber imemr nur einer der Zweige gemacht wird, wäre es
schöner zu lesen und einfacher zu erfassen, wenn man else so
einfügt:
1
  if(Labyrinth[x][y] == 'K' )
2
    return 1;
3
  else if(Labyrinth[x][y] == '#' )
4
    return 0;
5
  else if(Labyrinth[x][y] == '.')
6
    return 0;
7
  else if(SchrittNach(x-1, y))
8
    return 1;
9
  else if(SchrittNach(x+1, y))
10
    return 1;
11
  else if(SchrittNach(x, y-1))
12
    return 1;
13
  else if(SchrittNach(x, y+1))
14
    return 1;
15
  else if(Labyrinth[x][y] == ' ')
16
    return 0;
17
  else
18
    return 0; // sollte nie auftreten!


Weiterhion plädiere ich stark dafür, generell Blöcke zu klammern.
Es gibt auch gegenteilige Meinungen (Platzverschwendung!), aber
die sind alle doof.

Dann sieht es so aus:
1
  if(Labyrinth[x][y] == 'K' )
2
  {
3
    return 1;
4
  }
5
  else if(Labyrinth[x][y] == '#' )
6
  {
7
    return 0;
8
  }
9
  else if(Labyrinth[x][y] == '.')
10
  {
11
    return 0;
12
  }
13
  else if(SchrittNach(x-1, y))
14
  {
15
    return 1;
16
  }
17
  else if(SchrittNach(x+1, y))
18
  {
19
    return 1;
20
  }
21
  else if(SchrittNach(x, y-1))
22
  {
23
    return 1;
24
  }
25
  else if(SchrittNach(x, y+1))
26
  {
27
    return 1;
28
  }
29
  else if(Labyrinth[x][y] == ' ')
30
  {
31
    return 0;
32
  }
33
  else
34
  {
35
    return 0; // sollte nie auftreten!
36
  }

So wären alle zufrieden: ich und der Compiler.

von Tobias Mehrl (Gast)


Lesenswert?

Ok. Es sieht dann jetzt so aus:
1
#include<iostream>
2
using namespace std;
3
4
5
6
int SchrittNach(int x, int y)
7
{
8
  char Labyrinth[10][11];
9
10
  if(Labyrinth[x][y] == 'K')
11
  return 1;
12
13
  if(Labyrinth[x][y] == '#')
14
  return 0;
15
16
  if(Labyrinth[x][y] == '.')
17
  return 0;
18
19
20
21
  if(SchrittNach(x-1, y))
22
    return 1;
23
24
  if(SchrittNach(x+1, y))
25
    return 1;
26
27
  if(SchrittNach(x, y-1))
28
    return 1;
29
30
  if(SchrittNach(x, y+1))
31
    return 1;
32
33
34
35
  if(Labyrinth[x][y] == ' ')
36
    return 2;
37
}
38
39
40
41
int main()
42
{
43
  int x, y;
44
  char Labyrinth[10][11] = {
45
       "##########",
46
       "# #  #K  #",
47
       "# # #### #",
48
       "#   #    #",
49
       "# # # ####",
50
       "### # ## #",
51
       "#   #    #",
52
       "## ##### #",
53
       "#        #",
54
       "##########"
55
  };
56
57
58
  x = 1;
59
  y = 1;
60
61
62
  SchrittNach(x, y);
63
64
65
system("pause");
66
return 0;
67
}

Wenn ich es durchkompiliere, dann kommt aber der Fehler immer noch und 
das "Programm" bricht nach kurzer Zeit ab. -> Endlosschleife?

von Klaus W. (mfgkw)


Lesenswert?

Wo ist hier eine Schleife? Er wird halt fertig sein nach einem Schritt.

von Tobias Mehrl (Gast)


Lesenswert?

Ok. Es sieht dann jetzt so aus:
1
#include<iostream>
2
using namespace std;
3
4
5
6
int SchrittNach(int x, int y)
7
{
8
  char Labyrinth[10][11];
9
10
  if(Labyrinth[x][y] == 'K')
11
  {
12
    return 1;
13
  }
14
  else if(Labyrinth[x][y] == '#')
15
  {
16
    return 0;
17
  }
18
  else if(Labyrinth[x][y] == '.')
19
  {
20
    return 0;
21
  }
22
  else if(SchrittNach(x-1, y))
23
  {
24
    return 1;
25
  }
26
  else if(SchrittNach(x+1, y))
27
  {
28
    return 1;
29
  }
30
  else if(SchrittNach(x, y-1))
31
  {
32
    return 1;
33
  }
34
  else if(SchrittNach(x, y+1))
35
  {
36
    return 1;
37
  }
38
  else if(Labyrinth[x][y] == ' ')
39
  {
40
    return 0;
41
  }
42
  else
43
  {
44
    return 0;
45
  }
46
}
47
48
49
50
int main()
51
{
52
  int x, y;
53
  char Labyrinth[10][11] = {
54
       "##########",
55
       "# #  #K  #",
56
       "# # #### #",
57
       "#   #    #",
58
       "# # # ####",
59
       "### # ## #",
60
       "#   #    #",
61
       "## ##### #",
62
       "#        #",
63
       "##########"
64
  };
65
66
67
  x = 1;
68
  y = 1;
69
70
71
  SchrittNach(x, y);
72
73
74
system("pause");
75
return 0;
76
}

Wenn ich es durchkompiliere, bricht das "Programm" nach kurzer Zeit 
nachwie vor ab. Wenn es nach einem Schritt fertige ist, dann soll es ja 
nun noch mehr Schritt ausführen, oder?

von Klaus W. (mfgkw)


Lesenswert?

Klaus Wachtler schrieb:
> else
>   {
>     return 0; // sollte nie auftreten!
>   }

So etwas (eine Annahme, von der man sicher ist, daß sie immer
und ewig stimmt und natürlich NIE verletzt wird) sollte man
schlauerweise generell absichern:

[/c]
#include <assert.h>

...
 else
   {
     assert( !"hier komme ich nie an" );
     return 0; // sollte nie auftreten!
   }
[/c]

In der Debugversion wird das Programm mit einer passenden
Fehlermeldung abgebrochen wenn die BEdingung zur Laufzeit dann
doch nicht erfüllt ist, in der Releaseversion macht der
Kompiler nichts aus dem assert, es kostet also keinen Code
und keine Rechenzeit - und gibt auch keine Fehlermeldung.

von Klaus W. (mfgkw)


Lesenswert?

Tobias Mehrl schrieb:
> Wenn es nach einem Schritt fertige ist, dann soll es ja
> nun noch mehr Schritt ausführen, oder?

Mag sein, daß du dir das so vorstellst.
Dann musst du dem Compiler das auch sagen. Ich sehe nichts davon.
Der Compiler vermutlich auch nicht.

von Klaus W. (mfgkw)


Lesenswert?

Wenn du übrigens im Hauptprogramm ein Feld hast (oder irgendeine
Variable) und in einer Funktion nochmal, dann sind das zwei
verschiedene, auch wenn sie gleich heißen.
Willst du das bei Labyrinth[][]?

von Tobias Mehrl (Gast)


Lesenswert?

Mal eine andere Frage. Ist der Code soweit korrekt und der Fehler den 
ich bisher beschrieben habe kommt von den fehlenden Schritten? D.h. ich 
müsste jetzt irgendwie von Hand die Funktion SchrittNach per Hand wie 
z.B. durch Eingabe einer Taste immer wieder aufrufen. Könnte das so 
funzen?

"Willst du das bei Labyrinth[][]?"

Nein, eigentlich soll er immer wieder das gleiche Labyrinth hernehmen...

von Klaus W. (mfgkw)


Lesenswert?

Dann weisst du ja, was du im C-Buch deiner Wahl jetzt suchen musst.

von Klaus W. (mfgkw)


Lesenswert?

Tobias Mehrl schrieb:
> Mal eine andere Frage. Ist der Code soweit korrekt und der Fehler den

korrekt ist er noch lange nicht, alleine weil er nichts tut.

> ich bisher beschrieben habe kommt von den fehlenden Schritten? D.h. ich
> müsste jetzt irgendwie von Hand die Funktion SchrittNach per Hand wie
> z.B. durch Eingabe einer Taste immer wieder aufrufen. Könnte das so
> funzen?

Könnte, aber willst du so ein Programm?

von Tobias Mehrl (Gast)


Lesenswert?

Ja, ich würde gerne die Maus durch Tastendruck da hinführen. Ist das 
jetzt schlecht wenn es das Programm nicht von alleine tut? Wie kann man 
Funktionen wiederholt per Hand aufrufen?

von Klaus W. (mfgkw)


Lesenswert?

Tobias Mehrl schrieb:
> Ist das
> jetzt schlecht wenn es das Programm nicht von alleine tut?

Wenn du das so willst, ist es in Ordnung.
Ich habe damit keine Probleme, nur ging ich davon aus, daß das
Programm eine Lösung suchen soll. Dann müsste dein Programm
halt die Bewegung vorgeben.

von Tobias Mehrl (Gast)


Lesenswert?

Naja, ich hab die Aufgabenstellung so verstanden, dass nach jedem 
Tastendruck, z.B. die Enter-Taste, die Maus wieder ein Stück näher in 
Richtung des Käses rückt. Nur weiß ich eben nicht wie ich auf 
Tastendruck der Enter-Taste die Funktion SchrittNach in der main wieder 
aufrufen kann und man jeden einzelnen Schritt der Maus auch als Ausgabe 
sieht (aber das ist ja noch was anderes)...

von Klaus W. (mfgkw)


Lesenswert?

Tobias Mehrl schrieb:
> Wie kann man
> Funktionen wiederholt per Hand aufrufen?

In einer Schleife Zeichen lesen und dann die Funktion aufrufen?

Die Standardeingabe (getchar(), scanf(), getc(), gets()) ist
in C-Programmen üblicherweise gepuffert, sodaß man
lange tippen kann, bevor sich etwas tut - erst mit einem Return
oder Enter wird die ganze Zeile hingeworfen und dann abgearbeitet.
Das will man bei der Art von Programmen meist nicht.

Wenn du mit VC++ arbeitest, sollte es da eine Funktion getch()
oder _getch() geben, die nicht bis zum nächsten Enter hängt,
sondern jeden Tastendruck sofort liefert.
Mit kbhit() oder_kbhit() kann man abfragen, ob ein Tastendruck
stattgefunden hatte und man etwas abholen kann.
Für Linux hätte ich einen Ersatz für diese beiden Funktionen.

von Karl H. (kbuchegg)


Lesenswert?

Tobias Mehrl schrieb:
> Naja, ich hab die Aufgabenstellung so verstanden, dass nach jedem
> Tastendruck, z.B. die Enter-Taste, die Maus wieder ein Stück näher in
> Richtung des Käses rückt.

Das war gar nicht gefragt :-)
Gefragt war der Weg, den die Maus nehmen muss.
Dass der auf Tastendruck entstehen soll, ist in der Aufgabenstellung mit 
keinem Wort erwähnt.

von abc (Gast)


Lesenswert?

>Wenn du mit VC++ arbeitest, sollte es da eine Funktion getch()
GCC bzw. MinGw kennt die auch, conio.h.

von Tobias Mehrl (Gast)


Lesenswert?

Nunja, können wir es trotzdem so machen?

Ich hab das jetzt mal so versucht zu lösen. Jedes mal wenn man die 
Leertaste drückt, dann wird die Funktion SchrittNach aufgerufen. Seht 
selbst:
1
#include<iostream>
2
#include<stdlib.h>
3
using namespace std;
4
5
6
7
int SchrittNach(int x, int y)
8
{
9
  char Labyrinth[10][11];
10
11
  if(Labyrinth[x][y] == 'K')
12
  {
13
    return 1;
14
  }
15
  else if(Labyrinth[x][y] == '#')
16
  {
17
    return 0;
18
  }
19
  else if(Labyrinth[x][y] == '.')
20
  {
21
    return 0;
22
  }
23
  else if(SchrittNach(x-1, y))
24
  {
25
    return 1;
26
  }
27
  else if(SchrittNach(x+1, y))
28
  {
29
    return 1;
30
  }
31
  else if(SchrittNach(x, y-1))
32
  {
33
    return 1;
34
  }
35
  else if(SchrittNach(x, y+1))
36
  {
37
    return 1;
38
  }
39
  else if(Labyrinth[x][y] == ' ')
40
  {
41
    return 0;
42
  }
43
  else
44
  {
45
    return 0;
46
  }
47
}
48
49
50
51
int main()
52
{
53
  int x, y;
54
  char Labyrinth[10][11] = {
55
       "##########",
56
       "# #  #K  #",
57
       "# # #### #",
58
       "#   #    #",
59
       "# # # ####",
60
       "### # ## #",
61
       "#   #    #",
62
       "## ##### #",
63
       "#        #",
64
       "##########"
65
  };
66
67
68
  x = 1;
69
  y = 1;
70
71
  if(getchar() == ' ')
72
  {
73
  SchrittNach(x, y);
74
  }
75
  else
76
  {
77
  }
78
79
80
system("pause");
81
return 0;
82
}

Edit: Hab grad selbst bemerkt, dass das schon wieder völliger Blödsinn 
ist...

von Karl H. (kbuchegg)


Lesenswert?

Tobias Mehrl schrieb:
> Mal eine andere Frage. Ist der Code soweit korrekt

Nein.
Das ist so falsch, wie es nur sein kann.

Und das fängt schon mal damit an, das labyrinth keine lokale Variable 
der Funktion sein kann

Vergeude nicht deine Zeit. Stürz dich lieber auf sinnvollere Sachen, wie 
Arrays oder Strukturen oder Argument Passing. Die werden die nächsten 10 
Jahre dein Arbeitspferd sein, daher solltest du sie im Schlaf 
beherrschen. Und davon sehe ich in deinem Code noch nicht einmal 
Ansätze, dass du wenigstens in der Nähe von Können kommst. Von 
beherrschen red ich erst mal nicht.

von Karl H. (kbuchegg)


Lesenswert?

So könnte sowas aussehen.
Compiliert mit VC++ (wegen der conio.h wichtig)

Die Maus macht mit jedem Tastendruck einen Schritt. Wird der Käse 
gefunden, kommt hinten nach eine Liste der Schritte raus (allerdings in 
umgekehrter Reihenfolge: vom Käse zur Startposition)

1
#include <stdio.h>
2
#include <conio.h>
3
4
#ifndef TRUE
5
#define TRUE 1
6
#define FALSE 0
7
#endif
8
9
char Labyrinth[10][11] = {
10
     "##########",
11
     "# #  #K  #",
12
     "# # #### #",
13
     "#   #    #",
14
     "# # # ####",
15
     "### # ## #",
16
     "#   #    #",
17
     "## ##### #",
18
     "#        #",
19
     "##########"
20
};
21
22
void Output()
23
{
24
  int x, y;
25
  
26
  for( y = 0; y < 10; ++y ) {
27
    for( x = 0; x < 10; ++x ) {
28
      printf( "%c", Labyrinth[x][y] );
29
    }
30
    printf( "\n" );
31
  }
32
  
33
  printf( "\n" );
34
  
35
  {
36
    char c = _getch();
37
  }
38
}
39
40
int SchrittNach(int x, int y)
41
{
42
  Output();
43
  printf( "Trying %d %d\n", x, y );
44
  
45
  // Hurra! Da liegt er ja
46
  //
47
  if( Labyrinth[x][y] == 'K' )
48
    return TRUE;
49
50
  // Käse nicht da, aber laufen wir in eine Mauer? Wenn ja
51
  // dann kann dieser Schritt nicht Teil der Lösung sein
52
  if( Labyrinth[x][y] == '#' )
53
    return FALSE;
54
55
  // OK. Die Maus steht im freien Feld und der Käse ist auch nicht da
56
  // Das bedeutet: dieser SChritt könnte Teil der Lösung sein, wenn
57
  // es in einer Richtung zum Käse geht
58
  //
59
  // allerdings hinterlässt sich die Maus eine Markierung, dass sie schon
60
  // mal hier war, damit sie dasselbe Feld nicht noch einmal besucht. Wenn
61
  // sie schon mal hier war, dann gehts hier auch nicht zum Käse
62
  if( Labyrinth[x][y] == '.' )
63
    return FALSE;
64
65
  Labyrinth[x][y] = '.';
66
67
  if( SchrittNach( x+1, y ) ||
68
      SchrittNach( x-1, y ) ||
69
      SchrittNach( x, y-1 ) ||
70
      SchrittNach( x, y+1 ) ) {
71
    printf( "-> %d %d\n", x, y );
72
    return TRUE;
73
  }
74
75
  // in keiner der 4 Richtungen gehts zum Käse
76
  // Schade. Damit steht auch fest, dass dieses Feld nicht auf
77
  // dem Lösungsweg liegen kann. Mach die Markierung wieder weg
78
79
  Labyrinth[x][y] = ' ';
80
81
  return FALSE;
82
}
83
84
int main()
85
{
86
  if( SchrittNach(1, 1) )
87
    printf( "Kaese gefunden!\n" );
88
  else
89
    printf( "Kaese nicht gefunden :-( \n" );
90
91
system("pause");
92
return 0;
93
}

Probehalber auch mal den Käse unerreichbar einbauen oder überhaupt 
rausnehmen.

von M. B. (manubaum) Benutzerseite


Lesenswert?

Hey, lass dich nicht entmutigen. Du wirst das schon noch einsehen! 
Jedoch als Input möchte ich noch anfügen, das es in anderen Foren viele 
diskussionen gibt, die darum gehen, ob das in der Praxis jemals relevant 
ist (habs einmal gebraucht).

Folgendes ist auch noch zu erwähnen:
Die Rekursion ist ein schönes akademisches Anschauungsbeispiel (ich mag 
den Grundgedanken), jedoch ist ein For-Loop viel schneller und sicherer.

von Klaus W. (mfgkw)


Lesenswert?

M. B. schrieb:
> jedoch ist ein For-Loop viel schneller und sicherer.

Dann gib mal einen Binärbaum ohne Rekursion mit einer for-Schleife
aus.

Es gibt halt Probleme, wo nur Rekursion sinnvoll ist, es gibt
welche wo es keinen Sinn macht, und einen weiten Bereich
dazwischen, wo man sich davor mehr oder weniger gut drücken kann.

Generell etwas anderes als Rekursion zu empfehlen, nur weil man
sie nicht mag ist aber schon etwas verwegen :-)

von abc (Gast)


Lesenswert?

@Karl heinz Buchegger
Compiliert auch mit MinGW. Wegen system() sollte man noch stdlib.h 
includen und char c = _getch(); gibt ne Warnung von wegen unused 
variable, aber das brauch ich dir ja nicht zu erzählen. :-)

Davon mal abgesehen: Irgendwie stehen Maus und Rest der Welt auf dem 
Kopf...

Code:
1
##########
2
# #  #K  #
3
# # #### #
4
#   #    #
5
# # # ####
6
### # ## #
7
#   #    #
8
## ##### #
9
#        #
10
##########

Ausgabe:
1
##########
2
#    # # #
3
### ##   #
4
#      # #
5
# ###### #
6
###    # #
7
#K# ## # #
8
# # ## # #
9
#   #    #
10
##########

Jetzt stelle ich auch mal eine Frage die wohl in jedem C-Buch erklärt 
ist: Macht das einen Unterschied in den for-Schleifen ++x anstelle von 
x++ zu nutzen?

@M. B.
Zeig doch mal eine Lösung für die Maus welche ohne Rekursion auskommt. 
Der Vorteil im konkreten Fall ist ja das der Weg der Maus durch die 
ineinandergeschachtelten Funktionsaufrufe "gespeichert" wird, wenn ich 
an Schleifen denke lande ich da schnell bei realloc(). Ist das schöner?

von Karl H. (kbuchegg)


Lesenswert?

abc schrieb:

> Davon mal abgesehen: Irgendwie stehen Maus und Rest der Welt auf dem
> Kopf...

:-)
Fast.
Ist um 90° gedreht

Das 'Problem'

wie ist in

  char Labyrinth[7][7] =
     { ....

in der Initialisierung X und Y angeordnet?

Und wenn man darüber nachdenkt, kommt man drauf, dass es so sein muss

     ---->   Y

  |   "##########",
  |   "# #  #K  #",
  |   "# # #### #",
  v   "#   #    #",
      "# # # ####",
  X   "### # ## #",
      "#   #    #",
      "## ##### #",
      "#        #",
      "##########"

( der letzte Index läuft am schnellsten )


> Jetzt stelle ich auch mal eine Frage die wohl in jedem C-Buch erklärt
> ist: Macht das einen Unterschied in den for-Schleifen ++x anstelle von
> x++ zu nutzen?

Nein.
In C macht es überhaupt keinen Unterschied.
C++ Leute gewöhnen sich aber meistens um, denn in C++ kann es einen 
Unterschied machen, wenn x kein einfacher Datentyp sondern eine eigene 
Klasse mit überladenen ++ Operatoren ist.

> @M. B.
> Zeig doch mal eine Lösung für die Maus welche ohne Rekursion auskommt.
> Der Vorteil im konkreten Fall ist ja das der Weg der Maus durch die
> ineinandergeschachtelten Funktionsaufrufe "gespeichert" wird, wenn ich
> an Schleifen denke lande ich da schnell bei realloc(). Ist das schöner?

Das geile ist, dass die oft verteufelte Rekursion gerne schon mal mit 
einem selbst geführten Stack ersetzt wird. Das das dem Prinzip nach 
immer noch eine Rekursion ist , .... :-)

Aber es stimmt schon. Viele brauchen tatsächlich Rekursionen nicht. In 
dem Moment wo allerdings härtere Datenstrukturen als Arrays ins Spiel 
kommen, sind aber Rekursionen oft die natürlichste Lösung. Grob gesagt: 
Alles was mit ineinander geschachtelten for-Schleifen gelöst wird, wobei 
die ANzahl der Ineinanderschachtelungen von vorne herein nicht 
feststeht, ist ein dankbarer Kandidat für Rekursion.

von abc (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:
> Nein.
> In C macht es überhaupt keinen Unterschied.
Danke, das wollte ich wissen.

Die gedrehte Welt lass ich jetzt mal in Ruhe, dafür bin ich schon zu 
müde...

von Karl H. (kbuchegg)


Lesenswert?

abc schrieb:
> Karl heinz Buchegger schrieb:
>> Nein.
>> In C macht es überhaupt keinen Unterschied.
> Danke, das wollte ich wissen.
>
> Die gedrehte Welt lass ich jetzt mal in Ruhe, dafür bin ich schon zu
> müde...

Lässt sich in der Ausgabefunktion leicht korrigieren. Spielt aber im 
Grunde keine große Rolle.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.