I) Dati due numeri interi positivi a e b, quanti sono i quadrati perfetti 
contenuti nell'intervallo dato [a;b]?

QUADRATI0.C  uno scheletro vuoto con la sola interfaccia per caricare i 
dati e restituire i risultati, da cui partire per provare la propria 
soluzione.

QUADRATI1.C scorre i numeri compresi fra a e b con un ciclo e testa
ad ogni iterazione se il numero corrente i  un quadrato perfetto o no.
Per testarla, bisogna trovare un numero j il cui quadrato sia pari a i,
oppure dimostrare che non ne esistono.

Un modo non molto efficiente di fare tutto questo :

cont = 0;
for (i = a; i <= b; i++)
  if (QuadratoPerfetto(i)) cont++;

dove

boolean QuadratoPerfetto (long i)
{
  long si;
  
  si = 1;
  while (si * si < i)
    si++;
    
  if (si * si == i)
    return TRUE;
  else
    return FALSE;
}

Funziona perch scorre via via tutti gli interi "si" che sono radici 
quadrate di quadrati perfetti minori di "i", fino a uguagliare o superare 
"i". Se "si" al quadrato uguaglia "i", "i"  un quadrato perfetto; se lo 
supera, "i" non lo  ed  inutile provare valori di "si" superiori.
E' per molto inefficiente scorrere tutti i quadrati fino a "i", per ogni 
"i" (si provi con a = 10000000 e b = 20000000: ci sono 1310 quadrati 
perfetti e il mio portatile calcola questo valore in 83 secondi).

QUADRATI2.C determina se "i"  un quadrato perfetto calcolandone la radice
quadrata. Occorre la funzione sqrt(), che  un'operazione pi lenta delle 
semplici somme e prodotti, e occorre la libreria math.h per poterla usare. 
Il problema principale  che sqrt(i)  un numero reale, e non  possibile 
sapere con assoluta precisione se sqrt(i)  in effetti un valore intero o 
 solo molto vicino a un intero. Dobbiamo introdurre un'approssimazione
EPSILON.
Con a = 10000000 e b = 20000000 il mio portatile trova i 1310 quadrati 
perfetti in 1.4 secondi.

boolean QuadratoPerfetto (long i)
{
  double j;
  
  j = sqrt(i);
    
  if (j < (long) j) + EPSILON)
    return TRUE;
  else
    return FALSE;
}

QUADRATI3.C resta nell'ambito dei numeri interi, scorrendo, anzich i 
numeri fra "a" e "b", i quadrati perfetti fino a "b", e contando quelli 
compresi fra "a" e "b".

cont = 0L;
si = 1;
while (si * si <= b)
{
  if (si * si >= a) cont++;
  si++;
}

In questo ciclo, "si * si"  sempre un quadrato perfetto ed  sempre <= b.
Si tratta solo di verificare che sia >= a.
Con a = 10000000 e b = 20000000 il mio portatile trova i 1310 quadrati 
perfetti in meno di 1 millesimo di secondo.

QUADRATI4.C funziona come il precedente, ma inizializza "si" a 
(long) ceil(sqrt(a)), anzich a 1, per accelerare. Questo richiede una 
chiamata alla funzione sqrt(). 
Per controllare gli errori numerici, conviene abbassare leggermente "a" 
prima di estrarre la radice e alzarla leggermente prima di troncare, 
per evitare che l'arrotondamento per eccesso o il troncamento introducano
errori nel caso di numeri interi.
Il guadagno in tempo di calcolo non  valutabile, dato che il tempo  gi
inferiore al millesimo di secondo, anche per intervalli molto pi ampi.

QUADRATI5.C  ancora pi efficiente, usando due volte la funzione sqrt(). 
Infatti, l'intero pi grande il cui quadrato  <= b  (long) sqrt(b), 
mentre l'intero pi piccolo il cui quadrato  >= a   (int) ceil(sqrt(a)). 
Salvo qualche possibile problema numerico di approssimazione, il risultato 
del problema  la differenza di questi due numeri, incrementata di uno.
