4.8. Instructiunea do-while

2019/02/13 in Programare in C

Instructiunea do-while realizeaza structura ciclica conditionata posterior. Ea poate fi realizata cu ajutorul celorlalte instructiuni definite pana in prezent. Cu toate acestea, prezenta ei in limbaj mareste flexibilitatea in programare.

Ea are formatul:

do
instructiune
while(expresie);

Instructiunea do-while se executa in felul urmator:

  1. se executa instructiune;
  2. se calculeaza expresie. Daca aceasta are valoarea diferita de zero, atunci se revine la punctul 1, pentru a executa din nou instructiune; altfel (expresia are valoarea zero) se trece in secventa, adica la instructiunea urmatoare instructiunii do-while.

Se observa ca in cazul acestei instructiuni intai se executa instructiune si apoi se testeaza conditia de repetare a executiei ei.

Instructiunea do-while este echivalenta cu secventa:

instructiune;
while(expresie)
instructiune;

Consideram si in acest caz ca instructiune reprezinta corpul ciclului do-while.

In cazul instructiunii do-while corpul ciclului se executa cel putin o data, spre deosebire de cazul instructiunilor while si for, cand este posibil sa nu se execute niciodata.

Exercitii:

4.20. Sa se scrie un program care citeste un numar nenegativ, subunitar, calculeaza si afiseaza radacina patrata din numarul respectiv cu o eroare mai mica decat 0,0000000001.

Programul de fata nu foloseste functia sqrt din biblioteca standard.

Pentru a extrage radacina patrata dintr-un numar a din intervalul [0, 1] se poate folosi metoda iterativa a lui Newton de mai jos.

Fie sirul:

x[0], x[1], x[2], ..., x[n], ...

unde:

(1)

x[n + 1] = 0.5(x[n] + a / x[n]) 

pentru n = 0, 1, 2, ...

Se demonstreaza ca sirul de mai sus este convergent si are limita egala cu radacina patrata a lui a. Convergenta este foarte rapida pentru a subunitar. In acest caz se poate lua x[0] = 1.

Pentru a obtine radacina patrata cu precizia ceruta, este suficient ca diferenta, in valoare absoluta, dintre doi termeni consecutivi ai sirului sa fie mai mica decat:

EPS = 0.0000000001

Sa observam ca pentru a rezolva problema cu metoda indicata mai sus, la fiecare pas al calculului, sunt necesari numai doi termeni ai sirului:

  1. din x[n] se determina x[n + 1] cu relatia (1);
  2. se compara:

    abs(x[n] - x[n+1]) cu EPS;

    Daca valoarea absoluta respectiva este mai mica decat EPS, atunci se calculeaza x[n+2] folosind relatia (1), in care x[n] se inlocuieste cu x[n+1].

    Deci se poate reveni la punctul 1 de mai sus, dupa ce lui x[n] i se atribuie valoarea x[n+1].

    Deci, in loc sa utilizam tabloul x, este suficient sa folosim doua variabile x1 si x2 care pastreaza in fiecare moment doi termeni consecutivi ai sirului de mai sus. Termenii sirului se obtin executand repetat secventa de atribuiri:

    x1 = x2;
    x2 = 0.5(x1 + a / x1);

    Dupa fiecare ecuatie a acestei secvente, x1 si x2 au ca valori doi termeni consecutivi ai sirului cautat.

    Secventa se va repeta atat timp cat (2) abs(x1 - x2) >= EPS, test care il facem dupa fiecare executie a secventei de atribuiri. Daca el se confirma, atunci se repeta secventa respectiva, altfel, valoarea variabilei x2 aproximeaza radacina patrata din a cu o eroare mai mica decat EPS.

Acest proces de calcul se realizeaza printr-o instructiune do-while.

Secventa de atribuiri este corpul ciclului, iar expresia (2) reprezinta conditia de continuare a lui. Intrucat primul termen al sirului are valoarea 1, instructiunea do-while este precedata de atribuirea:

x2 = 1;
#include <stdio.h>
#include <stdlib.h>

#define EPS 1e-10

main()
{
    double a, x1, x2, y;

    printf("Tastati valoarea lui a = ");
    if (scanf("%lf", &a) != 1 || a < 0 || a > 1) {
        printf("nu s-a tastat un numar din intervalul [0, 1]\n");
        exit(1);
    }
    if ( a == 0)
        x2 = 0; /* radacina patrata din zero este zero*/
    else
        if ( a == 1)
          x2 = 1; /* radacina patrata din unu este unu*/
        else {
          x2 = 1;
          do {
            x1 = x2;
            x2 = 0.5*(x1 + a / x1);
            if ((y = x1 - x2) < 0)
              y = -y;
          } while ( y >= EPS );
        }
    printf("a = %.11g\tsqrt(a) = %.11g\n", a, x2);
}

Observatii:

  1. Instructiunea do-while poate fi scrisa mai compact astfel:
    do {
      x1 = x2;
      x2 = 0.5*(x1 + a / x1);
    } while ((y = x1 - x2) < 0 ? -y : y, y >= EPS );
  2. Pentru numere supraunitare se poate aplica aceeasi metoda. In acest caz, se poate lua ca prim termen al sirului chiar numarul din care se extrage radacina patrata. O alta posibilitate este de a extrage radacina patrata din inversul numarului. Aceasta deoarece metoda este rapid convergenta pentru numere subunitare.

    Fie a > 1. Atunci x = 1 /a. Daca y este radacina patrata din x, atunci 1 / y este radacina patrata din a.

4.21. Sa se scrie un program care citeste un intreg pozitiv ce reprezinta o suma exprimata in lei. Se cere sa se afiseze numarul minim de bancnote de 500 lei, 200 lei, 100 lei etc. necesare pentru a exprima suma respectiva.

Procesul de calcul incepe cu determinarea numarului de bancnote de 500 de lei. In acest scop se imparte suma respectiva s la 500:

s / 500

Pentru a determina numarul bancnotelor de 200 lei se determina restul impartirii lui s la 500 si acest rest de imparte la 200 s.a.m.d. Se obtin pasii urmatori:

  1. q = s / 500; afiseaza q
  2. s = s % 500;
  3. q = s / 200; afiseaza q
  4. s = s % 200;
  5. q = s / 100; afiseaza q
  6. s = s % 100;
  7. q = s / 50; afiseaza q
  8. s = s % 50;
  9. q = s / 10; afiseaza q
  10. s = s % 10;
  11. q = s / 5; afiseaza q
  12. s = s % 5;
  13. q = s / 1; afiseaza q
  14. s = s % 1;

Se observa ca secventa:

q = s / n; afiseaza q
s = s % n;

se repeta pentru valorile lui n egale cu:

500, 200, 100, 50, 10, 5 si 1

Pentru a putea executa repetat aceasta secventa este nevoie de o metoda simpla prin care sa se atribuie lui n valorile corespunzatoare. Aceasta se poate realiza daca pastram datele respective intr-un tablou de tip int. Fie:

ban[0] = 1 ban[1] = 5 ban[2] = 10
ban[3] = 50 ban[4] = 100 ban[5] = 200
ban[6] = 500

Atunci n poate fi inlocuit prin ban[i], unde initial i = 6 si se decrementeaza la fiecare pas al ciclului. Deci urmeaza a se executa repetat secventa:

q = s / ban[i]; afiseaza q
s = s % ban[i];
i = i - 1;

Aceasta secventa se va executa repetat pana cand s devine zero.

#include <stdio.h>
#include <stdlib.h>

main()
{
    int ban[7];
    long s, q;
    int i;

    printf("Tastati suma: ");
    if (scanf("%ld", &s) != 1 || s <= 0) {
        printf("nu s-a tastat un intreg pozitiv\n");
        exit(1);
    }
	ban[0] = 1;
	ban[1] = 5;
	ban[2] = 10;
	ban[3] = 50;
	ban[4] = 100;
	ban[5] = 200;
	ban[6] = 500;
	i = 6;
	do {
        q = s / ban[i];
        if (q)
            if (q == 1)
                printf("o bancnota de %d lei\n", ban[i]);
            else
                printf("%ld bancnote de %d lei\n", q, ban[i]);
	} while (s %= ban[i--]);
}

4.9. Instructiunea continue