2a Prova parziale di Programmazione 26 Gennaio 2003 TESTO e

2a Prova parziale di Programmazione
26 Gennaio 2003
TESTO e RISPOSTE
Attenzione questa è una delle varianti del compito
Esercizio 1 Comprensione codice C (punti 6 in prima approssimazione)
Consideriamo il seguente codice C
#include <stdio.h>
int k = 13;
/* i compiti differivano qui
void p1(void) ;
void p2(int) ;
void p3(int, int *) ;
void p4(void) ;
main() {
int j;
int n = 7 ;
/*
p1 ( ) ;
printf ("main 1:
p2 ( n ) ;
printf ("main 2:
j = 1 ;
p3 (n, &j) ;
printf ("main 3:
p4 ( ) ;
}
void p1(void)
{
void p2(int x) {
i compiti differivano qui
*/
*/
%d \n", k ) ;
%d \n", n ) ;
%d
%d \n", j , n ) ;
k++ ;
x++ ;
printf ("p1:
printf ("p2:
%d \n", k ) ;
%d \n", k+x ) ;
}
}
void p3(int x, int * y)
{
int aux = x;
x = *y;
*y = aux;
printf ("p3: %d %d \n", x , *y ) ;
}
int q(int) ;
void p4(void)
{
int k = 3 ;
printf ( "p4:
%d \n", q(k) ) ;
}
int q(int w) { return (k+w); }
Sul foglio risposte
scrivere l'output del programma;
tutto l'output ! (l'unica cosa che potete ignorare sono gli spazi bianchi)
1
Risposta
p1:
( Commenti NON richiesti )
14
main 1:
p2:
14
22
main 2:
( la variazione di k e` vista dal main )
( = 14 + 8 )
7
( la variazione di n fatta da p2 non viene vista
nel main perche` n e` stato passato per valore )
p3: 1
7
main 3: 7
p4:
( dentro p3 i valori vengono scambiati )
7
17
( all'uscita pero` n ritorna al valore precedente)
( il k che compare in q e` quello dichiarato
all'inizio e vale 14 )
Esercizio 2 Progetto di algoritmo (punti 16 in prima approssimazione)
Poker semplificato
Carte (in ordine di valore crescente): 7, 8, 9, 10, Fante, Donna, Re, Asso;
per ogni valore ci sono 4 carte (di "seme" diverso: cuori, quadri, picche, fiori; ma in quello
che segue il seme non conta); per un totale di 32 carte. Ogni giocatore riceve 5 carte.
Combinazioni interessanti:
 coppia: 2 carte di ugual valore (ma di seme diverso)
 tris:
3 carte di ugual valore (ma di seme diverso)
 poker:
4 carte di ugual valore (ma di seme diverso)
 coppia < tris < poker
 a parità di combinazione vince quella con carte di valore più alto; a parità di
valore, abbiamo "partita pari";
 se un giocatore ha 2 coppie, si considera solo quella con carte di valore piu alto; se
ha un tris ed una coppia, si considera solo il tris.
Si tratta di progettare un algoritmo per questo gioco. Più precisamente:
 due soli giocatori: A e B;
 il "mazzo di carte" è rappresentato da un file di 32 righe; quindi ogni riga
rappresenta una carta (come, fa parte dell'esercizio); supponiamo che il file sia
corretto e rappresenti un mazzo già "mescolato";
 l'algoritmo, per prima cosa, serve le carte: la prima carta del mazzo ad A, la
seconda a B, la terza ad A, la quarta a B,...., fino a che ciascun giocatore ha 5 carte;
 a questo punto, l'algoritmo controlla cosa hanno in mano i due giocatori e decide
chi ha vinto (oppure se c'è parità).
L'algoritmo deve essere strutturato come segue:
 il "main" apre il file
 poi per servire le carte chiama una procedura servi
 poi per capire cosa ha in mano A chiama una procedura/funzione conta
 poi per capire cosa ha in mano B chiama nuovamente conta
 in base alle info ricevute dalle due chiamate, il main decide chi ha vinto.
2
Non si possono usare "variabili globali". I passaggi di dati tra main e procedure e
funzioni devono avvenire tramite parametri e il risultato delle funzioni.
Si possono usare procedure / funzioni ausiliarie.
Domande
a) Come si rappresentano le carte nel file ?
Se non trovo la risposta non correggo il resto.
b) Come si rappresenta il gruppo di 5 carte in mano a ciascun
Se non trovo la risposta non correggo il resto.
giocatore ?
c) Come si codificano le combinazioni; ad es. come si codifica "A ha un tris di fanti " ?
Se non trovo la risposta non correggo il resto.
d) Per la procedura servi: dire quali sono i parametri e dare un'idea di come funziona (a
parole o in pseudo-codice ad alto livello).
Se non trovo la risposta non correggo il codice di servi.
e) La stessa cosa per conta.
f) Scrivere, usando lo pseudo-codice utilizzato nelle dispense, oppure il C, l'algoritmo
completo, commentando, quando necessario.
A voce ho detto che: dato il tipo di esercizio, bisognava pensare bene alla parte di
"progetto" (es domande b e c) anche a scapito del dettaglio nellarisposta alla domanda f).
Risposte (ovviamente qui ci sono alternative ....)
a) visto quello che dobbiamo fare, un modo semplice e` di usare dei numeri:
7, 8, 9, 10, 11 (Fante), 12 (Donna), 13 (Re), 14 (Asso)
b) per semplificarci la vita in quello che segue, un modo e` : al giocatore A associo
aa : array [ 7 .. 14 ] of integer
aa [ k ] = x
con l'idea che dopo aver servito le carte
sse A ha ricevuto x carte di valore k
Analogamente per B
Nota: sia in a) che in b) sarebbe piu' elegante usare dei tipi enumerazione
type Carte = {SETTE, OTTO, NOVE, DIECI, FANTE, DONNA, RE, ASSO}
type Combinazioni = { NULLA, COPPIA, TRIS, POKER }
eccetera ...............
c) per facilitare il confronto (nella parte finale dell'algoritmo), possiamo rappresentare il
punteggio di un giocatore con una coppia, cioè un record a due campi interi :
type risultato = record { combi, carta : integer }
3
il campo combi ha 4 possibili valori : 2 (coppia), 3 (tris), 4 (poker), 0 (altrimenti)
il campo carta ha valore : 7, 8, 9, ...14
ad A associo il record ra, a B il record rb
quindi
ra = [ 3 , 14 ] vuol dire che A ha un tris d'assi
d) la procedura servi:

ha 3 parametri :

la procedura è molto semplice: legge dal file un numero alla volta e aggiorna,
alternando, i due array ..............
il file "mazzo" , parametro IN
due parametri OUT che sono array [ 7 .. 14 ] of integer
e) conta:

e` abbasatanza naturale usare una funzione:
function conta ( x : array [ 7 .. 14 ] of integer ) : risultato

si usa una variabile ausiliaria res di tipo risultato con i due campi inizializzati a 0

si legge l'array x con un ciclo per k da 7 a 14
se x[ k ] è minore di 2 (nemmeno una coppia di valore k) non si fa niente,
altrimenti : si confronta x[ k ] col valore corrente di res.combi:
se x[ k ] < res. combi non si fa niente
se x[ k ] > res. combi allora res. combi  x[ k ]
se x[ k ] = res. combi allora res.carta  k

alla fine la funzione restituisce res
f) Algoritmo completo in pseudo-codice (senza commenti, perche` mi sembra che dopo
le spiegazioni di sopra sia tutto chiaro ...)
dichiarazioni
type risultato = record { combi, carta : integer }
procedura servi ( fmazzo : file .... IN a , b : array [ 7 .. 14 ] of integer OUT )
per i dettagli vedere sotto
function conta ( x : array [ 7 .. 14 ] of integer ) : risultato
per i dettagli vedere sotto
variabili:
ra, rb : risultato
aa, bb : array [ 7 .. 14 ] of integer
mazzo : file
4
istruzioni
apri il file mazzo in lettura
if errore nell'aprire il file
then scrivi ( "errore" )
else {
servi (mazzo, aa, bb)
chiudi mazzo
ra  conta(aa)
rb  conta(bb)
5
if ra.comb < rb. comb then scrivi ( "vince B ")
else
if ra.comb > rb. comb then scrivi ( vince A ")
else
[ quindi a parita` di combinazione ]
if ra.carta < rb. carta then scrivi ( "vince B ")
else
if ra.carta > rb. carta then scrivi ( "vince A ")
else scrivi ( " partita pari ")
procedura servi ( fmazzo : file .... IN
a , b : array [ 7 .. 14 ] of integer OUT )
{
variabili
k , carta : integer
a[ k ]  0
per k = 7, 8, ..., 14 :
per k = 1, 2,... 5 :
{
ed anche b[ k ]  0
leggi (fmazzo, carta)
a [ carta ] ++
leggi (fmazzo, carta)
b [ carta ] ++
}
}
function conta ( x : array [ 7 .. 14 ] of integer ) : risultato
{
variabili: k intera, res di tipo risultato
res.comb  0 ;
res.carta  0
per k = 7, 8, 9, ..., 14:
if x[k] > 1 then
if x[k] > res.comb
then
{ res.comb  x[k];
res.carta  k }
else
if x[k] = res.comb then res.carta  k
return (res);
}
/*
Programma per il poker in C
con pochi commenti
dato che quasi tutto e` stato spiegato prima
il programma contiene delle istruzioni di output "non richieste"
6
che pero` sono comode per verificare che funziona .....
si usano numeri per codificare le carte e le combinazioni
un modo piu' elegante di fare le cose e` usare le enumerazioni
*/
#include <stdio.h>
#define
CARTE
8
typedef struct { int comb;
int carta ; } risultato ;
void servi ( FILE * ,
int a[ ], int b[ ] ) ;
risultato conta ( int x [ ] );
main()
{
risultato ra, rb ;
int aa[ CARTE ] ;
/* aa [ j- 7 ] == quante carte di valore j
int bb[ CARTE ]
FILE
mazzo
;
possiede A
*/
/* idem per B */
* mazzo ;
= fopen("mazzo","r");
if (mazzo == NULL )
else
{
printf ( "ERRORE DATI" );
servi (mazzo, aa, bb) ;
fclose (mazzo) ;
ra = conta(aa);
rb = conta(bb);
printf("\n %d %d", ra.comb, ra.carta);
printf("\n %d %d", rb.comb, rb.carta);
if (ra.comb < rb. comb) printf("\n vince B \n");
else if (ra.comb > rb. comb) printf("\n vince A \n");
else if (ra.carta < rb. carta) printf("\n vince B \n");
else if (ra.carta > rb. carta)
printf("\n vince A \n");
else printf("\n partita pari \n");
}
}
void servi ( FILE * fmazzo,
int a[ ], int b[ ] ) {
int k , carta;
for (k=0; k < CARTE; k++) a[ k ] = 0;
for (k=0; k < CARTE; k++) b[ k ] = 0;
for ( k = 1 ; k <= 5 ; k++ ) {
fscanf (fmazzo, "%d", &carta) ;
a[carta - 7]++;
fscanf (fmazzo, "%d", &carta) ;
b[carta - 7]++;
}
7
printf("\n");
for (k=0; k < CARTE; k++)
printf("\n");
for (k=0; k < CARTE; k++)
printf("\n");
for (k=0; k < CARTE; k++)
printf("%d
", k+7);
printf("%d
", a[ k ] );
printf("%d
", b[ k ] );
}
risultato conta ( int x[ ] ) {
int k;
risultato res;
res.comb = 0; res.carta = 0;
for (k = 0; k < CARTE ; k++)
if ( x[k] > 1 )
if ( x[k] > res.comb )
{ res.comb = x[k];
res.carta = k+7;
}
else if ( x[k] == res.comb )
res.carta = k+7;
return (res);
}
Esercizio 3 Complessità (punti 8 in prima approssimazione)
L'algoritmo che segue simula un torneo ad "eliminazione diretta"; la procedura elimina,
ad ogni passo, simula una fase di eliminazione.
Per semplicità supponiamo che i concorrenti siano n, con n che è potenza di 2; inoltre
supponiamo che i concorrenti siano numeri interi diversi; tra due vince il maggiore.
Algoritmo:
const MAX = .............
var:
n, k, j : int
concorrenti : array [1 .. MAX ] of int
leggi ( n )
qui si suppone n ≤ MAX
per k = 1, 2, 3, .... n : leggi (concorrenti[ k ] )
j  n div 2
divisione intera
while ( j > 0 )
{
elimina ( concorrenti, j )
j  j div 2
}
8
stampa ( concorrenti [1])
ecco il vincitore !
=======================
procedura elimina ( aa : array [1 .. MAX ] of int IN-OUT , sup : int IN ) ;
{
qui si suppone sup ≤ MAX
var i : int ;
per i = 1, 2, 3 ..., sup:
if
aa[ 2*i - 1 ] < aa[ 2*i ]
then
aa[ i ]  aa[ 2*i ]
else
aa[ i ]  aa[ 2*i - 1 ]
}
Domande:
a) Calcolare la complessità della chiamata: elimina ( concorrenti, j )
in funzione di j
b) Calcolare la complessità dell'algoritmo in funzione di n
Possibilmente dare stime in ( ...).
Non fare conti troppo dettagliati, ma non limitarsi a dare il risultato o poco più;
un risultato giusto, ma non motivato, non verrà contato.....
Domande:
a) Indichiamo con TT ( j )
funzione di j .
la complessità della chiamata: elimina ( concorrenti, j ), in
I costi sono tutti costanti (passaggio dei parametri, valutazione delle espressioni,
esecuzione dell' istruzione if-then-else); il costo totale dipende solo da quante volte si
esegue l' if-then-else (cioè quante volte si ripete il ciclo for).
Quindi
TT ( j ) = a j + b (con a, b costanti oppertune e a> 0)
dunque
TT ( j ) è in ( j ).
b) Indichiamo con T ( n )
la complessità dell'algoritmo, in funzione di n.
I costi sono tutti costanti, tranne che quello delle istruzioni per
Il costo di
per k = 1 ... n : ....
e while
è ovviamente lineare in n
Il while si ripete per j = n/2, n/4, ..., n/(2i), ..., n/n
il costo del while è dunque dato dalla sommatoria (c, d costanti, c > 0 )
 (j = n/2, ....) (c j + d) =
c  (j = n/2, ....) j +  (j = n/2, ....) d
La seconda sommatoria ha per valore circa d( log2 n ) infatti gli j sono circa log2 n
9
La prima è
c (n/2 + n/4 + ..... ) =
c n ( 1/2 + 1/4 + 1/8 + ..... 1/n) =
c n (1 - 1/n)
[ vedere (*) sotto ]
quindi è circa c n
Sommando tutto, il termine dominante e` lineare in n
quindi
(*)
T ( n ) è in ( n ).
notare che :
1/2 + 1/4 = 1 - 1/4
1/2 + 1/4 + 1/8 = 1 - 1/8
1/2 + 1/4 + 1/8 + 1/16 = 1 - 1/16
eccetera
questo si vede ancora meglio disegnando una "torta"
10