9.  Цикли

 
"Цикъл - минималното време необходимо на процесора,
за да изпълни една инструкция."
ЕИМ Свят - Списанието на Юнаците... :-)
 
Прости цикли - оператор while.

** Синтаксис:

while (<условие>) <оператор>;

** Удвояване на влог (задачата от лекция 1. Увод).
    Внасяме 10000 лв. в банкова сметка с 6% год. лихва при месечно олихвяване. След колко години сумата в сметката ще се удвои?
// doublinv.cpp
#include <iostream>
using namespace std;

int main()
{  double rate = 6;
   double balance = 10000;
   int month = 0;

   while (balance < 20000)
   {  month++;
      balance = balance * (1 + rate/12/100);
   }
   cout << "The investment doubled after "
        << month / 12.0 << " years.\n";
   return 0;
}

The investment doubled after 11.5833 years.

** Факториел.
    Да се пресметне стойността на функцията факториел без рекурсия.
// whilefac.cpp
#include <iostream>
using namespace std;

long factorial(int n)
{  int factor = 1;
   long product = 1;
   while (factor <= n)
   {  product = product * factor;
      factor++;
   }
   return product;
}
int main()
{  cout << "Please enter a number: ";
   int n;
   cin >> n;
   cout << n << "! = " << factorial(n) << "\n";
   return 0;
}

** Максимална стойност.
    Програмата чете редица от стойности (числа) и извежда най-голямата стойност.
// maxtemp.cpp
#include <iostream>
using namespace std;

int main()
{
/* съхранява текущото число */
   double next;
/* съхранява най-голямото въведено число до момента */
   double highest;
   cout << "Please enter the temperature values:\n";
   if (cin >> next)
/* инициализира highest с първото въведено число */
      highest = next;
   else
   {  cout << "No data!\n";
      return 1;
   }
   while (cin >> next)
      if (next > highest) highest = next;
   cout << "The highest temperature is " << highest << "\n";
   return 0;
}

Please enter the temperature values:
12
22
-3
a
The highest temperature is 22

Цикъл for.

** Синтаксис:

for (<инициализация>; <условие>; <корекция>) <оператор>;

Редът на изпълнение на четирите части на оператора е:
<инициализация>
<условие> - ако не е изпълнено, край на цикъла
<оператор>
<корекция>
<условие> - ако не е изпълнено, край на цикъла
<оператор>
<корекция> и т.н.
** Най-често срещаната форма за използване на оператора for е:
      for (i=начало; i<=край; i++)
      {
       тяло на цикъла
      }
** Пресмятане стойността на функцията факториел - вместо цикъл while се използва цикъл for.
// forfac.cpp
#include <iostream>
using namespace std;

long factorial(int n)
{  long product = 1;
   int factor;
   for (factor = 1; factor <= n; factor++)
      product = product * factor;
   return product;
}
int main()
{  cout << "Please enter a number: ";
   int n;
   cin >> n;
   cout << n << "! = " << factorial(n) << "\n";
   return 0;
}



Цикъл do/while

** Синтаксис:

do <оператор>; while (<условие>);

** Алгоритъм на Херон за намиране на квадратен корен.
    Търси се квадратен корен от положителното число a. Строи се редицата:
 x0 = a,  xn+1 = (xn + a/xn)/2
която е сходяща с граница точно търсеното число. Организира се цикъл за пресмятане членовете на редицата и  когато разликата между два съседни члена на редицата стане достатъчно близо до 0, т.е. |xn+1-xn| < eps, пресмятанията се прекратяват.
// sqroot.cpp
#include <iostream>
#include <cmath>
using namespace std;

bool not_equal(double x, double y)
/* ЦЕЛ:      сравнява две числа с плаваща точка
   ПОЛУЧАВА: x,y - числа
   ВРЪЩА:    true ако x не е достатъчно близо до y
*/
{  const double EPSILON = 1E-14;
   double dmax = fabs(x);
   if (fabs(y) > dmax) dmax = fabs(y);
   return fabs(x - y) > dmax*EPSILON;
}
double squareroot(double a)
/* ЦЕЛ:      пресмята квадратен корен по формулата на Херон
   ПОЛУЧАВА: a - положително число
   ВРЪЩА:    квадратен корен от a
*/
{  if (a == 0) return 0;
   double xnew = a;
   double xold;
   do
   {  xold = xnew;
      xnew = (xold + a/xold)/2;
   }
   while (not_equal(xnew, xold));
   return xnew;
}
int main()
{  cout << "Please enter a number: ";
   double x;
   cin >> x;
   cout << "The square root is " << squareroot(x) << "\n";
   return 0;
}

Please enter a number: 2
The square root is 1.41421

Безкрайни цикли
    Примери:
    for (i=1; i>0; i++) cout << i;
    while (true) cout << "no end";
    Смислено използване на безкраен цикъл: в тялото на цикъла се намира изхода от цикъла, най-често оператор return, който прекратява изпълнението на функцията.
    double squareroot(double a)
    {
     if (a == 0) return 0;
     double xnew = a;
     while (true)
     {
      double xold = xnew;
      xnew = (xold + a/xold)/2;
      if (equal(xnew, xold)) return xnew;
     }
    }



Конструкции с цикъл.
** Четене на данни.
    Конструкцията while (cin >> x) чете данни от входа докато не се появи грешка във входния поток. Грешка във входния поток може да се появи когато има несъответствие между типа на променливата x и типа на въведената стойност или когато се въведе специален символ за край на входа (край на файла). За операционната система DOS този символ е Ctrl Z.
    Програмата намира броя на думите от входния поток.
// words.cpp
#include <iostream>
#include <string>
using namespace std;

int main()
/* Програмата намира броя на думите от входния поток */
{  int count = 0;
   string word;
   while (cin >> word)  count++;
   cout << count << " words.\n";
   return 0;
}

Here are 4 words
^Z
4 words.

** Пренасочване на входния и изходния потоци.
    C:\my\>myprog < a_cin.txt
    Вместо от клавиатурата, операционната система пренасочва входа от текстов файл, в случая това е файла a_cin.txt.По този начин програмата за намиране на броя на думите може намери броя на думите в текстов файл (едно по-смислено приложение на тази програма).
    C:\my\>myprog > a_cout.txt
    Вместо на екрана, операционната система пренасочва изхода на текстов файл, в случая това е файла a_cout.txt.
** Извеждане на таблици.
    Да се отпечати таблица, която показва съдържанието на банкова сметка с първоначален влог 10000 в края на десетата година. Лихвата варира от 5% до 10% и се изменя със стъпка 0.5%.
// baltable.cpp
#include <iostream>
#include <iomanip>
#include <cmath>
using namespace std;

double future_value(double initial_balance,  double p, int nyear)
/*
ЦЕЛ:       пресмята стойността на влог със сложна лихва
ПОЛУЧАВА:  initial_balance - начална стойност на влога
           p - годишен лихвен процент
           nyear - броя на годините
ЗАБЕЛЕЖКИ: лихвата се начислява всеки месец
*/
{  double b = initial_balance*pow(1 + p/12/100, 12*nyear);
   return b;
}
int main()
{ cout << "      Rate   Balance\n";
  double rate;
  double initial_amount = 10000;
  int nyear = 10;
  cout << fixed << setprecision(2);
  for (rate = 5; rate <= 10; rate = rate + 0.5)
  { double balance = future_value(initial_amount, rate, nyear);
    cout << setw(10) << rate << setw(10) << balance << "\n";
  }
   return 0;
}

 Rate   Balance
 5.00  16470.09
 5.50  17310.76
 6.00  18193.97
 6.50  19121.84
 7.00  20096.61
 7.50  21120.65
 8.00  22196.40
 8.50  23326.47
 9.00  24513.57
 9.50  25760.55
10.00  27070.41

    Същата информация може да се изобразим графично, като се начертае редица от точки - по една точка за всяка стойност.
// balgraph.cpp
#include "ccc_win.cpp"

double future_value(double initial_balance, double p, int nyear)
{  double b = initial_balance * pow(1 + p/12/100, 12*nyear);
   return b;
}
int main()
{  double rate;
   double initial_amount = 10000;
   int nyear = 10;
   cwin.coord(5, 3*initial_amount, 10, 0);
   for (rate = 5; rate <= 10; rate = rate + 0.5)
   { double balance = future_value(initial_amount, rate, nyear);
     cwin << Point(rate, balance);
   }
   return 0;
}



Итеративни алгоритми и сходимост на алгоритми.
    Ако един алгоритъм генерира безкрайна редица, то той се нарича итеративен алгоритъм. В повечето случаи редицата от точки е сходяща и границата й е решение на задачата.
    Следващата програма е нагледна илюстрация на сходимостта на алгоритъма за намиране на квадратен корен за положителни числа. За положителни стойности на a редицата е сходяща. За отрицателни a може да се види поведението на получената редица - тя не е сходяща.
// converge.cpp
#include "ccc_win.cpp"

int main()
{  double a = cwin.get_double("Please enter a number");
   if (a == 0) return 0;
   double xnew = a;
   double xold;
   int iteration = 0;
   const int MAX_ITERATIONS = 20;
   cwin.coord(0, a, MAX_ITERATIONS, -a);
   do
   {  xold = xnew;
      xnew = (xold + a / xold) / 2;
      cwin << Point(iteration, xnew);
      iteration++;
   } while (iteration < MAX_ITERATIONS);
   return 0;
}



Генериране на случайни събития и симулации.
    Иглата на Бюфон. Игла с дължина 1 инч (2.54 см) се пуска върху лист хартия, на който са начертани хоризонтални линии, намиращи се на разстояние 2 инча една от друга. Ако иглата падне върху такава линия, казваме, че имаме попадение. Хипотезата на Бюфон е, че отношението на броя на всички опити към броя на попаденията клони към числото p.
// buffon.cpp
#include <iostream>
#include <cstdlib>
#include <cmath>
#include <ctime>
using namespace std;

void rand_seed()
/* ЦЕЛ: Инициализира генератора за случайни числа
*/
{  int seed = static_cast<int>(time(0));
   srand(seed);
}
double rand_double(double a, double b)
/* ЦЕЛ:  намира случайно число в интервала [a, b]
   ПОЛУЧАВА: границите на интервала
   ВРЪЩА: случайно число x, a <= x и x <= b
*/
{  return a + (b - a)*rand()*(1.0/RAND_MAX);
}
double deg2rad(double alpha)
/* ЦЕЛ:  превръща градуси в радиани
   ПОЛУЧАВА: alpha - големина на ъгъл в градуси
   ВРЪЩА:  големината на ъгъла в радиани
*/
{  const double PI = 3.141592653589793;
   return alpha * PI / 180;
}
int main()
{  int NTRIES = 10000;
   int i;
   int hits = 0;
   rand_seed();
   for (i = 1; i <= NTRIES; i++)
   {  double ylow = rand_double(0, 2);
      double angle = rand_double(0, 180);
      double yhigh = ylow + sin(deg2rad(angle));
      if (yhigh >= 2) hits++;
   }
   cout << "Tries / Hits = " << NTRIES*(1.0/hits) << "\n";
   return 0;
}

Tries / Hits = 3.18471

Вложени цикли.
      Да се отпечати таблица, която показва натрупаната сума по влог от 10000 лв. за 5, 10, 15, и 20 години при годишна лихва от 5 до 10% (през 0.5%).
// table.cpp
#include <iostream>
#include <iomanip>
#include <cmath>
using namespace std;

double future_value(double init_balance, double p, int n)
{ return init_balance*pow(1 + p/12/100, 12*n);
}
int main()
{
 const double RATE_MIN = 5;
 const double RATE_MAX = 10;
 const double RATE_INCR = 0.5;
 const int YEAR_MIN = 5;
 const int YEAR_MAX = 20;
 const int YEAR_INCR = 5;
 

/* отпечатва заглавната част на таблицата */
 cout << " Rate  ";
 int year;
 for (year=YEAR_MIN; year<=YEAR_MAX; year=year+YEAR_INCR)
     cout << setw(2) << year << " years  ";
 cout << "\n";
 cout << fixed << setprecision(2);
 double rate;
 double init_bal = 10000;
 for (rate=RATE_MIN; rate<=RATE_MAX; rate=rate+RATE_INCR)
/* отпечатва ред от таблицата */
 { int year;
   cout << setw(5) << rate;
   for (year=YEAR_MIN; year<=YEAR_MAX; year=year+YEAR_INCR)
   { double balance = future_value(init_bal, rate, year);
     cout << setw(10) << balance;
   }
   cout << "\n";
 }
 return 0;
}

 Rate   5 years  10 years  15 years  20 years 
  5.0  12833.59  16470.09  21137.04  27126.40
  5.5  13157.04  17310.76  22775.84  29966.26
  6.0  13488.50  18193.97  24540.94  33102.04
  6.5  13828.17  19121.84  26442.01  36564.47
  7.0  14176.25  20096.61  28489.47  40387.39
  7.5  14532.94  21120.65  30694.52  44608.17
  8.0  14898.46  22196.40  33069.21  49268.03
  8.5  15273.01  23326.47  35626.53  54412.43
  9.0  15656.81  24513.57  38380.43  60091.52
  9.5  16050.09  25760.55  41345.93  66360.61
 10.0  16453.09  27070.41  44539.20  73280.74

Област на действие на променлива.
     Всяка променлива се разпознава само вътре в блока, в който е дефинирана. Променливата, дефинирана в оператор for се разпознава до края на блока на оператора for. В следващия пример са използвани съкращенията НОДП - начало на областта на действие на променливата и КОДП - край на областта на действие на променливата.
 
 

int main()
{
 cout << "Minimal interest rate in percent: ";
 double rmin;                               /* НОДП rmin */
 cin >> rmin;
 double init = 10000;                       /* НОДП init */
 for (int r=rmin; r<=rmin+5; r++)           /* НОДП r */
 { cout << setw(5) << r;
   for (int year=2; year<=10; year+=2)      /* НОДП year */
   { double bal = future_value(init,r,year);/* НОДП bal */
     cout << setw(10) << bal;
   }                                /* КОДП year и bal */
   cout << "\n";
 }                                  /* КОДП r */
 return 0;
}                                   /* КОДП rmin и init */