Ein Beispielprogramm zu Schleifen

von: Ansgar Schiffler, 2005

zurück zu 'Die Programmiersprache C++'

Die Lösung

Verständnisfragen mit Erklärungen

Die Aufgabenstellung

 

In einem Tal werden pro Jahr eine bestimmte Anzahl Füchse zum Abschuss freigegeben. In der sich anschließenden Schonzeit vermehren sich die Füchse. Ihre Aufgabe besteht darin, zu prüfen, wie sich der Fuchsbestand voraussichtlich über die Jahre entwickeln wird. Hierzu gibt der Benutzer die folgenden Daten ein.

 

- der aktuelle Fuchsbestand (der Wert soll zwischen 2 und 1000  liegen )

- die Anzahl der zum Abschuss freigegebenen Füchse (der Wert soll zwischen 0 und 100

   liegen )

- die Vermehrungsrate der Füchse in der Schonzeit in Prozent. (der Wert sollte zwischen -10

   und +20 liegen, es sind auch Kommazahlen zulässig ).

 

In einer Schleife ist für jedes Jahr der prognostizierte Fuchsbestand auszugeben. Die Schleife ist zu beenden, wenn entweder 20 Jahre vergangen sind, sich der anfängliche Fuchsbestand verdoppelt hat oder es keine Füchse mehr gibt.

Falls sich innerhalb der 20 Jahre der Fuchsbestand verdoppelt hat oder es keine Füchse mehr geben sollte, ist eine entsprechende Meldung auszugeben mit Angabe der Anzahl der Jahre.

 

Bespiel für die Ausgabe auf dem Bildschirm:

 

Mit diesem Programm laesst sich der Fuchsbestand vorhersagen.

Bitte geben Sie die derzeitige Anzahl der Fuechse ein: 400

Wie viele Fuechse sollen pro Jahr abgeschossen werden? 7

Wie hoch ist die jaehrliche Vermehrungsrate der Fuechse in Prozent? 18

 

Im 1. Jahr gibt es 400 Fuechse.

Im 2. Jahr gibt es 464 Fuechse.

Im 3. Jahr gibt es 539 Fuechse.

Im 4. Jahr gibt es 628 Fuechse.

Im 5. Jahr gibt es 732 Fuechse.

Im 6. Jahr gibt es 856 Fuechse.

Die Anzahl der Fuechse hat sich innerhalb von 5 Jahren verdoppelt.

Moechten Sie eine erneute Berechnung duerchfuehren lassen? ( j/n)

 

 

Die Lösung      Verständnisfragen mit Erklärungen

     

#include <iostream.h>
#include <stdio.h>
#include <conio.h>

int main() // written by A. Schiffler, Name: fuechse.cpp
{
//Deklaration der Konstanten
const int min_fuechse = 2, max_fuechse = 1000;
const int min_abschuss = 0, max_abschuss = 100;
const int min_vermehrungsrate = -10, max_vermehrungsrate = 20;
const int max_jahre = 20;

//Deklaration der Variablen
int wahl, jahr, abschuss;
char wiederholung;
float max_bestand, fuechse, zuwachsrate;

do
  {
  jahr = 1;
  cout <<"\nMit diesem Programm kann der Fuchsbestand vorhergesagt werden\n";

  //Einlesen des anfänglichen Fuchsbestandes
  do
    {
    cout<<"\nGeben Sie bitte den derzeitigen Fuchsbestand an: ";
    cin >> fuechse;
    if ( fuechse > max_fuechse || fuechse < min_fuechse)
       {
      cout<<"\n Der Wert muss zwischen "<<min_fuechse<<" und "<<max_fuechse<<" liegen!\n";
       }
    }
  while (fuechse > max_fuechse || fuechse < min_fuechse);

  //Einlesen der Abschussrate
  do
    {
    cout<<"\nGeben Sie bitte die jaehrliche Abschussrate ein: ";
    cin >> abschuss;
    if ( abschuss < min_abschuss || abschuss > max_abschuss )
       {
       cout<<"\n Der Wert muss zwischen "<<min_abschuss<<" und "<<max_abschuss<<" liegen!\n";
       }
    }
  while ( abschuss < min_abschuss || abschuss > max_abschuss );

  //Einlesen der Vermehrungsrate
  do
    {
    cout<<"\nGeben Sie bitte die jaehrliche Vermehrungsrate in Prozent ein: ";
    cin >> zuwachsrate;
    if ( zuwachsrate < min_vermehrungsrate || zuwachsrate > max_vermehrungsrate )
      {
      cout<<"\n Der Wert muss zwischen "<<min_vermehrungsrate<<" und "<<max_vermehrungsrate<<" liegen!\n";
      }
    }
  while ( zuwachsrate < min_vermehrungsrate || zuwachsrate > max_vermehrungsrate );

  max_bestand = 2*fuechse;

  cout<<"\nWaehlen Sie die Darstellungsart: Tabelle (1) oder Text (andere Eingabe): ";
  cin>>wahl;

  if (wahl != 1) //Darstellungsart Text
    {
    printf("\nFuchsbestand am Anfang des 1. Jahres: %4.0f\n", fuechse);

     do
        {
        fuechse = fuechse - abschuss;
        fuechse = fuechse * ( 1 + zuwachsrate / 100 );
        jahr++;
        if ( fuechse < 0 ) fuechse = 0;
        printf("Fuchsbestand am Anfang des %i. Jahres: %4.0f\n", jahr, fuechse);
        }
     while ( fuechse < max_bestand && fuechse > 0 && jahr <= max_jahre );
    }//if (wahl != 1) Darstellungsart Text

  else //Darstellungsart Tabelle
    {
    printf("\n\t\tJahr\t\tAnzahl Fuechse\n"); //Tabellenüberschrift ausgeben
    printf("\t\t%2.0i\t\t %4.0f\n", jahr, fuechse);
    do
        {
        fuechse -= abschuss;
        fuechse *= ( 1 + zuwachsrate / 100 );
        jahr++;
        if ( fuechse < 0 ) fuechse = 0;
        printf("\t\t%2.0i\t\t %4.0f\n", jahr, fuechse);
        }
    while ( fuechse < max_bestand && fuechse > 0 && jahr <= max_jahre );
    }//else Darstellungsart Tabelle

  //Es wird festgestellt, weshalb die Schleife verlassen wurde

  //wurde Scheife verlassen, weil sich die Anzahl der Fuechse verdoppelt hat?
  if ( fuechse >= max_bestand )
     {
     cout <<"\nDer Fuchsbestand hat sich innerhalb von "<< (jahr - 1 );
     cout <<" Jahren verdoppelt\n";
     }

  //wurde Scheife verlassen, weil es keine Fuechse mehr gibt?
  if ( fuechse <= 0 )
      {
      cout <<"\nNach "<< ( jahr - 1 ) <<" Jahren gibt es keine Fuechse mehr";
      }

  cout<<"\nMoechten Sie eine neue Berechnung durchfuehren lassen? (j/n) ";
  cin>> wiederholung;
  }
while(wiederholung == 'j');

getch();
return 0;
} // end of main

Hier können Sie sich den Quellcode herunterladen: Quellcode
          
zurück zum Seitenanfang                       zurück zu 'Die Programmiersprache C++'

Verständnisfragen mit Erklärungen

       

1. Beachten Sie bitte die Abfrage, mit der festgestellt werden soll, ob sich die Anzahl der Füchse verdoppelt hat. Ist diese Abfrage korrekt formuliert?

    do
        {
        fuechse -= abschuss;
        fuechse *= ( 1 + zuwachsrate / 100 );
        jahr++;
        if ( fuechse < 0 ) fuechse = 0;
        printf("\t\t%2.0i\t\t %4.0f\n", jahr, fuechse);
        }
    while (
fuechse < 2*fuechse && fuechse > 0 && jahr <= max_jahre );

Zunächst könnten man denken: Na ja, mit 2*fuechse erhalte ich ja wohl die doppelte Anzahl und ich frage hier ab, ob die Anzahl der Füchse kleiner ist als die doppelte Anzahl. Aber Achtung: Der in 'fuechse' gespeicherte Wert gibt ja die derzeitige Anzahl der Füchse an. Wenn diese sich vermehren, vergrößert sich diese Zahl und auch das Doppelte dieser Zahl vergrößert sich dann. Sind die Füchse also von 100 auf 150 angewachsen, so ist '2*fuechse' von 200 auf 300 angewachsen. Jede positive Zahl ist kleiner als das Doppelte dieser Zahl. Diese Abfrage ist also unsinnig. Sie müssen das Doppelte der Anfangszahl der Füchse in einer Variablen speichern und dieser Wert darf sich nicht mehr ändern. Die Lösung lautet also:

 max_bestand = 2*fuechse;

  do
        {
        fuechse -= abschuss;
        fuechse *= ( 1 + zuwachsrate / 100 );
        jahr++;
        if ( fuechse < 0 ) fuechse = 0;
        printf("\t\t%2.0i\t\t %4.0f\n", jahr, fuechse);
        }
    while (
fuechse < max_bestand && fuechse > 0 && jahr <= max_jahre );

 

2. In dem folgenden Beispiel wollte es der Programmierer besonders gut machen, indem er für die Füchse gleich drei Variablen eingeführt hat: In 'fuchsbestand' wird die Anzahl der Füchse gespeichert, bevor der erste Fuchs abgeschossen wurde. In 'fuechse_1' ist die Anzahl der Füchse gespeichert, die nach der Jagdsaison noch übrig geblieben sind. In 'fuechse_2' steht die Anzahl der Füchse, nachdem sie sich in der anschließenden Schonzeit vermehrt haben. Das ist doch alles sehr ordentlich. 

    do
        {
        fuechse_1 = fuchsbestand - abschuss;
        fuechse_2 =  fuechse_1 * ( 1 + zuwachsrate / 100 );
        jahr++;
        if ( fuechse_2 < 0 ) fuechse_2 = 0;
        printf("\t\t%2.0i\t\t %4.0f\n", jahr, fuechse_2);
        }
    while ( fuechse_2 < max_bestand && fuechse_2 > 0 && jahr <= max_jahre );

Aber wie Sie es schon ahnen, gibt es hier einen Haken. Wird denn an irgendeiner Stelle innerhalb der Schleife der Wert von 'fuchsbestand' verändert? Nein, die Variable 'fuchsbestand' steht immer rechts vom Zuweisungsoperator und es werden auf diese Variable überhaupt keine Operationen angewendet. Der Wert dieser Variablen wird immer nur ausgelesen und nie verändert. Also haben Sie in 'fuechse_2' immer die Anzahl der Füchse gespeichert, die nach einem Jahr in dem Tal leben. Auch wenn Sie diese Schleife hundert mal durchlaufen lassen, Sie haben am Ende jeden Jahres immer die gleiche Anzahl Füchse, unabhängig von der Abschusszahl und der Vermehrungsrate.

Eine Lösung bestünde darin, vor Beginn der Schleife in 'fuechse_2' die Anfangsanzahl der Füchse zu speichern und dann in der ersten Zeile der Schleife die Anweisung 'fuchsbestand = fuechse_2' zu ergänzen. Aber das wäre kein guter Programmierstil, insbesondere ist nicht unmittelbar erkenntlich, wofür 'fuechse_1' und 'fuechse_2' stehen. Es müsste mindestens in einem Kommentar erklärt werden. Eleganter wäre es, Sie verwendeten nur eine einzige Variable.

3. Bitte sehen Sie sich die Wiederholbedingung genauer an: 

max_bestand = 2*fuechse;

  do
        {
        fuechse -= abschuss;
        fuechse *= ( 1 + zuwachsrate / 100 );
        jahr++;
        if ( fuechse < 0 ) fuechse = 0;
        printf("\t\t%2.0i\t\t %4.0f\n", jahr, fuechse);
        }
    while (
fuechse >= max_bestand || fuechse <= 0 || jahr > max_jahre );

Die Erklärung des Programmierers lautet folgendermaßen: Die Schleife soll doch verlassen werden, wenn entweder sich die Anzahl der Füchse verdoppelt hat oder die Füchse ausgestorben sind oder die maximale Anzahl der Jahre überschritten ist. Dies ist vollkommen richtig. Aber, mit ein wenig Kenntnis der englischen Sprache lässt sich der Fehler verdeutlichen: do .... while   bedeutet: 'tue/mache .... solange (diese Bedingung erfüllt ist). Es handelt sich also um eine Wiederholbedingung. Hingegen bedeutet   repeat .... until   'tue/mache .... solange bis (diese Bedingung erfüllt ist). Es handelt sich also um eine Abbruchbedingung. Solche Schleifen gibt es weder in C++ noch in Java. Wenn die Bedingung erfüllt ist, bedeutet dies immer: mach weiter!!  und nie: hör auf!!. 

Also sagt sich der Programmierer: o.k., dann ändere ich also die 'ODER' in 'UND' und er präsentiert dann die folgende Lösung: 

while ( fuechse >= max_bestand && fuechse <= 0 && jahr > max_jahre );

Dumm gelaufen. Wie ist es denn möglich, dass sich die Anzahl der Füchse verdoppelt hat und außerdem die Füchse ausgestorben sind. Es können nie beide Bedingungen erfüllt sein. Ist aber nur eine einzige Bedingung in einer UND-Verknüpfung nicht erfüllt, so ergibt die Gesamtaussage 'false'. Dies ist also ein schönes Beispiel für eine Endlosschleife.

Sehen wir uns nun die Lösung an: 

while ( fuechse < max_bestand && fuechse > 0 && jahr <= max_jahre );

Dies bedeutet: mache weiter, wenn alle drei folgenden Bedingungen erfüllt sind: die Anzahl der Füchse hat sich nicht verdoppelt, die Füchse sind nicht ausgestorben und die maximale Anzahl der Jahre ist noch nicht erreicht. Wenn nur eine einzige dieser drei Bedingungen nicht erfüllt ist (weil z.B. die Füchse ausgestorben sind oder sich die Anzahl der Füchse verdoppelt hat oder ..), dann ist das Gesamtergebnis der Abfrage 'false' und die Schleife wird nicht wiederholt, sondern sie wird verlassen.

Noch eine Anmerkung zu gutem Programmierstil. Sehen Sie sich bitte die folgende Wiederholbedingung an: 

while ( fuechse < max_bestand && fuechse > 0 && jahr != max_jahre );

Sobald die maximale Anzahl der Jahre erreicht ist, liefert der Vergleich 'jahr != max_jahre' als Ergebnis 'false' und die Schleife wird verlassen. Das Programm funktioniert tadellos. Aber könnte trotzdem jemand mit dieser Lösung unzufrieden sein? Natürlich. Ein Programm, das funktioniert, ist deswegen noch lange kein gutes Programm. Das Funktionieren ist die notwendige Bedingung, damit ein Programm gut ist, aber das allein reicht noch lange nicht aus. Zwei ganz wichtige weitere Kriterien sind die Lesbarkeit und die Änderungsfreundlichkeit, wobei die Änderungsfreundlichkeit eine gute Lesbarkeit voraussetzt. Nach dieser langen Vorrede, hier die Erklärung:

Jemand ändert ihr Programm so ab, dass nur noch jedes vierte Jahr ausgegeben wird. Dummerweise wird der Wert 'max_jahre' nun unter Umständen nicht genau getroffen. Nehmen wir an 'max_jahre' hat den Wert 20. Hat 'jahr' den Wert 18, ist das ungleich 20. Nach der Erhöhung um 4, ist in 'jahr' der Wert 22 gespeichert. Dieser Wert ist glatt an der 20 'vorbeigelaufen' und ihr Programm das zuvor tadellos funktionierte, produziert durch einen kleine Änderung nun eine Endlosschleife.

zurück zum Seitenanfang                  zurück zu 'Die Programmiersprache C++'