Capitolo 8

Problemi riepilogativi.

 


Gestire i tempi di una gara di slalom.

Realizzare un tipo grandeint per interi che superano la capacità del tipo integer.

realizzare il tipo razionale

Ricercare una sottostringa r in una stringa s.

Determinare tutti i numeri primi minori di n.

Dato un numero intero n dire se è primo.

Torre di Hanoi


Problema 1: scrivere un programma per gestire i tempi di una gara di slalom.

OBIETTIVO: in genere con il termine "gestire" si intende offrire la possibilità di inserire, visualizzare, modificare e ricercare dati di un determinato tipo. Nel nostro caso i tempi degli atleti di una gara di slalom. La visualizzazione sarà la stampa della classifica con tempo e atleta, che dovrà avvenire dopo ogni discesa. Poichè inoltre le gare di slalom si svolgono in due manche, durante la seconda discesa sarà necessario visualizzare anche la classifica finale. Ovviamente il programma dovrà permettere di inserire, dopo ogni discesa, il tempo dell'atleta. Ogni atleta sarà individuato dal numero di pettorale.

RISULTATI: la classifica dopo ogni concorrente, la classifica della prima manche, la classifica finale.

DATI: il numero di pettorale e il tempo di ciascun concorrente nella prima e nella seconda manche. Supponiamo che il tempo sia misurato in centesimi di secondo e che in caso di prova nulla sia assegnato il tempo massimo di 32000. La variabile che dovrà contenere il tempo sarà di tipo integer.

STRUTTURA DATI: per quanto riguarda la struttura dati sono necessarie alcune osservazioni. Noi dobbiamo conoscere il pettorale ( un integer ) con un tempo corrispondente ( un integer ). I pettorali sono tutti diversi e, a priori si conosce quanti atleti partecipano alla gara. Inoltre la stampa delle classifiche avverrà sempre per ordine crescente di tempi. I dati saranno inseriti uno alla volta rispettando l'ordinamento. Si possono utilizzare, per ogni classifica, due vettori uno per i tempi e uno per i pettorali. Alla posizione i del vettore dei tempi ci sarà il tempo del concorrente classificato all'i-esimo posto. Il numero di pettorale che individua l'atleta sarà alla stessa posizione del vettore dei pettorali. La struttura dati sarà:

CONST

dim=100;

fuori=32000;

TYPE

vettore=ARRAY[0..dim] OF INTEGER;

{ la posizione 0 contiene la lunghezza effettiva del vettore }

VAR

manche1, manche2, finale,

petto1, petto2, pettof :vettore

ALGORITMO:

BEGIN

inizializza vettori

WHILE not fine DO BEGIN

leggi( tempo, pettorale );

inserisci( tempo, pettorale, manche1, petto1 );

stampa( manche1, petto1 )

END;

WHILE not fine DO BEGIN

leggi( tempo, pettorale );

inserisci( tempo, pettorale, manche2, petto2 );

stampa( manche2, petto2 );

aggiorna finale e pettof

stampa( finale, pettof );

END

END.

 

PROCEDURE leggi( VAR t, p:INTEGER );

BEGIN

REPEAT

WRITE('Pettorale ');

READLN( p );

UNTIL (p > 0) AND (p < dim);

REPEAT

WRITE('Tempo: [0 se fuori gara] ');

READLN( t );

UNTIL t >= 0;

IF t = 0 THEN t:=fuori;

END;

 

PROCEDURE inizializza;

BEGIN

petto1[0]:=0; petto2[0]:=0; pettof[0]:=0;

manche1[0]:=0; manche2[0]:=0; finale[0]:=0;

END;

- fine: utilizziamo una funzione che richiede conferma per giudicare terminata una gara.

FUNCTION fine:BOOLEAN;

VAR

risposta:CHAR;

BEGIN

REPEAT

WRITE('Fine gara? [S/N] ');

READLN(risposta)

UNTIL (risposta='S') OR (risposta='N');

fine:=risposta='S'

END;

- inserimento: avverrà rispettando l'ordinamento crescente del vettore dei tempi.

PROCEDURE inserisci( t, p:INTEGER; VAR cl, pp:vettore );

VAR

i:INTEGER;

{ l'inserimento avviene ordinato per tempi crescenti }

FUNCTION posizione( v:vettore; x:INTEGER ):INTEGER;

{ ricerca la posizione dove inserire il nuovo tempo }

VAR

i :INTEGER;

alt :BOOLEAN;

BEGIN

i:=1; alt:=FALSE;

WHILE (i<=v[0]) AND NOT alt DO

IF x <= v[i] THEN alt:=TRUE

ELSE i:=i+1;

posizione:=i;

END; { posizione }

PROCEDURE buco( VAR v:vettore; p:INTEGER );

{ sposta tutti gli elementi del vettore per inserire il nuovo dato }

VAR

i:INTEGER;

BEGIN

FOR i:=v[0] DOWNTO p DO

v[i+1]:=v[i];

END; { buco }

 

BEGIN { inserisci }

i:=posizione( cl, t );

buco( cl, i ); { crea buco nel vettore tempi }

buco( pp, i ); { crea buco nel vettore pettorali }

cl[i]:=t; pp[i]:=p;

cl[0]:=cl[0]+1; pp[0]:=pp[0]+1;

END;

- aggiornamento della classifica finale: è necessario svolgere una ricerca per ottenere il tempo di ogni atleta nella prima manche. Se realizziamo una function che, avuto in input un numero di pettorale e la classifica, ci restituisce il tempo che ha ottenuto ecco che il problema è risolto.

FUNCTION tmp( tempi, petto:vettore; p:INTEGER ):INTEGER;

VAR

i:INTEGER;

BEGIN

i:=0;

REPEAT

i:=i+1;

UNTIL petto[i]=p;

tmp:=tempi[i];

END;

Osserviamo che se uno sciatore gareggia nella seconda manche sicuramente sarà disceso anche nella prima.

Aggiorna finale e pettof diventerà:

PROCEDURE aggiorna( tempo, pettorale:INTEGER );

BEGIN

IF tempo <> fuori THEN

inserisci( tempo + tmp(manche1, petto1, pettorale),

pettorale, finale, pettof );

END;

- stampa: utilizziamo una procedura che avuto in ingresso un vettore di tempi e il corrispondente vettore dei pettorali li stampa.

PROCEDURE stampa( t, p:vettore );

VAR

I : INTEGER;

BEGIN

FOR i:=1 TO t[0] DO

IF t[i]<>fuori THEN

WRITELN('Posizione ',i,' Pettorale ',p[i], ' Tempo ',t[i])

ELSE

WRITELN('Pettorale ', p[i], ' fuori gara ')

END;

 

Problema 2: realizzare un tipo grandeint per interi che superano la capacità del tipo integer.

Le funzioni che si devono realizzare per gestire il tipo grandeint sono le stesse che il Pascal mette a disposizione per il tipo integer. Per quanto riguarda la struttura dati possiamo "copiare" quella utilizzata per il tipo integer e cioè utilizzare una rappresentazione binaria del numero con l'accorgimento di utilizzare molti più bit. Ad esempio potremo rappresentare il tipo grandint così:

CONST

dim=64;

TYPE

grandint=PACKED ARRAY[0..dim] of BOOLEAN;

N.B la posizione 0 del vettore serve solo a semplificare la scrittura delle procedure: i bit significativi sono 1..64.

Alla costante FALSE assoceremo 0, alla costante TRUE assoceremo 1.

La posizione 1 avrà il peso maggiore. Se il numero è rappresentato in complememto a due il bit del segno sarà il bit 1. Il bit 0 è stato inserito per semplificare la scrittura di alcune funzioni.

Vediamo alcune funzioni.

1- la procedura ZERO pone il numero a zero: cioè pone tutti gli elementi del vettore di BOOLEAN a FALSE.

PROCEDURE zero (VAR v:grandint);

VAR

i:INTEGER;

BEGIN

FOR i:=1 TO dim DO v[i]:=FALSE;

END;

2- la procedura SUCCESSIVO incrementa di uno il valore del numero: partendo dal bit meno significativo si cambiano i bit da 1 a 0 fermandosi quando si incontra un bit a 0 che viene posto a 1.

Esempio: 10110111 + 1 = 010111000

11111111 + 1 = 000000000

PROCEDURE successivo (VAR v:grandint);

VAR

i:INTEGER;

BEGIN

i:=dim;

{ finchè sono 1, ponili a 0 }

WHILE v[i] and (i > 0) DO BEGIN

v[i]:= not v[i];

i:= i-1

END;

v[i]:=TRUE; { poni a 1 }

END;

La procedura SUCESSIVO sfrutta la presenza della posizione 0 del vettore.

3- la procedura COMPLEMENTO calcola il complemento a 2 di un numero: calcola il complemento a 1 e somma 1.

PROCEDURE complemento (VAR v:grandint);

VAR

i:INTEGER;

BEGIN

FOR i:=1 TO dim DO v[i]:=not v[i];

successivo(v);

END;

4- la procedura SOMMA somma due numeri: partendo dal bit meno significativo si può notare che i casi possibili si riducono a due:

1- i bit da sommare sono uguali:

rip = 1 rip = 0 rip = 1 rip = 1

a[i]= 0 a[i]= 0 a[i]= 1 a[i]= 0

b[i]= 0 b[i]= 0 b[i]= 1 b[i]= 0

portano sempre a:

c[i]:=rip e rip:=a[i]

2- i bit da sommare sono diversi:

rip = 1 rip = 0 rip = 1 rip = 0

a[i]= 1 a[i]= 1 a[i]= 0 a[i]= 0

b[i]= 0 b[i]= 0 b[i]= 1 b[i]= 1

portano sempre a:

se rip=1 allora c[i]:=0 altrimenti c[i]:=1

rip rimane invariato

PROCEDURE somma (a,b:grandint; VAR c:grandint);

VAR

i:INTEGER;

rip:BOOLEAN;

BEGIN

rip:=FALSE;

FOR i:=dim DOWNTO 1 DO BEGIN

IF a[i] = b[i] THEN BEGIN

c[i]:= rip;

rip:= a[i]

END

ELSE c[i]:=NOT rip

END

END;

5- la procedura SOTTRAI calcola c= a - b : il calcolo viene eseguito sommando ad a il complemento a 2 di b.

PROCEDURE sottrai (a,b:grandint; VAR c:grandint);

BEGIN

complemento(b);

somma(a,b,c);

END;

6- la funzione IS_ZERO rstituisce TRUE se un numero è uguale a 0: controlla che tutti gli elementi del vettore siano FALSE.

FUNCTION is_zero (v:grandint):BOOLEAN;

VAR

i:INTEGER;

BEGIN

i:=dim;

WHILE (not v[i]) and (i>0) DO i:= i-1;

is_zero:= (i=0);

END;

7- la procedura PRODOTTO calcola il prodotto di due numeri: viene implementata tramite moltiplicazioni per due e somme successive.

PROCEDURE prodotto (a,b:grandint; VAR c:grandint);

VAR

i:INTEGER;

PROCEDURE perdue (VAR a:grandint);

VAR

i:INTEGER;

BEGIN

FOR i:= 1 TO dim-1 DO

a[i]:=a[i+1];

a[dim]:=FALSE;

END;

BEGIN

zero(c);

i:=1;

WHILE (i<=dim) and (not b[i]) DO i:=i+1;

WHILE i<=dim DO BEGIN

perdue(c);

IF b[i] THEN somma(a,c,c);

i:=i+1;

END;

END;

8- la procedura STAMPA visualizza il numero in binario.

PROCEDURE stampa (v:grandint);

VAR

i:INTEGER;

BEGIN

FOR i:=1 TO dim DO

IF v[i] THEN write('1') ELSE write('0');

END;

Come applicazione delle funzioni precedenti calcoliamo il fattoriale.

PROCEDURE fatt(v:grandint; VAR c:grandint);

VAR

un1:grandint;

BEGIN

zero(un1);

successivo(un1);

zero(c);

successivo(c);

WHILE not is_zero(v) DO BEGIN

prodotto(v,c,c);

sottrai(v,un1,v);

END;

END;

Esercizio. Implementare le seguenti funzioni e PROCEDURE definite sul tipo grandint.

1. FUNCTION minore(a:grandint; b:grandint):BOOLEAN;

2. FUNCTION maggiore(a:grandint; b:grandint):BOOLEAN;

3. FUNCTION uguale(a:grandint; b:grandint):BOOLEAN;

4. FUNCTION positivo(a:grandint):BOOLEAN;

5. FUNCTION negativo(a:grandint):BOOLEAN;

6. PROCEDURE assegna(a:grandint; VAR b:grandint);

7. PROCEDURE eleva (a:grandint; b:grandint; VAR c:grandint);

8. PROCEDURE divisodue (a:grandint; VAR b:grandint);

9. PROCEDURE dividi (a:grandint; b:grandint; VAR c:grandint);

10. Sviluppare una funzione di lettura che permetta di ricevere dall'esterno un numero grande e restituisca un grandint.

 

Problema 3: realizzare il tipo razionale.

Dalla matematica sappiamo che ogni numero razionale può essere rappresentato come rapporto tra due numeri interi primi tra loro.

Allora per la rappresentazione dei razionali abbiamo bisogno di un vettore con due elementi, il numeratore e il denominatore.

STRUTTURA DATI:

TYPE

RAZIONALE = ARRAY[1..2] OF INTEGER;

Per utilizzare i razionali abbiamo bisogno di un costruttore, cioe di una procedura che, avuto in input due numeri interi restituisce un razionale, cioè un vettore contenente un numeratore e un denominatore primi tra loro, e di due selettori, uno che restituisce il numeratore e l'altro il denominatore. Utilizzando poi queste funzioni elementari si possono costruire tutte le operazioni con i razionali.

Il costruttore dei razionali sarà:

PROCEDURE crea_raz (x:INTEGER ; y:INTEGER ; VAR r:RAZIONALE);

VAR

cd: INTEGER;

FUNCTION mcd (x: INTEGER; y: INTEGER): INTEGER;

VAR

a: INTEGER;

BEGIN

REPEAT

x:= x MOD y;

a:= x; x:= y; y:=a;

UNTIL y = 0;

mcd:= x;

END;

BEGIN { crea_raz }

IF y <> 0 THEN { il denominatore deve essere <> 0 }

IF x = 0 THEN BEGIN

r[1]:= 0; r[2]:= 1

END

ELSE BEGIN

cd := mcd(abs(x),abs(y)); { calcola il m.c.d. tra x e y }

IF y < 0 THEN BEGIN

x:= -x; y:= -y

END;

r[1]:= x DIV cd;

r[2]:= y DIV cd

END

ELSE errore( 'divisione per 0' );

END;

Il selettore num, sarà una funzione che, avuto in ingresso un razionale restituisce il numeratore.

FUNCTION num (r:RAZIONALE): INTEGER;

BEGIN

num:=r[1];

END;

Il selettore den, sarà una funzione che, avuto in ingresso un razionale restituisce il denominatore.

FUNCTION den (r:RAZIONALE): INTEGER;

BEGIN

den:=r[2];

END;

Con queste tre funzioni si possono realizzare tutte le operazioni con i razionali. Ad esempio:

PROCEDURE add_raz ( x:RAZIONALE; y:RAZIONALE; VAR z:RAZIONALE );

BEGIN

crea_raz( num(x)*den(y) + den(x)*num(y), den(x)*den(y), z );

END;

PROCEDURE sub_raz ( x:RAZIONALE; y:RAZIONALE; VAR z:RAZIONALE );

BEGIN

crea_raz( num(x)*den(y) - den(x)*num(y), den(x)*den(y), z );

END;

PROCEDURE molt_raz ( x:RAZIONALE; y:RAZIONALE; VAR z:RAZIONALE );

BEGIN

crea_raz( num(x)*num(y), den(x)*den(y), z );

END;

PROCEDURE div_raz ( x:RAZIONALE; y:RAZIONALE; VAR z:RAZIONALE );

BEGIN

crea_raz( num(x)*den(y), den(x)*num(y), z );

END;

PROCEDURE print_raz( x:RAZIONALE );

BEGIN

WRITE( num(x),'/',den(x),' ' );

END;

FUNCTION ug_raz( x:RAZIONALE; y:RAZIONALE ):BOOLEAN;

BEGIN

ug_raz:=( num(x)*den(y) = num(y)*den(x) );

END;

Esercizio 1. Definire il tipo complesso e realizzare le seguenti primitive:

1. PROCEDURE crea_complesso (a,b:REAL; VAR c:complesso);

2. FUNCTION parte_reale (a:complesso):REAL;

3. FUNCTION parte_imm (a:complesso):REAL;

4. PROCEDURE add (a,b:complesso; VAR c:complesso)

5. PROCEDURE molt (a,b:complesso; VAR c:complesso)

6. PROCEDURE sub (a,b:complesso; VAR c:complesso)

7. PROCEDURE div (a,b:complesso; VAR c:complesso

8. PROCEDURE print (a:complesso);

9. FUNCTION eguali (a,b:complesso):BOOLEAN;

10. FUNCTION is_zero (a:complesso):BOOLEAN;

Esercizio 2. Definire il tipo data e realizzare le seguenti primitive:

1. PROCEDURE crea_data(a,b,c:INTEGER; VAR d:data);

2. FUNCTION giorno(d:data):INTEGER;

3. FUNCTION mese(d:data):INTEGER;

4. FUNCTION anno(d:data):INTEGER;

5. PROCEDURE successivo(VAR d:data);

6. FUNCTION giorni_tra_due_date(a,b:data):INTEGER;

 

Problema 4: ricercare una sottostringa r in una stringa s.

RISULTATI: la posizione della prima occorrenza di r in s, 0 se non esiste.

DATI: la striga r e la stringa s STRUTTURE DATI:

CONST

dim=600;

TYPE

stringa=PACKED ARRAY[1..dim] OF CHAR;

VAR

s:stringa;

r:stringa;

supponiamo che le stringhe s ed r siano terminate dal carattere CHR(0).

ALGORITMO: si deve realizzare una funzione che, avuto in input le due stringhe, restituisca 0 se non trovato, l'indice della prima occorrenza, se trovato.

Soluzione 1.

WHILE NOT TROVATO DO BEGIN

Confronta la prima lettera di r con tutte le lettere di s:

quando si incontra una lettera uguale si confrontano anche le rimanenti lettere di r.

Se anche queste sono tutte uguali fine.

FUNCTION ricerca( VAR s,r:stringa ):INTEGER;

VAR

i:INTEGER;

trovato:BOOLEAN;

FUNCTION confronta( p:INTEGER ):BOOLEAN;

{ confronta tutti i caratteri di r con quelli di s a partire dalla posizione p }

VAR

i:INTEGER;

BEGIN

i:=2; p:=p+1;

WHILE (s[p]=r[i]) AND (r[i]<>CHR(0)) AND (s[p]<>CHR(0)) DO BEGIN

i:=i+1; p:=p+1;

END;

confronta:= (r[i]=CHR(0));

END;

BEGIN { ricerca }

i:=1; trovato:=FALSE;

WHILE (s[i]<>CHR(0)) AND NOT trovato DO BEGIN

IF s[i]=r[1] THEN { se uguali }

trovato:=confronta( i ); { controlla le rimanenti }

IF not trovato THEN i:=i+1;

END;

IF trovato THEN ricerca:=i ELSE ricerca:=0;

END;

Soluzione 2.

Nell'algoritmo presentato in precedenza l'indice i viene sempre incrementato di 1. Ma nel 1976 due ricercatori dell'università di Austin, nel Texas hanno trovato un metodo che consente incrementi maggiori di i.

Sia

s: l'uomo vive ogni cosa subito per la prima volta, senza preparazione.

r: cosa

Supponiamo di avere a disposizione una tabella contenente per ogni lettera la distanza dall'ultimo carattere della parola da ricercare: per i caratteri non presenti in r e per l'ultimo di r ci sarà la lunghezza della parola:

a ---- 4

b ---- 4

c ---- 3

d ---- 4

........

........

o ---- 2

........

s ---- 1

Il confronto avviene così:

L'uomo vive ogni cosa subito per la prima volta, senza preparazione.

cosa

Si confronta a partire dall'ultimo carattere di r. Se il corrispondente è diverso si sposta r del valore indicato nella tabella in corrispondenza del carattere di s. Nell'esempio si cerca la lettera o.

L'uomo vive ogni cosa subito per la prima volta, senza preparazione.

cosa

cosa

cosa

cosa

cosa

cosa

cosa

Nel caso in cui le lettere sono uguali si passa ad esaminare la penultima lettera, ripetendo il ragionamento descritto in precedenza.

L'algoritmo completo sarà:

FUNCTION len( VAR x:stringa ):INTEGER;

{ calcola la lunghezza di una stringa }

VAR

i:INTEGER;

BEGIN

i:=1;

WHILE x[i]<>CHR(0) DO i:=i+1;

len:=i-1;

END;

FUNCTION ricerca( VAR s, r:stringa ):INTEGER;

TYPE

tab=ARRAY[ CHAR ] OF INTEGER;

VAR

t :tab; { tabella per il confronto }

c :CHAR;

ls, lr,

is, ir :INTEGER;

trovato:BOOLEAN;

BEGIN

lr:=len( r );

FOR c:=CHR(0) TO CHR(255) DO

t[c]:=lr; { inizializzazione della tabella }

FOR ir:=1 TO lr-1 DO

t[ r[ir] ]:=lr-ir; { distanza delle singole lettere dal fondo }

is:=0; trovato:=FALSE;

WHILE (is<=ls-lr) AND NOT trovato DO BEGIN

ir:=lr;

WHILE (ir>0) AND (s[is+ir]=r[ir]) DO

ir:=ir-1;

IF ir=0 THEN trovato:=TRUE

ELSE is:=is+t[ s[is+ir] ];

END;

F trovato THEN ricerca:=is+1 ELSE ricerca:=0;

END;

 

Problema 5: determinare tutti i numeri primi minori di n.

DATI: n

RISULTATO: vettore di numeri primi compresi tra 1 e n

ALGORITMO:

Un numero T è primo se non ammette divisori tra i numeri primi compresi fra 2 e la parte intera della radice quadrata di T.

L'algoritmo diventa:

per ogni numero dispari compreso tra 2 e N

controlla se è primo

se è primo allora inseriscilo nel vettore

PROGRAM num_primi (INPUT,OUTPUT);

CONST

dim=100;

TYPE

vet = ARRAY[1..dim] OF INTEGER;

VAR

n,m:INTEGER;

primi: vet;

PROCEDURE calc_primi (VAR v:vet;max,dimens:INTEGER;VAR num_valori:INTEGER);

VAR

k,j,h:INTEGER;

primo:BOOLEAN;

BEGIN

num_valori:=1;

v[num_valori]:=2;

k:=3;

WHILE (k <= max) AND (num_valori < dimens) DO BEGIN

j:=1; primo:=TRUE;

h:=trunc(sqrt(k));

WHILE primo AND (h >= v[j]) DO BEGIN

primo:=((k mod v[j]) <> 0);

j:= j+1

END;

IF primo THEN BEGIN

num_valori:=num_valori + 1;

v[num_valori]:=k

END;

k:= k + 2;

END

END;

BEGIN

REPEAT

WRITE('Inserisci il valore massimo da valutare ');

READLN(m)

UNTIL m > 2;

calc_primi(primi,m,dim,n);

FOR m:=1 TO n DO WRITE(primi[m]:6);

WRITELN;

END.

Problema 6: dato un numero intero n dire se è primo.

DATI: n

RISULTATI: "primo", "non primo"

ALGORITMO

 

Vogliamo quì presentare un algoritmo probabilistico per determinare se un numero è primo. Il punto di partenza è il teorema di Fermat:

se N è primo e A è un naturale < N allora

A^N mod N = A

Quindi:

se A^N mod N <> A allora N non è primo

se A^N mod N = A , con A scelto a caso ,allora ci sono " buone probabilità" che N sia primo; tali probabilità aumentano se ciò si verifica per più valori casuali di A.

L'algoritmo consiste quindi nel ripetere un certo numero di volte il test di Fermat: la funzione fast_prime svolge questo compito.

La funzione fermat_test genera semplcemente un numero casuale e verifica la condizione di uguaglianza imposta dal test.

A questo punto abbiamo il problema di calcolare A^N mod N. Se procediamo con il calcolo di A^N dovremo affrontare problemi notevoli: ad esempio supponiamo di voler sapere se 1000 è un numero primo: se il valore casuale generato è 100 dovremmo calcolare 100^1000. Il risultato non è rappresentabile con il tipo integer. Ci viene in aiuto una semplice proprietà aritmetica:

(x * y) mod n = ((x mod n) * (y mod n)) mod n

Infatti siano

r1= x mod n r2= y mod n

allora

x = q1 * n + r1 y = q2 * n + r2

x * y = (q1*n+r1)*(q2*n+r2) = (q1*q2*n + q1*r2 + q2*r1) * n + r1*r2

(x * y) mod n = (r1*r2) mod n

E` evidente quindi anche

x^2 mod n = (x mod n)^2 mod n

Ricordando che:

b^e = 1 se e = 0

(b^e/2)^2 se e è pari

(b * b^(e-1) se e è dispari

ricaviamo:

(b^e)mod n = 1 se e = 0

(b^e/2 mod n)^2 mod n se e è pari

((b mod n) * (b^(e-1) mod n)) mod n se e è dispari

Si noti che:

1- Nel notro caso b mod n = b poichè si sceglie b < n;

2- Il numero maggiore che compare è minore di n^2;

PROGRAM fermat(INPUT,OUTPUT);

VAR

n :INTEGER;

primo :BOOLEAN;

FUNCTION expmod(b,e,m:INTEGER):INTEGER;

BEGIN

IF (e = 0) THEN expmod:=1

ELSE IF (e mod 2) = 0 THEN expmod:=sqr(expmod(b,e div 2,m)) mod m

ELSE expmod:=(b * expmod(b,e-1,m)) mod m

END;

FUNCTION fermat_test (n:INTEGER):BOOLEAN;

VAR

a: INTEGER;

BEGIN

a:= random(n-2) + 2;

fermat_test:=(expmod(a,n,n) = a)

END;

FUNCTION fast_prime(n,volte:INTEGER):BOOLEAN;

BEGIN

IF volte = 0 THEN fast_prime:=TRUE

ELSE IF fermat_test(n) THEN fast_prime:=fast_prime(n,volte-1)

ELSE fast_prime:=FALSE

END;

BEGIN

REPEAT

READLN(n)

UNTIL n > 2;

primo:=fast_prime(n,5);

IF primo THEN WRITELN(' primo ')

ELSE WRITELN(' non primo ')

END.

Quando questo algoritmo dice che il numero non è primo il risultato è valido, ma quando dice che è primo lo afferma associando ad esso una probabilità.

Si tenga presente inoltre che esistono particolari numeri che eludono il test di Fermat per quante volte esso venga ripetuto: ci sono dei numeri N non primi tali che

a^n mod n = a per tutti gli a < n

Esempi di questi numeri (non primi) sono:

561, 1105, 1729, 2465, 2821

La probabilità di errore è minore di 1/2^m dove m è il numero di volte che viene eseguito il test di Fermat.

Questo algoritmo è molto efficiente e molto usato: si tenga però presente che in Pascal, il fatto che gli INTEGER siano definiti a 16 o 32 bit lo rende così come esposto praticamente inutile.

Questo algoritmo ha dimostrato, ad esempio, che 2^400 - 593 è (probabilmente) primo: tale numero non è rappresentabile in Pascal col tipo INTEGER.

Problema 7: siano dati tre pioli e N dischi. I dischi possono essere impilati sui pioli in modo da formare delle torri. Sia N il numero dei dischi inizialmente posti sul piolo A, in ordine di dimensione decrescente. Il problema consiste nello spostare N dischi dal piolo A al piolo C in modo che siano disposti secondo l'ordine originale. Vengono posti i seguenti vincoli:

1- ad ogni passo si sposta un solo disco

2- un disco non può essere messo sopra un disco più piccolo

3- può essere usato solo un terzo piolo come deposito ausiliario

(Il problema è noto come il problema della " torre di Hanoi " )

Algoritmo per spostare una torre di altezza N da A a C servendosi di B

se N=1 allora spostare l'unico disco da A a C

altrimenti

1- spostare una torre di N-1 dischi da A a B servendosi di C

2- portare il disco da A a C

3- spostare una torre di N-1 dischi da B a C servendosi di A

PROCEDURE Hanoi(n:INTEGER;sorg,dest,interm:CHAR);

BEGIN

IF n=1 THEN WRITELN(n,' da ',sorg,' a ',dest)

ELSE BEGIN

hanoi(n-1,sorg,interm,dest);

WRITELN(n,' da ',sorg,' a ',dest);

hanoi(n-1,interm,dest,sorg);

END

END;