TERNE PITAGORICHE Obiettivi dell’esercitazione Realizzare programmi che includano semplici funzioni Contenuti tecnici Scrittura e invocazione di funzioni di calcolo con parametri passati by reference. Uso delle funzioni. Metodo di progetto: Raffinamenti successivi
Problema Realizzare un programma che generi e stampi tutte le terne pitagoriche nell'intervallo degli interi (A, B e C formano una terna pitagorica se A2 + B2 = C2). È richiesto che il test venga effettuato da una funzione che restituisca il valore TRUE se la terna passata come parametro è pitagorica, FALSE altrimenti. Suggerimento: attenzione all’overflow della somma!
Analisi Occorre provare per tutti gli a, per tutti i b e per tutti i c se (a,b,c) formano una terna pitagorica. Deve essere a2 (e b2 e c2) < INT_MAX, quindi i valori di a, b e c vanno limitati a
Schema base val_max = sqrt(INT_MAX); for(a = 1; a <= val_max; a++) { for(b = 1; b <= val_max ; b++) for(c = 1; c <= val_max; c++) if(is_terna_pitagorica(a, b, c,)) printf("%d %d %d\n", a, b, c); }
Osservazioni Se a, b e c iniziano tutti da 1, verranno trovate le terne equivalenti: 3, 4, 5 4, 3, 5 5, 4,3 Si ovvia iniziando b da un valore successivo ad a, c da un valore successivo a b.
val_max = sqrt(INT_MAX); for(a = 1; a <= val_max; a++) { for(b = a+1; b <= val_max; b++) for(c = b+1; c <= val_max; c++) if(is_terna_pitagorica(a, b, c)) printf("%d %d %d\n", a, b, c); }
Se si trova un valore di c che soddisfa alla condizione che a, b, e c sono una terna, è inutile continuare i test con i valori successivi di c! Si usa un flag, trovata_terna, per bloccare il ciclo più interno appena si trova una terna.
val_max = sqrt(INT_MAX); for(a = 1; a <= val_max; a++) { for(b = a+1; b <= val_max; b++) trovata_terna = FALSE; for(c = b+1; (c <= val_max) && !trovata_terna; c++) if(is_terna_pitagorica(a, b, c)) printf("%d %d %d\n", a, b, c); trovata_terna = TRUE; }
Funzione is_terna_pitagorica Lo schema int is_terna_pitagorica(int a, int b, int c) { int risultato; int a_quad_piu_b_quad; a_quad_piu_b_quad = a * a + b * b; risultato = (a_quad_piu_b_quad == c * c); return(risultato); }
Osservazione La somma a2 + b2 può dare overflow. In questo caso il confronto con c2 non ha senso Occorre comunicare al programma chiamante questa condizione (oltre all’informazione se è o meno una terna pitagorica): occorre un parametro passato by-reference Trattandosi di una somma tra valori positivi, il test di overflow si esegue controllando se il risultato è negativo
Versione corretta per l’overflow int is_terna_pitagorica(int a, int b, int c, int *p_stato) { int risultato; int a_quad_piu_b_quad; a_quad_piu_b_quad = a * a + b * b; if(a_quad_piu_b_quad < 0) *p_stato = SI_OVERFLOW; risultato = FALSE; } else *p_stato = NO_OVERFLOW; risultato = (a_quad_piu_b_quad == c * c); return(risultato);
Osservazioni per il main Se per una coppia a, b, si riscontra overflow, è inutile continuare con valori superiori (darebbero overflow a maggior ragione): occorre bloccare i cicli più interni. Il main diventa:
Int main() { int val_max; int condizione; int trovata_terna; val_max = sqrt(INT_MAX); for(a = 1; a <= val_max; a++) condizione = NO_OVERFLOW; for(b = a+1; b <= val_max && condizione == NO_OVERFLOW; b++) trovata_terna = FALSE; for(c = b+1; (c <= val_max) && (condizione == NO_OVERFLOW) && !trovata_terna; c++) if(is_terna_pitagorica(a, b, c, &condizione)) printf("%d %d %d\n", a, b, c); trovata_terna = TRUE; } return EXIT_SUCCESS;
Problema nella visualizzazione dei risultati Il numero di terne pitagoriche trovato è molto grande, e il programma le stampa tutte: a causa dello scorrimento dello schermo, si rischia di riuscire a vedere solo quelle finali, perdendo quelle iniziali. Soluzione: bloccare lo schermo ogni 23 righe, chiedendo che venga battuto INVIO per continuare. Il main diventa:
#include <stdio.h> #include <math.h> #include <limits.h> #include <stdlib.h> #define NO_OVERFLOW 0 #define SI_OVERFLOW 1 #define FALSE 0 #define TRUE 1 int is_terna_pitagorica(int a, int b, int c, int *p_stato); Int main() { int a, b, c; int val_max; int condizione; int trovata_terna; int contatore = 0;
val_max = sqrt(MAXINT); for(a = 1; a <= val_max; a++) { condizione = NO_OVERFLOW; for(b = a+1; b <= val_max && condizione == NO_OVERFLOW; b++) trovata_terna = FALSE; for(c = b+1; (c <= val_max) && (condizione == NO_OVERFLOW) && !trovata_terna; c++) if(is_terna_pitagorica(a, b, c, &condizione)) printf("%d %d %d\n", a, b, c); trovata_terna = TRUE; contatore++; if((contatore % 23) == 0) system("PAUSE"); } system(“PAUSE”); return EXIT_SUCCESS;
Considerazioni finali Si è partiti ipotizzando per a, b e c l’intervallo 1 . Si è trovato un vincolo che coinvolge a e b: a2+b2 < INT_MAX (test di overflow). Ad una attenta osservazione, si può vedere che esiste un vincolo tra c e a e b: è inutile testare valori di c per cui c2 > a2+b2. Se ciò accade, possiamo chiudere il ciclo più interno (è stata superata la soglia).
Programma definitivo #include <stdio.h> #include <math.h> #include <limits.h> #include <stdlib.h> #define NO_OVERFLOW 0 #define SI_OVERFLOW 1 #define FALSE 0 #define TRUE 1 int is_terna_pitagorica(int a, int b, int c, int *p_stato, int *p_superato_limite)
int main() { int a, b, c; int val_max; int condizione; Int superato_limite; int trovata_terna; int contatore = 0; val_max = sqrt(MAXINT); for(a = 1; a <= val_max; a++) condizione = NO_OVERFLOW; for(b = a+1; b <= val_max && condizione == NO_OVERFLOW; b++)
{ trovata_terna = FALSE; superato_limite = FALSE; for(c = b+1; (c <= val_max) && (condizione == NO_OVERFLOW) && !trovata_terna && !superato_limite; c++) if(is_terna_pitagorica(a, b, c, &condizione, &superato_limite)) printf("%d %d %d\n", a, b, c); trovata_terna = TRUE; contatore++; if((contatore % 23) == 0) system("PAUSE"); } system(“PAUSE”); return EXIT_SUCCESS;
int is_terna_pitagorica(int a, int b, int c, int. p_stato, int int is_terna_pitagorica(int a, int b, int c, int *p_stato, int *p_superato_limite) { int risultato; int a_quad_piu_b_quad; a_quad_piu_b_quad = a * a + b * b; if(a_quad_piu_b_quad < 0) *p_stato = SI_OVERFLOW; risultato = FALSE; } else *p_stato = NO_OVERFLOW; if(c * c > a_quad_piu_b_quad) *p_superato_limite = TRUE; risultato = (a_quad_piu_b_quad == c * c); return(risultato);