[C++] Approssimazione di $pi$

Landau1
Salve a tutti, sono alle prese con un nuovo problema di informatica: l'approssimazione di $pi$ con un metodo Monte Carlo.

Allora, l'esercizio si dovrebbe articolare in quattro step. Quello che mi crea più problemi è il primo:

1) Definire una funzione float misuraPi(int nPunti, float a); che, presi in ingresso il numero di punti da utilizzare e il lato a del quadrato centrato nell’origine che include la circonferenza di raggio unitario, restituisca una misura di $pi$.

Infatti non saprei dove mettere le mani per scrivere questa funzione. L'idea sarebbe considerare un generatore di numeri casuali nel quadrato in modo tale che le loro coordinate siano o dentro o fuori la circonferenza unitaria. Da quello che mi ricordo poi il rapporto \(\displaystyle \text{punti nella circonferenza}/\text{punti totali} \) dovrebbe essere un'approssimazione di $pi$. Quindi pensavo di fare così: con un ciclo for, genero una coppia di numeri casuali $(x, y)$ per ogni punto in input, imponendo la condizione $-a/2
Questo è quello che mi è uscito:

float misurePi(int nPunti, float a){

	float x, y;
	int centri=0;
	for(unsigned int j=0; j<nPunti; j++){
		x=randUnif(-a/2,a/2);
		y=randUnif(-a/2,a/2);
		
		if(distO(x,y)<=1){
			centri++;
		}
	return (float) centri/nPunti;
	}


dove le funzioni randUnif e distO sono definite così:

float randUnif(float a, float b){
	if (a==b)
		return a;
	if (a>b){
		float tmp;
		tmp=a;
		a=b;
		b=tmp;
	}

	srand(time(NULL));
	return ((float)rand()*(b-a)/RAND_MAX+a); //oppure rand()%(b-a)+a
}

float distO (float x, float y){
	return sqrt(x*x+y*y);
}


Cosa ne dite, fino a qui può avere senso il procedimento? Nel secondo punto, dovrei salvare un certo numero di misure in un array dinamico:

float *esperimentoPi(int nPunti, float a, int nMisure){

	float* results=new float[nMisure];
	for(unsigned int i=0; i<nMisure; i++){
		results[i]=misurePi(nPunti, a);
	}
	return &results; 
}


Ho due dubbi essenziali: è corretto usare & per la variabile di return? E quando chiamo la funzione nel main con i parametri attuali, devo mettere l'*?

Risposte
Super Squirrel
Per quanto riguarda il primo punto ci sono varie cose che non mi convincono.

Perché la circonferenza dovrebbe avere raggio unitario?
Bisogna considerare la circonferenza inscritta nel quadrato (che avrà quindi un raggio pari ad a/2), altrimenti non ha senso.

Chiamati n i punti totali e m quelli che ricadono nella circonferenza, il rapporto m/n sarà sicuramente <=1 (essendo n>=m) e quindi non potrebbe in alcun caso essere un'approssimazione di pi che sappiamo essere pari a 3,14.
In pratica l'idea è che il rapporto tra l'area del cerchio e quella del quadrato sia uguale al rapporto tra i punti che ricadono nel cerchio e quelli che ricadono nel quadrato (ossia quelli totali). Quindi:
$ (pi*r^2)/a^2=m/n => pi/a^2*(a/2)^2=m/n=>pi=4*m/n $
Inoltre lo stesso ragionamento vale anche se si considera solo un quadrante e quindi per semplicità conviene operare solo nel primo quadrante dove x e y sono entrambe positive.

Relativamente al codice:
- la funzione srand() va richiamata una sola volta e quindi va messa nel main e non nella funzione randUnif che viene richiamata più e più volte;
- la funzione randUnif l'hai copiata da qualche parte? Lo dico perchè per il caso in questione basta passargli un solo argomento e di conseguenza tutti quei controlli diventano inutili e l'espressione che restituisce il valore di ritorno si semplifica in (float)rand/RAND_MAX*a+a.
Inoltre la suddetta funzione oltre ad essere molto poco random, ritorna solo valori contenuti nell'intervallo [-a;-a/2] che ovviamente non vanno bene. Potresti provare con qualcosa del genere magari:
float randUnif(float a)
{
    return rand() % (int)a + (float)rand() / RAND_MAX;
}

E' la prima cosa che mi è venuta in mente, ma sembra funzionare bene. Il valore di ritorno sarà compreso tra 0 e a, e considerando la precedente osservazione relativa al primo quadrante sarebbe già sufficiente, ma se vuoi valori compresi tra -a e a basta aggiungere un if e moltiplicare quel valore per -1 quando (float)rand()/RAND_MAX<0.5;

- per quanto riguarda la funzione distO puoi anche evitare di scomodare la radice quadrata controllando semplicemente che sia x^2+y^2<=r^2.

EDIT:
La formula che ho postato funziona bene solo se il valore di a è intero, altrimenti non restituisce valori maggiori della parte intera di a. Per esempio per a=3.4 non va oltre 3.
Qualche idea per aggiustarla ce l'avrei, nel caso domani provo a metterla in pratica.

Landau1
La funzione randUnif l'ho presa da un vecchio esercizio. L'idea sarebbe di avere una funzione che mi restituisca un numero casuale compreso tra due numeri fissati, in questo caso voglio che le coordinate $x$ e $y$ del mio punto siano comprese tra $-a/2$ e $a/2$. Non capisco perché non dovrebbero essere casuali i numeri ottenuti... l'unica modifica che mi viene in mente è scrivere return rand()%(b-a)+a; nel modo più semplice possibile.

Hai ragione per quanto riguarda il fatto che una circonferenza unitaria non avrebbe molto senso, però così recita il testo del problema. :| Quindi a questo punto dovrei considerare come raggio la metà del lato. Il codice diventa così:

float misuraPi(int nPunti, float a){

	float x, y;
	int centri=0;
	for(unsigned int j=0; j<nPunti; j++){
		x=randUnif(-a/2,a/2);
		y=randUnif(-a/2,a/2);
		
		if(distO(x,y)<=a/2){
			centri++;
		}
	}
	return (float) 4*centri/nPunti;
	}

float randUnif(float a, float b){
	if (a==b)
		return a;
	if (a>b){
		float tmp;
		tmp=a;
		a=b;
		b=tmp;
	}

	return ((float)rand()*(b-a)/RAND_MAX+a); 

float distO (float x, float y){
	return sqrt(x*x+y*y);
}


Però testandolo il codice restituisce subito zero (un main in cui stampo soltanto la funzione) quindi c'è qualcosa che non torna :?

Super Squirrel
La funzione randUnif l'ho presa da un vecchio esercizio. L'idea sarebbe di avere una funzione che mi restituisca un numero casuale compreso tra due numeri fissati, in questo caso voglio che le coordinate x e y del mio punto siano comprese tra −a/2 e a/2. Non capisco perché non dovrebbero essere casuali i numeri ottenuti...

In effetti va bene, avevo letto io male la funzione.
In ogni caso se segui il mio consiglio di considerare solo il primo quadrante, la funzione randUnif dovrà ritornare un valore compreso tra 0 e a e si semplifica quindi nel seguente modo:
float randUnif(const float &a)
{
    return a * rand() / RAND_MAX;
}


Come già detto nel precedente post lascia perdere la radice quadrata, ho notato che rallenta notevolmente il programma.

Però testandolo il codice restituisce subito zero (un main in cui stampo soltanto la funzione) quindi c'è qualcosa che non torna :?

Strano... nel main hai richiamato la funzione srand()?
In ogni caso posta il codice completo.

Landau1
Ecco a te Squirrel!

#include <iostream>
#include <fstream>
#include <ctime>
#include <cmath>
#include <cstdlib>

using namespace std;

float randUnif(float, float);
float distO (float, float);
float misuraPi(int, float);

//************************************************************************//

int main(){

	srand(time(NULL));

	int n;
	float lato;
	
	cout << "Inserire nPunti: ";
	cin >> n;
	cout << "Inserire lato: ";
	cin >> lato;
	
	cout << misuraPi(n, lato) << endl;

	return 0;
}

//************************************************************************//

float randUnif(const float &a)
{
    return a * rand() / RAND_MAX;
}

float distO (float x, float y){
	return (x*x+y*y);
}

//************************************************************************//

float misuraPi(int nPunti, float a){

	float x, y;
	int centri=0;
	for(unsigned int j=0; j<nPunti; j++){
		x=randUnif(a/2);
		y=randUnif(a/2);
		
		if(distO(x,y)<=pow(a,2)/4){
			centri++;
		}
	}
	return (float) 4*(centri/nPunti);
}

Super Squirrel
if(distO(x,y)<=pow(a,2)/4)

Perchè scomodare la funzione pow() e creare la funzione dist0 quando basta fare
if(x * x + y * y <= a * a / 4)

?

Le librerie fstream e cstdlib (e anche cmath se non utilizzi pow) sono superflue.

Il motivo per cui il programma restituisce 0 si trova nella seguente riga di codice:
return (float) 4*(centri/nPunti);

Quello che avviene, nell'ordine, è:
- conversione di 4 da int a float;
- divisione intera tra centri e nPunti (essendo entrambi di tipo int) che restituisce 0;
- prodotto tra 4.0 e 0 che fa 0.
In ogni caso c'è qualcosa che non mi torna... nel precedente post avevi scritto:
return (float) 4*centri/nPunti;

che va bene. Mi sembra strano che il programma ritornasse 0!

apatriarca
Una regola che cerco di impormi quando scrivo delle espressioni il cui valore può cambiare in base alle regole arbitrarie* di precedenza e associatività di un linguaggio è quella di forzare la precedenza usando delle parentesi. E' per esempio il caso nell'espressione
return a * rand() / RAND_MAX;

Il valore è infatti quello "corretto" se l'espressione viene valutata come
return (a * rand()) / RAND_MAX;

ma è uguale a zero nel caso in cui l'espressione sia valutata come
return a * (rand() / RAND_MAX);

Ovviamente è necessario fare attenzione alla differenza tra divisione intera e tra float per non usare le parentesi in modo errato come hai fatto nell'altra situazione simile.
return (float) 4*(centri/nPunti);


Altre cose che ho notato sono:
1. Non c'è alcuna ragione di passare un valore in virgola mobile per riferimento se non deve essere modificato. Rendi il codice solo piú lento..
2. Siccome il risultato è indipendente dal lato del quadrato usato per calcolare l'approssimazione di \(\pi\), è in realtà possibile estrarre valori compresi tra \([0, 1]\) e ignorare \(a\)..
3. Piuttosto che dividere è in generale meglio moltiplicare. Per cui \( a \leq b/c \) è meglio scritto come \( a*c \leq b \)..


* Ci sono regole come la precedenza della moltiplicazione sulla somma che vengono dall'algebra, ma altre sono piú arbitrarie e non sono necessariamente le stesse quando si cambia linguaggio.

Super Squirrel
1. Non c'è alcuna ragione di passare un valore in virgola mobile per riferimento se non deve essere modificato. Rendi il codice solo piú lento..

Non sono molto informato sull'argomento, in termini di tempo e di memoria conviene maggiormente il passaggio per valore o quello per riferimento?

3. Piuttosto che dividere è in generale meglio moltiplicare. Per cui a≤b/c è meglio scritto come a∗c≤b..

Come mai? Ti riferisci alla divisione in generale oppure bisogna fare una distinzione con la divisione intera?
Per esempio se sto operando con interi grandi, optando per la divisione non mi tutelo dal rischio di overflow?

Ci sono regole come la precedenza della moltiplicazione sulla somma che vengono dall'algebra, ma altre sono piú arbitrarie e non sono necessariamente le stesse quando si cambia linguaggio.

Ma nel C++ operazioni che hanno la stessa precedenza vengono effettuate sempre da sinistra verso destra o ci possono essere delle eccezioni dettate per esempio da ottimizzazioni del compilatore?

apatriarca
1. Quando passi una variabile per riferimento, stai in pratica passando il suo indirizzo. Quando il valore non viene modificato stai rendendo l'accesso piú lento perché invece di usare un indirizzo costante, accedi ad esso tramite una variabile. Il potenziale vantaggio risiede invece nel fatto di non aver bisogno di copiare tutta la memoria della relativa variabile. Se la variabile è grossa, conviene passare per riferimento, altrimenti per valore. Nel caso di un float stai in realtà copiando piú memoria perché la dimensione di un float è nelle moderne architetture a 64bit inferiore ad un indirizzo in memoria.

3. Dipende in effetti dalla situazione, potrebbe essere una preferenza personale in effetti. La divisione impedisce l'overflow, ma mi è capitato piú volte di aver problemi con la divisione. Per esempio non notando che l'espressione è intera o perché ho diviso per un valore troppo piccolo o altro. Discorso simile vale per somma e sottrazione per me. Preferisco scrivere le espressioni con la somma invece che con la sottrazione se possibile. Ma tutte le "regole" di questo tipo vanno valutate caso per caso.

La precedenza/associatività di un operatore è sempre la stessa. Il compilatore non può cambiare il significato del codice durante le ottimizzazioni e non può quindi certamente cambiare qualcosa come precedenza o associatività. In C++ le operazioni binarie ad eccezione degli assegnamenti vanno da sinistra verso destra. La differenza si può avere quando si passa ad altri linguaggi e il problema è piú che altro legato alla leggibilità e al fatto che quando dopo mesi tocchi di nuovo il codice non ricordi necessariamente che l'ordine delle operazioni era importante.

Super Squirrel
Molto chiaro, grazie per la spiegazione.

... e il problema è piú che altro legato alla leggibilità e al fatto che quando dopo mesi tocchi di nuovo il codice non ricordi necessariamente che l'ordine delle operazioni era importante.

Vero, non ci avevo pensato.

Landau1
Ora torna tutto, grazie ad entrambi! Per il secondo punto, cosa ne pensate di quello che ho postato? Il dubbio resta ancora valido :roll:

float *esperimentoPi(int nPunti, float a, int nMisure){

   float* results=new float[nMisure];
   for(unsigned int i=0; i<nMisure; i++){
      results[i]=misurePi(nPunti, a);
   }
   return &results; 
}

Super Squirrel
Nel secondo punto, dovrei salvare un certo numero di misure in un array dinamico:
float *esperimentoPi(int nPunti, float a, int nMisure){

   float* results=new float[nMisure];
   for(unsigned int i=0; i<nMisure; i++){
      results[i]=misurePi(nPunti, a);
   }
   return &results; 
}

Ho due dubbi essenziali: è corretto usare & per la variabile di return?

No, inoltre non compilerebbe quel codice perchè la funzione deve ritornare una variabile di tipo float*, mentre &results, essendo l'indirizzo di un puntatore a float, coincide col tipo float**.

E quando chiamo la funzione nel main con i parametri attuali, devo mettere l'*?

Non ho capito cosa intendi, magari posta il codice.

P.S.
Se le nMisure le fai una sola volta non vedo l'utilità di utilizzare una funzione esperimentoPi().

Landau1
Ciao, scusa per il ritardo. In realtà dovrei fare Nmisure misure e salvarle nel vettore!

Edit: o meglio, dovrei fare Nmisure misure per ciascuna componente del vettore, credo...

Super Squirrel
Ciao!
In realtà dovrei fare Nmisure misure e salvarle nel vettore!

Non ho capito il nesso logico di questa affermazione con il mio precedente post... :|

EDIT:
Edit: o meglio, dovrei fare Nmisure misure per ciascuna componente del vettore, credo...

Non mi è chiara come cosa...
In ogni caso dal punto di vista puramente informatico hai capito quello che intendevo nel post precedente?

Landau1
Ok, ricomincio un attimo da capo, perché ormai è passato del tempo :lol:
Mi si chiede questo: Scrivere una funzione float *esperimentoPi(int nPunti, float a, int nMisure); che, creato dinamicamente un vettore di float di dimensione nMisure effettui nMisure misure di $π$ utilizzando la funzione misuraPi definita nell’esercizio precedente. Le misure dovranno essere salvate nel vettore creato dinamicamente, il cui indirizzo verrà restituito dalla funzione.

Quindi semplicemente dichiaro il vettore dinamico float* results=new float[nMisure];, lo carico con misure diverse di $pi$ for(unsigned int i=0; i=misurePi(nPunti, a);} e lo restituisco con return results; . Per quanto riguarda il tuo post, effettivamente non ho capito cosa intendi. In qualche modo questa funzione la devo costruire perché me la richiede esplicitamente il testo, poi non so se sia effettivamente necessaria :-D

Il main deve fare questo:

Scrivere un programma che analizzi il comportamento della stima Monte Carlo in funzione dei punti usati in ciascuna misura di pi come segue.

1. Fissare il numero di misure per esperimento a NMISURE 50
2. Eseguire una sequenza di esperimenti al variare del numero di punti (parametro nPunti) tra 50 e 1000 con incrementi di 50.
3. Registrare il numero di punti usati, la media e la deviazione standard campionaria (!!!) degli esperimenti sul file risultatiPi.dat.


Quindi definisco la costante #define NMISURE 50 e faccio la serie di esperimenti usando la funzione di prima: for(unsigned j=1; j<=20; j++) esperimentoPi(50*j,a,NMISURE);. Apro il file con ofstream file_out; file_out.open("risultati.dat",ios::out);, uso le vecchie funzioni per calcolare media e deviazione standard e stampo tutto.

Questo è sommariamente quanto prodotto fino ad adesso, oltre alla funzione misuraPi :D

Landau1
Quindi alla fine il main verrebbe più o meno così:

int main(){

	srand(time(NULL));
        ofstream file_out;
	file_out.open("risultati.dat",ios::out);

	
	for(unsigned j=1; j<=20; j++)
		esperimentoPi(50*j,a,NMISURE);

		file_out << "Numero dati:" << 50*j << ", Media: " << media(results, nMisure) << " , 
                Deviazione Standard:" << devStd(results, nMisure) << endl;

	return 0;
}

vict85
Quel main ha molti errori. Per esempio non si capisce come stai riempendo il vettore results, o come mai nMisure sia ogni tanto maiuscolo e ogni tanto no. Inoltre hai dimenticato le parentesi nel for e quindi la stampa nel file non è inclusa nel ciclo. Tra l'altro non hai chiamato delete sulla memoria che hai allocato dentro esperimento Pi.

Per capirci, questo è il main nella "mia versione":
#include <stdlib.h>
#include <fstream>
#include <iostream>  // cerr
#include <time.h>
#include <cmath>

float misuraPi(int nPunti, float);

float * esperimentoPi(int nPunti, float a, int nMisure);

float media(const float * array, int N);

float devStd(const float * array, int N, const float * media_ptr = nullptr);

inline void
delete_array_of_array(float * array[], int N)
{
  for (int i = 0; i != N; i++)
    {
      delete[] array[i];
      array[i] = nullptr;
    }
}

int
main( )
{
  constexpr int NMISURE = 50;
  constexpr int NRIPETIZIONI = 1000 / 50;
  constexpr float a = 1.f;
  srand(time(nullptr));  // nullptr e' type safe

  float * esperimenti[NRIPETIZIONI];

  for (int i = 0, nPunti = 50; i != NRIPETIZIONI; ++i, nPunti += 50)
    {
      esperimenti[i] = esperimentoPi(nPunti, a, NMISURE);
      if (!esperimenti)
        {
          std::cerr << "Errore in esperimentoPi" << std::endl;
          delete_array_of_array(esperimenti, i);
          return EXIT_FAILURE;
        }
    }

  std::ofstream file_out;
  file_out.open("risultati.dat", std::ios::out);
  if (file_out.fail( ))
    {
      std::cerr << "Errore nella creazione del file" << std::endl;
      delete_array_of_array(esperimenti, NRIPETIZIONI);
      return EXIT_FAILURE;
    }

  for (int i = 0, nPunti = 50; i != NRIPETIZIONI; ++i, nPunti += 50)
    {
      float m = media(esperimenti[i], NMISURE);
      file_out << "Numero dati: " << nPunti << ", Media: " << m
               << ", Deviazione Standard: "
               << devStd(esperimenti[i], NMISURE, &m) << std::endl;
    }
  delete_array_of_array(esperimenti, NRIPETIZIONI);
}
dove ovviamente ho omesso le funzioni che ti viene richiesto di implementare. Ho diviso calcolo e scrittura su file apposta per mostrarti qualcosa di diverso. Altri approcci sono possibili, per esempio questo:
#include <stdlib.h>
#include <fstream>
#include <iostream>  // cerr
#include <time.h>
#include <cmath>

float misuraPi(int nPunti, float);

float * esperimentoPi(int nPunti, float a, int nMisure);

float media(const float * array, int N);

float devStd(const float * array, int N, const float * media_ptr = nullptr);

int
main( )
{
  constexpr int NMISURE = 50;
  constexpr float a = 1.0f;
  srand(time(nullptr));  // nullptr e' type safe

  std::ofstream file_out;
  file_out.open("risultati.dat", std::ios::out);
  if (file_out.fail( ))
    {
      std::cerr << "Errore nella creazione del file" << std::endl;
      return EXIT_FAILURE;
    }

  for (int nPunti = 50; nPunti < 1001; nPunti += 50)
    {
      float const * esperimento = esperimentoPi(nPunti, a, NMISURE);
      if (!esperimento)
        {
          std::cerr << "Errore in esperimentoPi" << std::endl;
          return EXIT_FAILURE;
        }

      float m = media(esperimento, NMISURE);
      file_out << "Numero dati: " << nPunti << ", Media: " << m
               << ", Deviazione Standard: " << devStd(esperimento, NMISURE, &m)
               << std::endl;
      // un po' uno spreco, sarebbe più pratico allocare una volta e
      // riutilizzare
      delete[] esperimento;
    }
}
o anche
#include <stdlib.h>
#include <fstream>
#include <iostream>  // cerr
#include <time.h>
#include <cmath>

float misuraPi(int nPunti, float);

float * esperimentoPi(int nPunti, float a, int nMisure);

float media(const float * array, int N);

float devStd(const float * array, int N, const float * media_ptr = nullptr);

inline void
delete_array_of_array(float * array[], int N)
{
  for (int i = 0; i != N; i++)
    {
      delete[] array[i];
      array[i] = nullptr;
    }
}

int
main( )
{
  constexpr int NMISURE = 50;
  constexpr int NRIPETIZIONI = 1000 / 50;
  constexpr float a = 1.f;
  srand(time(nullptr));  // nullptr e' type safe

  float esperimenti[NRIPETIZIONI][2];

  for (int i = 0, nPunti = 50; i != NRIPETIZIONI; ++i, nPunti += 50)
    {
      float * esperimento = esperimentoPi(nPunti, a, NMISURE);
      if (!esperimento)
        {
          std::cerr << "Errore in esperimentoPi" << std::endl;
          return EXIT_FAILURE;
        }
      esperimenti[i][0] = media(esperimento, NMISURE);
      esperimenti[i][1] = devStd(esperimento, NMISURE, &esperimenti[i][0]);

      // che spreco di memoria...
      delete[] esperimento;
    }

  std::ofstream file_out;
  file_out.open("risultati.dat", std::ios::out);
  if (file_out.fail( ))
    {
      std::cerr << "Errore nella creazione del file" << std::endl;
      return EXIT_FAILURE;
    }

  for (int i = 0, nPunti = 50; i != NRIPETIZIONI; ++i, nPunti += 50)
    {
      file_out << "Numero dati: " << nPunti << ", Media: " << esperimenti[i][0]
               << ", Deviazione Standard: " << esperimenti[i][1] << std::endl;
    }
}


I codici sono stati messi in ordine abbastanza sparso. L'ultimo è quello che preferisco. Il codice richiede di essere compilato con l'opzione --std=c++11 per l'uso dei [inline]constexpr[/inline], puoi anche trasformarli in dei [inline]#define[/inline] o in dei [inline]const[/inline].

Comunque l'approccio è piuttosto inefficiente: il codice produce qualcosa come
Numero dati: 50, Media: 3.12, Deviazione Standard: 0.191332
Numero dati: 100, Media: 3.1976, Deviazione Standard: 0.189553
Numero dati: 150, Media: 3.112, Deviazione Standard: 0.127024
Numero dati: 200, Media: 3.1508, Deviazione Standard: 0.117997
Numero dati: 250, Media: 3.14432, Deviazione Standard: 0.127368
Numero dati: 300, Media: 3.1312, Deviazione Standard: 0.106178
Numero dati: 350, Media: 3.12777, Deviazione Standard: 0.0758926
Numero dati: 400, Media: 3.132, Deviazione Standard: 0.0843564
Numero dati: 450, Media: 3.14009, Deviazione Standard: 0.0776913
Numero dati: 500, Media: 3.14832, Deviazione Standard: 0.0704584
Numero dati: 550, Media: 3.136, Deviazione Standard: 0.0728145
Numero dati: 600, Media: 3.12987, Deviazione Standard: 0.0595277
Numero dati: 650, Media: 3.14769, Deviazione Standard: 0.0618607
Numero dati: 700, Media: 3.14046, Deviazione Standard: 0.0595722
Numero dati: 750, Media: 3.13696, Deviazione Standard: 0.0575969
Numero dati: 800, Media: 3.1366, Deviazione Standard: 0.0685525
Numero dati: 850, Media: 3.136, Deviazione Standard: 0.0623809
Numero dati: 900, Media: 3.14018, Deviazione Standard: 0.0583372
Numero dati: 950, Media: 3.13651, Deviazione Standard: 0.0692006
Numero dati: 1000, Media: 3.16048, Deviazione Standard: 0.0420867


Insomma hai bisogno di una buona funzione di numeri casuali per avere risultati accettabili.

Rispondi
Per rispondere a questa discussione devi prima effettuare il login.