AngoLinux

Calcolo dell'entropia di coppie di caratteri letti da standard input e stampa le 10 più frequenti

- A cura del Prof. Stefano Salvi -


Ancora i ragazzi conoscono i vettori ma non le funzioni, quindi il programma viene scritto tutto nel main.

Il testo dell'esercizio è il seguente:

Scrivere un programma C che calcoli l'entropia dei dati letti da statndard input. I dati dovranno essere terminati da un EOF (ctrl-d). Si dovrà calcolare l'entropia di coppie di caratteri, quindi i caratteri andranno letti a due a due e poi composti in un unico codice a 16 bit, del quale calcoleremo l'entropia.
Il programma dovrà stampare:
  • Il numero di simboli (coppie di caratteri) letti
  • I 10 simboli più frequenti
  • L'entropia calcolata per il messaggio letto
  • La dimensione ottima teorica del messaggio, in bit, espressa come <entropia> * <no simboli letti>
  • La dimensione ottima teorica del messaggio, in byte, espressa come <entropia> * <no simboli letti> / 8
Note:
  • Per calcolare le frequenze relative, creare un vetore di interi della dimensione del numero di possibili codici diversi letti (i codici sono ad 16 bit, quindi avremo 216 = 65536 codici diversi al massimo) ed incrementare l'elemento dell'array il cui indice è il codice letto.
  • La funzione getchar () legge un carattere, ma ritorna un tipo intero. EOF non è un carattere, ma il numero intero -1. Inserire il risultato di getchar () in un int, per poter fare il confronto con EOF e poi verificare di utilizzare solo i caratteri (nel campo da 0 a 255) come indici del vettore
  • Per comporre due codici ad 8 bit in uno a 16 si può sommare il primo codice letto con il secondo moltiplicato per 256.
  • Fare attenzione che la divisione di due numeri interi è un numero intero, quindi, per ottenere delle Ps attendibili occorrerà convertire il primo operando in float prima della divisione, per forzare una sivisione in virgola mobile.
  • Fare attenzione che il logaritmo di 0 non è definito, quindi non cercare di calcolare log (Ps) se la frequenza relativa è 0, pena un errore matematico
  • Per trovare i 10 simboli piu' frequenti:
    • predisporre un array che possa contenere i 10 codici
    • Calcolare per ognuno il massimo dell'array delle frequenze, con il solito algoritmo
    • Dopo ogni calcolo di massimo, azzerare la frequenza dell'elemento con frequenza massima in modo da trovare come massimo il successivo nell'ordine al ciclo successivo
  • La funzione log10() non fà parte della libreria standard del C, ma della libreria matematica. Per usarla, con cc occorre aggiungere l'opzione -lm

Una possibile soluzione è la seguente:
// Tizio - Pinco - 3AIN
// Calcola l'entropia di una stringa considerando coppie di caratteri

#include <stdio.h>  // Contiene le dichiarazioni delle funzioni standard
#include <math.h>   // Contiene la definizione della funzione log10

#define DIM (256*256)	/* Numero di combinazioni (due caratteri) */


main()
{
int i,j;	// Contatori
int max;	// Codice con il conteggio massimo
int vet[10];	// I 10 codici piu' frequenti
float ps;	// Probabilita' di sorgente
float q=0;	// Entropia
float it;	// Informazione totale (in bit)
float itb;	// Informazione Totale (in byte)
int a,b;	// Codici letti, da comporre
int c;		// Codice composto da due caratteri
int cc;		// Contatore complessivo dei caratteri
int ca[DIM];	// Array delle frequenze relative

	
  cc=0;		// Inizializza il conteggio totale

  // Inizializza il vettore dei conteggi parziali
  for (i=0;i<DIM;i++)
  {
    ca[i]=0;
  }

  printf("Caratteri : ");
  do {	// Ciclo su tutti i caratteri letti
    a=getchar();	// Legge il primo carattere della coppia		
    b=getchar();	// Legge il secondo carattere della coppia
    c=b*256+a;		// Compone i due caratteri in un codice a 16 bit
    if(a!=EOF && b!=EOF) // Se non ha finito il messaggio
    {      
      ca[c]++;		// Conta la coppia di carattere (nel contatore del sio codice)
      cc++;		// Conta la coppia di carattere (nel totale)
    }
  }while(a!=EOF && b!=EOF); // Ripete fino a che l'input non e' terminato

  // Calcolo l'entropia	
  for(i=0;i<DIM;i++)	// Per ogni codice possibile
  {		
    if (ca[i]!=0)
    {
      ps=(float)ca[i]/(float)cc;	// Calcolo la probabilita' di sorgente
      // Aggiungo il contributo del codice all'entropia (con segno -, subito)
      q-=(ps * (log(ps)/log(2)));
    }	
  }

  // Individuo i 10 codici piu' frequenti
  for(i=0;i<10;i++)	// Per ognulo degli elementi
  { 
    // Calcolo il massimo
    max=0;	// Codice con conteggio massimo, inizialmente il primo
    for(j=1;j<DIM;j++)	// Ciclo su tutti i codici
    {
      if (ca[j]>ca[max]) // Se il codice corrente ha conteggio maggiore
			 // di quello con il 'massimo corrente'
      {
        max=j;		// Accetta il codice corrente come nuovo massimo
      }
    }
    vet[i]=max;		// Immagazzina il codice con conteggio massimo
    ca[max]=0;		// Azzera il conteggio di quel codice, per trovare il successivo
  }
	
  it=q*cc;	// Calcolo l'informazione totale in bit
  itb=it/8;	// Calcolo l'informazione totale in byte

  // Stampa finale dei risltati
  printf("Sono stati inseriti %d caratteri\n",cc);
  printf("Le 10 coppie di caratteri più frequenti sono :\n"); 
  for(i=0;i<10;i++)
  {
    printf("'%c%c'   ",vet[i]/256,vet[i]%256);
  }
  printf("\nL'entropia e' : %f\n",q);
  printf("L'informazione tot e' : %f bit\n",it);
  printf("L'informazione tot e' : %f byte\n\n",itb);
}

Per provare il programma, scaricare il sorgente, compilarlo con il comando cc entropia1.c -lm ed eseguirlo con il comando ./a.out.


[Home Page dell'ITIS "Fermi"] [Indice Terza] [Precedente] [Successivo]

© Ing. Stefano Salvi - Released under GPL licence