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.
Ricercare una sottostringa r in una stringa s.
Determinare tutti i numeri primi minori di n.
Dato un numero intero n dire se è primo.
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;
: 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.
: 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;
: 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;
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.
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.
: 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;