giovedì 9 maggio 2013

Generare numeri random con scheda audio


Se tutti i numeri che vengono generati da un processo sono equamente probabili in ogni istante, allora questi numeri sono casuali. Gli utilizzi dei numeri casuali sono molteplici simulazioni,campionatura,analisi numerica,crittografia, programmazione e ad altri ancora. Generare via software numeri casuali con un algoritmo di lunghezza finita non è possibile, per cui sono nati vari algoritmi per generare sequenze numeriche random "pseudocasuali", uno dei primi è il "midlle square" inventato nel 1946 da John von Neumann. Funziona in questo modo, supponiamo di volere generare un numero casuale di 4 cifre, ossia un numero tra 0000 e 9999. Il metodo middle-square richiede come tutti i generatori di numeri pseudocasuali un valore iniziale, detto "seme" dal quale vengono generati i valori successivi. Ad esempio a partire da 1234, elevando tale numero al quadrato abbiamo le otto cifre 01522756 delle quali teniamo solamente le quattro cifre di mezzo 5227. Da queste ripetendo il procedimento otteniamo 27321529 e quindi 3215 e cosi via. Ovviamente la sequenza inizierà a ripetersi una volta che si riavrà il "seme", la lunghezza totale della sequenza fino a riavere lo stesso seme è chiamata "periodo". Per generare sequenze random senza periodo, si devono sfruttare fenomeni fisici non prevedibili come , ad esempio il "rumore" dei dispositivi elettronici. Questo tipo di segnale  generalmente ha un andamento nel tempo non descrivibile in maniera analitica, è quindi possibile studiarlo solo in termini statistici. L'idea di base era di sfruttare il rumore generato dal microfono del pc per generare sequenze di bit, e sottoporle a qualche test statistico per verificare se effettivamente la sequenze ottenute erano da considerarsi casuali. Prima di procedere, ho fatto qualche considerazione :

1)Il microfono serve per registrare segnali audio, quindi frequenze fra 20Hz e 20kHz, e taglia frequenze più alte e più basse.
2)Campionando a 8000Hz non potrei comunque analizzare frequenze superiori alla frequenza di Nyquist ovvero superiori a 4000Hz.
3)Visto che per una ricostruzione "leggibile" bisogna campionare almeno a 4 volte la frequenza analizzata, il segnale audio "valido" deve avere al massimo la frequenza di 1000Hz, segnali di frequenza superiore o sono segnali ricostruiti con errori notevoli o sono disturbi.
4)Abbassando al minimo la sensibilità del microfono amplificherei il "rumore" rispetto al segnale utile,e scegliendo di considerare frequenze di 1333Hz(con periodo pari a 6 volte il tempo di campionamento a 8000Hz) potrei aspettarmi che i valori letti siano dovuti al "rumore" e quindi casuali.


Fatte le dovute considerazioni ho scritto questo semplice programmino per linux in c++:


#include <wx/init.h>
#include <wx/string.h>
#include<math.h>


float runtest(float n1,float n0){//calcolo valore runs-test
  FILE *fr;
  char x,x1;
  float valoreatteso=1+2*((n1*n0)/(n1+n0));
  float varianzaattesa=((valoreatteso-1)*(valoreatteso-2))/(n1+n0-1);
  varianzaattesa=sqrt(varianzaattesa);
  fr=fopen("rand-mic.txt", "r");
  float contatore=1;
  for(int y=0;y<1023;y++){
    fseek(fr , y ,0);
x=fgetc(fr);
    x1=fgetc(fr);
    if(x1!=x)
contatore++;}
  float scarto= abs(contatore-valoreatteso);
  float diffscarto=2*varianzaattesa-scarto;//con 2 la prob. è 0,95
  fclose(fr);
  return (diffscarto);
}



int main( )
{
  FILE *fd;
  FILE *fw;
  int x, x1,x2;
  int n1=0;
  int n0=0;
  
  //registra 2 secondi a 8bit 8000hz
  system("arecord -d 2 rand-mic.wav");
  //apre il file per il controllo dei bit
  fd=fopen("rand-mic.wav", "r");
  //apre il file che conterrà la stringa casuale 1024 0/1
  fw=fopen("rand-mic.txt", "w");
  fseek(fd , 44,0);//salta i primi 44 byte intestazione file wav
  while(!feof(fd)){
   x=fgetc(fd);
   x1=fgetc(fd);
   x2=fgetc(fd);
   if((x1-x)>0 && (x1-x2)>0){
   fputc('1',fw);
    n1++;}
   if((x1-x)<0 && (x1-x2)<0){
   fputc('0',fw);
   n0++;}
  if(n1+n0==1024)// esce quando la sequenza è lunga 1024
  break;
}

  printf("numero segni 1 %d", n1);
  printf("numero segni 0 %d", n0);

 // monobit test n1+n0>100 e ind>0.05 per passare test
 float ind=abs(n1-n0)/sqrt(n1+n0);
 ind=ind/sqrt(2);
 fclose(fd);
 fclose(fw);
 //chiama funzione controllo run test se ind1>0 prob=0.95
 float ind1=runtest((float)n1,(float)n0); 
 printf("monobit test =  %f", ind);
 printf("runs test =  %f", ind1);
return 0;
}


Il programma scrive in un file di testo una sequenza di 1024 segni 1/0 e poi sottopone l'intera sequenza a 2 test per valutarne la casualità. Il primo test è il monobit-test che valuta il numero di 1/0, questo test da come risultato un valore numerico che se è superiore a 0,05 considera la sequenza casuale. Il secondo è il runs-test che valuta la frequenza delle variazioni 1/0 e 0/1(per approfondire leggere qui http://it.wikipedia.org/wiki/Test_dei_run) questo test da come risultato la differenza fra 2 volte lo scarto atteso e il valore assoluto dello scarto rilevato, se è maggiore di 0 significa che il 95% delle sequenze casuali rientrano in questo range.

Nessun commento:

Posta un commento