Riduzione di una frazione

Un problema matematico decisamente molto affrontato nei corsi di informatica e C++. Il problema è di per se stesso semplice ma permette all’alunno alle prime armi di addentrarsi nell’uso delle strutture del linguaggio di programmazione e di affinare la capacità di astrarre e generalizzare un problema.Vediamo come.

L’approccio è sostanzialmente semplice: parte di definizione delle variabili, programma centrale e output. E’ il classico problema che non richiede una lettura ripetuta di variabili di ingresso. Non mi stancherò mai di scriverlo e ripeterlo: le variabili, specialmente di lavoro e output vanno inizializzate a 0 o opportuno valore numerico e di testo/carattere. Il nome delle variabili consiglio sempre sia chiaro ed esplicativo. Qualche docente tende ad abbreviare i nomi, ma diventano incomprensibili ed è facile confondersi: meglio scrivere qualche carattere in più!

#include <iostream>

using namespace std;

int main()
{
    //Dichiarazione delle varibili
    int numeratore = 0;    //input
    int denominatore = 0;  //input

    int numeratoreridotto = 0; //output
    int denominatoreridotto = 0; //output

    //Lettura delle variabili
    cout << "Inserisci il numeratore" << endl;
    cin >> numeratore;
    cout << "Inserisci il denominatore" << endl;
    cin >> denominatore;

Eccoci al corpo centrale. Dobbiamo cercare di gestire tutti i casi possibili. Consiglio sempre al lettore ed alunno di risolvere prima il problema matematico su carta, cercando di capire con calma quali sono tutti i casi da considerare e come comportarsi. Gettarsi sul codice è un errore che si paga perdendosi dietro a variabili e assegnazioni improbabili. Provate degli esempi numerici, non c’è nulla di male a partire da casi pratici per generalizzare la soluzione! A questo punto, se  le nostre ipotesi su carta sono complete, si tratta di tradurre i vari casi in corrispondenti if binari! Qui ne abbiamo individuati 4, ognuno che quindi va a creare la soluzione opportuna ed ovviamente esclude gli altri. Si potevano usare gli else? Si certo diventavano tutti else if in realtà. Per mia preferenza, evito di creare strutture annidate di if/else quando non c’è necessità di strutture binarie strette. Mettere più if uno sotto l’altro in realtà non aumentano la complessità computazionale e non pregiudica le prestazioni. Il caso più complesso è quando numeratore o denominatore non sono perfettamente divisibili ma occorre trovare quali siano i divisori intermedi comuni, escludendo 1 e i numeri stessi. Per fare questo quindi tocca contare con un contatore e vedere se divide entrambi, così da aggiornare i rispettivi numeratori e divisori divisi. Per capire bene, provate con due numeri come nei commenti e e incrementate il contatore per vedere come si comporta il codice: come sempre, se scrivete codice a caso, l’errore è dietro l’angolo. Fate un esempio e vedete se il vostro ragionamento col codice raggiunge lo scopo. Una nota per i più esperti: i cicli per vedere i divisori potrebbero essere stoppati alla meta del numero più piccolo tra numeratore e denominatore. Es. 21/15 il ciclo si potrebbe stoppare da 2 a 7 perché se ci pensate bene, non esisterebbero più divisori se non minori strettamente di 2. Si tratta comunque di una finezza che non pregiudica il nostro risultato, ma vale la pena citarlo.

    // operatore modulo (resto)   10%5= 0    10%3=1
    // Caso es. 10/2
    if (numeratore % denominatore == 0)
    {
        numeratoreridotto= numeratore / denominatore;
        denominatoreridotto = 1;
    }

    //Caso es. 2/10
    if (denominatore % numeratore == 0)
    {
        numeratoreridotto = 1;
        denominatoreridotto = denominatore / numeratore;
    }

    //caso numeratore > denominatore  es. 21/15
    if (numeratore > denominatore)
    {
        for (int i = 2; i < denominatore; i++)
        {
            //faccio scorrere il contatore i e lo uso per vedere se trovo un divisore
            if (denominatore % i == 0 && numeratore % i == 0)
            {
                numeratoreridotto = numeratore / i;
                denominatoreridotto = denominatore / i;
            }
        }
    }

    // Caso es. 15/21
    if (denominatore > numeratore)
    {
        //caso denominatore > numeratore     15/21
        for (int i = 2; i < numeratore; i++)
        {
            //faccio scorrere il contatore i e lo uso per vedere se trovo un divisore
            if (denominatore % i == 0 && numeratore % i == 0)
            {
                numeratoreridotto = numeratore / i;
                denominatoreridotto = denominatore / i;
            }
        }
    }

Non ci resta che mostrare il risultato che abbiamo calcolato separatamente in ogni if!

    //Mostrare il risultato
    cout << numeratoreridotto;
    cout << " / ";
    cout << denominatoreridotto;

Ed ecco il codice completo.

#include <iostream>

using namespace std;

int main()
{
    //Dichiarazione delle varibili
    int numeratore = 0;    //input
    int denominatore = 0;  //input

    int numeratoreridotto = 0; //output
    int denominatoreridotto = 0; //output

    //Lettura delle variabili
    cout << "Inserisci il numeratore" << endl;
    cin >> numeratore;
    cout << "Inserisci il denominatore" << endl;
    cin >> denominatore;

    
    // operatore modulo (resto)   10%5= 0    10%3=1
    if (numeratore % denominatore == 0)
    {
        numeratoreridotto= numeratore / denominatore;
        denominatoreridotto = 1;
    }

    if (denominatore % numeratore == 0)
    {
        numeratoreridotto = 1;
        denominatoreridotto = denominatore / numeratore;
    }

    //caso numeratore > denominatore    21/15
    if (numeratore > denominatore)
    {
        for (int i = 2; i < denominatore; i++)
        {
            //faccio scorrere il contatore i e lo uso per vedere se trovo un divisore
            if (denominatore % i == 0 && numeratore % i == 0)
            {
                numeratoreridotto = numeratore / i;
                denominatoreridotto = denominatore / i;
            }
        }
    }

    if (denominatore > numeratore)
    {
        //caso denominatore > numeratore     15/21
        for (int i = 2; i < numeratore; i++)
        {
            //faccio scorrere il contatore i e lo uso per vedere se trovo un divisore
            if (denominatore % i == 0 && numeratore % i == 0)
            {
                numeratoreridotto = numeratore / i;
                denominatoreridotto = denominatore / i;
            }
        }
    }

    //Mostrare il risultato
    cout << numeratoreridotto;
    cout << " / ";
    cout << denominatoreridotto;

    getchar();
    return 0;
}

Ultima modifica 5 Aprile 2023