Operazioni |
Ovviamente adesso vogliamo implementare le operazioni, altrimenti la
classe fraction non avrebbe alcuna utilità.
Iniziamo con quelle più semplici: il prodotto e la divisione: a c a*c
a c a
d a*d
In entrambi i casi occorre ridurre ai minimi termini prima di restituire
il risultato.
friend fraction operator*(const fraction&
f1, const fraction& f2)
friend fraction operator/(const fraction&
f1, const fraction& f2)
L'accesso diretto in questo caso non è necessario, quindi non sarebbe stato necessario definirli come friend (avremmo potuto usare i metodi num( ) e denom( ) che sono efficienti come l'accesso diretto), però volevo sottolineare il fatto che questi operatori appartengono alla classe e li ho definiti friend lo stesso. Notare che il costruttore si occupa di ridurre ai minimi termini. Prima di addizione e sottrazione, occupiamoci dei confronti che sono più facili: il metodo più semplice per implementare gli operatori <,>,<=,>=, consiste nell'usare la regola dei prodotti incrociati e nell'evitare la costosa operazione di riduzione ad un comune denominatore: es. di regola dei prodotti incrociati a c
Ecco per esempio < e <= (> e >= sono analoghi): friend bool operator<(const fraction&
f1, const fraction& f2)
friend bool operator<=(const fraction&
f1, const fraction& f2)
La scelta tra metodi inline e non è una questione di gusti (maggiore velocità o programmi piccoli?). Infine gli operatori == e !=, che sono semplici, ma occorre comunque fare attenzione. Si potrebbe pensare che siccome le frazioni sono memorizzate in forma ridotta ai minimi termini si abbia: a c
a c
friend bool operator==(const fraction& f1, const fraction&
f2)
friend bool operator!=(const fraction& f1, const fraction&
f2)
Queste versioni sono palesemente sbagliate! Non vanno bene con la nostra
convenzione di lasciare il segno - davanti a numeratore e denominatore.
Infatti (-1,1) e (1,-1) sono considerate diverse, mentre sono uguali. Inoltre
(0, 2) viene considerata diversa da (0,1), ecc...
a b
friend bool operator==(const fraction&
f1, const fraction& f2)
friend bool operator!=(const fraction&
f1, const fraction& f2)
L'addizione e la sottrazione sono più difficili: ci occorre un algoritmo efficiente per calcolare il minimo comune multiplo. Infatti occorre ridurre le frazioni al loro minimo comune denominatore. Il minimo comune denominatore non è altro che il minimo comune multiplo dei denominatori: Es. 2 5
6/3*2 + 6/6*5 9 3
come evidente occorre infine in generale ridurre ai minimi termini. L'operazione è piuttosto costosa come vedete, ma non c'è scampo. La sottrazione è analoga, basta mettere un - nella formula anzichè il +: a c
mcm(b,d)/b*a +/- mcm(b,d)/d*c
L'algoritmo del mcm è molto semplice. Nella scorsa lezione avete
visto come la matematica e l'informatica non sono in realtà due
scienze separate (in realtà nessuna scienza è separata dall'altra
e personalmente ritengo sia riduttivo o addirittura ridicolo ridurre la
scienza in discipline separate).
// mcm.c
long mcm(long a, long b)
while (o*a % b)
int main()
scanf("%ld%ld", &a, &b); printf("%ld", mcm(a,b)); exit(0);
Come potete vedere l'algoritmo agisce a forza bruta, provando tutti i multipli di a fino a che ne trova il primo divisibile per b. Questo è il mcm. Questa era la mia prima versione, funziona, ma ci sono dei miglioramenti che possiamo fare al fine di renderlo più veloce. Un primo miglioramento consiste nel sostituire nel ciclo una moltiplicazione con un'addizione, che risulta un po' più veloce: long mcm(long a, long b)
while (a % b)
oppure, equivalentemente (e lo preferisco perchè più leggibile): long mcm(long a, long b)
while (mcm % b)
Notare che assegnare lo stesso nome ad una variabile locale della funzione è lecito (l'unico inconveniente è che non si potrà chiamare ricorsivamente la funzione, ma qui vogliamo fare una funzione ottimizzata, mica ricorsiva!) Un altro miglioramento possibile: considerate il caso in cui a=2 e b=100,000.
Occorre fare 49999
nota: come calcolo quante addizioni occorrono? faccio questo ragionamento: se b fosse 4, occorrerebbe incrementare a di 2 una volta. Se b fosse 6, 2 volte. Se b fosse 8, 3 volte. Vedi una qualche relazione? Quando b è 100,000 occorre incrementarlo (100,000-2)/2=49999. La soluzione è semplice: osservate ad es. che se fosse a=100,000 e b=2, il while non verrebbe eseguito nemmeno una volta. Quindi occorre fare in modo che prima che l'algoritmo inizi, a sia maggiore di b. Se già non lo è occorre scambiarli // versione migliorata
if (a<b)
mcm=a;
Bene, questo non vi "impalla" più il computer. Più ottimizzato di così non riesco a concepirlo. Se avete idee migliori, fatemelo sapere. Tenete conto che deve comportarsi bene coi numeri negativi. Bene, finalmente possiamo scrivere gli operatori + e - (ricordate l'identità di prima): friend fraction operator+(const fraction&
f1, const fraction& f2)
friend fraction operator-(const fraction&
f1, const fraction& f2)
|
![]() |
![]() |
![]() |