// aprenentatge de bucles

Bucles en C

while, for i do-while: com repeteix un programa instruccions, com es controla amb break i continue, i com llegir-ne l'execució pas a pas.

Per què repetim instruccions?

Un bucle executa un bloc de codi repetidament mentre es compleixi (o fins que es compleixi) una condició. Evita haver d'escriure la mateixa instrucció moltes vegades i permet processar llistes de dades, comptar, o esperar un esdeveniment.

while mentre la condició sigui certa

Comprova la condició abans de cada volta. Si és falsa des del principi, el cos no s'executa mai.

Sintaxi

while (condició) {
    // cos: s'executa mentre condició sigui certa
}

Exemple

int i = 1;
while (i <= 5) {
    printf("%d\n", i);
    i++;
}

S'imprimeixen els nombres 1 a 5. Cada volta: es comprova i <= 5, s'executa el cos, i s'incrementa i manualment — si oblidéssim l'i++, el bucle no acabaria mai.

for comptador amb tres parts

Agrupa en una sola línia la inicialització, la condició i l'increment. És la forma habitual quan ja saps quantes voltes vols fer.

Sintaxi

for (inicialització; condició; increment) {
    // cos
}

Exemple

for (int i = 1; i <= 5; i++) {
    printf("%d\n", i);
}

int i = 1 s'executa una única vegada, al principi. Després, a cada volta: es comprova i <= 5; si és certa s'executa el cos i, en acabar, s'executa i++; llavors es torna a comprovar la condició.

do-while primer el cos, després la condició

Igual que while, però la condició es comprova després del cos. Per això el cos s'executa sempre com a mínim una vegada, encara que la condició sigui falsa des del principi.

Sintaxi

do {
    // cos: s'executa sempre com a mínim un cop
} while (condició);

Exemple

int i = 9;
do {
    printf("%d\n", i);
    i++;
} while (i <= 5);

Encara que 9 <= 5 és fals, s'imprimeix un cop el 9 abans de comprovar-ho. És útil, per exemple, quan vols demanar una dada a l'usuari almenys una vegada i repetir-ho si la introdueix malament.

Control del flux dins un bucle

De vegades cal sortir abans d'hora, o saltar-nos una volta concreta sense sortir del tot.

break

Talla el bucle immediatament: no es torna a comprovar la condició, s'ix directament del bucle.

for (int i = 1; i <= 10; i++) {
    if (i == 4) {
        break;
    }
    printf("%d\n", i);
}
// imprimeix: 1 2 3

continue

Salta directament a la següent volta (a l'increment, en un for), sense executar la resta del cos d'aquesta volta.

for (int i = 1; i <= 5; i++) {
    if (i == 3) {
        continue;
    }
    printf("%d\n", i);
}
// imprimeix: 1 2 4 5  (el 3 no s'arriba a imprimir)

Mira el bucle executar-se pas a pas

Tria una variant i avança pas a pas (o deixa-ho en reproducció automàtica) per veure com evolucionen les variables i la sortida per terminal.

Codi

Variables

NomValor

Terminal

Pas 1 / 1

Problemes per resoldre

Sense solució ni comprovació automàtica: escriu el programa, compila'l (gcc programa.c -o programa) i prova'l amb els exemples. Amb variables, condicionals i bucles en tens prou. Les pistes són plegades perquè les obris només si les necessites.

1. Suma d'una progressió aritmètica for

Una progressió aritmètica té com a terme general a(n) = a·n + b. Demana a i b (nombres decimals) i n (enter), i calcula la suma dels n primers termes sumant-los un a un. Després comprova el resultat calculant-lo també amb la fórmula (a1 + an) · n / 2, on a1 és el primer terme i an l'últim.

Introdueix a, b i n: 2 1 5
Suma dels 5 primers termes: 35.00
Amb la fórmula: 35.00
Pistes

Els termes són 3, 5, 7, 9 i 11, i sumen 35. Necessites un acumulador suma que comenci a 0 i un for amb un comptador que vagi d'1 a n; a cada volta hi afegeixes a*ct + b.

Fes servir double per a i b (amb %lf al scanf) i int per a n. El primer terme és a*1 + b i l'últim a*n + b; vigila els parèntesis de la fórmula.

2. Una dada vàlida do-while

Demana una nota entre 0 i 10 (tots dos inclosos). Si l'usuari escriu un valor fora d'aquest rang, mostra un missatge d'error i torna a demanar-la, tantes vegades com calgui. Quan la nota sigui correcta, mostra-la.

Nota (0-10): 12
Valor fora de rang.
Nota (0-10): -3
Valor fora de rang.
Nota (0-10): 7
Nota acceptada: 7
Pistes

La nota s'ha de demanar almenys un cop abans de poder-la comprovar: és el cas típic del do-while.

La condició per repetir és la contrària de «és vàlida»: nota < 0 || nota > 10. El missatge d'error només s'ha de mostrar quan el valor és incorrecte, així que caldrà un if dins del bucle.

3. Multiplicació russa do-while

L'algorisme rus per multiplicar escriu a i b en dues columnes. A cada fila, a es divideix entre 2 (divisió entera) i b es multiplica per 2, fins que a arriba a 0. La suma dels valors de b de les files on a és senar és el producte a·b. Escriu un programa que ho calculi així, sense fer a*b.

Introdueix a i b: 46 43
46 x 43 = 1978
Pistes

Per a 46 i 43 les files són:

  a     b
 46    43
 23    86   ← a és senar: se suma 86
 11   172   ← senar: se suma
  5   344   ← senar: se suma
  2   688
  1  1376   ← senar: se suma

I 86 + 172 + 344 + 1376 = 1978.

Si vols escriure a i b originals al final, treballa amb còpies (per exemple x i y) dins del bucle. Un nombre és senar quan x % 2 == 1.

4. Decimal a binari while

Demana un nombre natural més petit que 256 i escriu-ne la representació en binari fent divisions successives per 2 i quedant-te amb les restes. Si el nombre no és vàlid, torna a demanar-lo.

Nombre (0-255): 100
100 en binari és 1100100
Pistes

Les restes surten de dreta a esquerra: 100%2 = 0, 50%2 = 0, 25%2 = 1, 12%2 = 0... La primera que surt és el bit menys significatiu.

Sense guardar-les enlloc, pots anar construint el resultat com un enter: multiplica cada resta per pos (que comença a 1 i es multiplica per 10 a cada volta) i suma-ho a bin. Per a 255 el resultat és 11111111, que hi cap en un int.

Comprova que el 0 també funciona (ha d'escriure 0). Per demanar el nombre fins que sigui vàlid, reutilitza la idea de l'exercici 2.

5. Nombre primer while

Demana un enter n i digues si és primer. Un nombre és primer si només és divisible entre 1 i entre ell mateix; el 0 i l'1 no ho són.

Nombre: 17
17 és primer

Nombre: 91
91 no és primer

Nombre: 1
1 no és primer
Pistes

Prova els divisors 2, 3, 4... i para en trobar el primer que divideixi n (n % divisor == 0): no cal mirar-los tots.

N'hi ha prou de provar-los mentre divisor*divisor <= n (si n té un divisor més gran que la seva arrel, en té també un de més petit). Així no cal cap arrel quadrada ni math.h.

Una variable indicadora primer (1 o 0) que passa a 0 quan trobes un divisor et deixa sortir amb la condició del while. Comença-la a 0 si n < 2.

6. MCD de tres nombres while

L'algorisme d'Euclides calcula el màxim comú divisor de m i n: mentre n no sigui 0, es substitueix (m, n) per (n, resta de m entre n); quan n és 0, el MCD és m. Escriu un programa que llegeixi tres enters positius i en calculi el MCD, sabent que MCD(a, b, c) = MCD(MCD(a, b), c).

Introdueix a, b i c: 120 140 210
MCD(120, 140, 210) = 10
Pistes

Els passos són MCD(120, 140) = 20 i després MCD(20, 210) = 10.

Necessites m, n i una variable r per a la resta. El cos del bucle és r = m % n; m = n; n = r;. Calcula primer el MCD d'a i b i guarda'l; després aplica el mateix bucle amb aquest resultat i c. Treballa amb còpies si vols escriure a, b i c al final.

7. L'algorisme 3n+1 while

Parteix d'un enter n ≥ 1 i repeteix: escriu n; si n val 1, acaba; si n és senar, substitueix-lo per 3n+1; si és parell, per n/2. Escriu tots els valors (el «cicle» de n) en una sola línia i, al final, la longitud del cicle (quants valors has escrit, comptant el primer i l'1).

Nombre: 22
22 11 34 17 52 26 13 40 20 10 5 16 8 4 2 1
Longitud del cicle: 16

Nombre: 8
8 4 2 1
Longitud del cicle: 4
Pistes

No saps quantes voltes farà el bucle, per això un while (que s'acaba quan n val 1) hi encaixa millor que un for.

Escriu el primer valor abans del bucle, i dins del bucle actualitza n i escriu-lo amb un espai al davant. Un comptador que comenci a 1 (el primer valor ja escrit) et dona la longitud. Prova nombres de la forma 2n (64, 1024...): tenen cicles molt curts.

8. Nombres «XX» bucles encaixats

Diem que un enter és «XX» si tots els seus divisors, excepte ell mateix i l'1, són nombres primers. Demana un enter positiu N i escriu tots els nombres «XX» entre 0 i N (tots dos exclosos), separats per espais.

N: 21
1 2 3 4 5 6 7 9 10 11 13 14 15 17 19
Pistes

El 8 no és «XX» perquè és divisible entre 4, que no és primer. El 6 sí que ho és: els seus divisors 2 i 3 són primers. L'1 no té cap divisor per comprovar, així que hi entra.

Tres bucles encaixats: el de fora recorre els candidats i d'1 a N-1; el del mig recorre els possibles divisors d de 2 a i-1 i mira si i % d == 0; el de dins comprova si aquest divisor és primer (com a l'exercici 5).

Una variable indicadora xx que comença a 1 i es posa a 0 quan trobes un divisor que no és primer. Escrius i només si, en acabar, xx segueix a 1.

Exercicis d'autoavaluació

Cada exercici es genera amb valors nous a l'atzar. Llegeix el codi, pensa quina serà la sortida per terminal i comprova-ho.

—

Quina serà la sortida per terminal d'aquest programa?