Generazione di numeri float casuali


271

Come posso generare float casuali in C ++?

Pensavo di poter prendere il rand intero e dividerlo per qualcosa, sarebbe sufficiente?


2
Dipende piuttosto da cosa vuoi il numero e da quanto casuale. tipicamente rand () darà 15 bit di casualità, ma i float hanno una precisione di 23 bit, quindi perderanno alcuni valori.
Pete Kirkham,

1
Ho aggiornato la mia risposta per includere tutte le principali opzioni disponibili e la mia scelta di concentrarmi sull'intestazione randomaggiunta in C ++ 11 è ulteriormente rafforzata dal documento standard N3924: Discouraging rand () in C ++ 14 . Includo rand()nella mia risposta per considerazioni per lo più storiche ma anche la realizzazione di un'applicazione legacy esiste.
Shafik Yaghmour,

La mia risposta include come evitare di ottenere gli stessi numeri ogni volta con l' <random>intestazione
Andreas DM

Risposte:


381

rand()può essere usato per generare numeri pseudo-casuali in C ++. In combinazione con RAND_MAXe un po 'di matematica, puoi generare numeri casuali in qualsiasi intervallo arbitrario che scegli. Questo è sufficiente per scopi di apprendimento e programmi giocattolo. Se hai bisogno di numeri veramente casuali con distribuzione normale, dovrai utilizzare un metodo più avanzato.


Questo genererà un numero compreso tra 0,0 e 1,0, inclusi.

float r = static_cast <float> (rand()) / static_cast <float> (RAND_MAX);

Questo genererà un numero da 0,0 a qualche arbitraria float, X:

float r2 = static_cast <float> (rand()) / (static_cast <float> (RAND_MAX/X));

Questo genererà un numero da un numero arbitrario LOa un numero arbitrario HI:

float r3 = LO + static_cast <float> (rand()) /( static_cast <float> (RAND_MAX/(HI-LO)));

Nota che la rand()funzione spesso non sarà sufficiente se hai bisogno di numeri veramente casuali.


Prima di chiamare rand(), devi prima "seminare" il generatore di numeri casuali chiamando srand(). Questo dovrebbe essere fatto una volta durante l'esecuzione del programma, non una volta ogni volta che chiami rand(). Questo viene spesso fatto in questo modo:

srand (static_cast <unsigned> (time(0)));

Per chiamare rando srandè necessario #include <cstdlib>.

Per chiamare time, è necessario #include <ctime>.


22
Non dimenticare di seminare prima!
Klaim,

14
È meglio notare che entrambi i limiti sono inclusivi.
dmckee --- ex gattino moderatore,

14
Questa risposta è fuorviante. È stato coperto a Going Native 2013 la scorsa settimana; rand () Considerato dannoso , channel9.msdn.com/Events/GoingNative/2013/… per una spiegazione molto dettagliata.
Ade Miller,

14
Non capisco perché così tante persone hanno votato questa risposta. È matematicamente errato. RAND_MAX è un numero molto piccolo (in genere 2 ^ 16). Ciò significa che da 23 bit del virgola mobile ne fai solo 15 casuali. Gli altri saranno probabilmente zero. Riceverai numeri casuali in una distribuzione uniforme ma di bassa precisione. Ad esempio, il tuo generatore casuale può generare 0,00001 e 0,00002 ma non può generare 0,000017. Quindi hai una distribuzione uniforme ma di bassa precisione (256 volte meno precisione del punto in virgola mobile reale).
DanielHsH,

10
@DanielHsH: L'OP ha chiesto in particolare con quale meccanica si potesse usare per generare float casuali rand(). Questa domanda, e la mia risposta, era specificamente focalizzata sull'apprendimento delle basi e non era preoccupata per gli alti livelli di precisione. Devi imparare a camminare prima di poter imparare a correre.
John Dibling,

137

C ++ 11 ti offre molte nuove opzioni con random. L'articolo canonico su questo argomento sarebbe N3551, generazione di numeri casuali in C ++ 11

Per capire perché l'uso rand()può essere problematico, consultare il materiale di presentazione rand () Considerato dannoso di Stephan T. Lavavej, fornito durante l' evento GoingNative 2013 . Le diapositive sono nei commenti ma ecco un link diretto .

Copro anche boostoltre a utilizzare randpoiché il codice legacy potrebbe ancora richiedere il suo supporto.

L'esempio seguente è distillato dal sito cppreference e utilizza il motore std :: mersenne_twister_engine e lo std :: uniform_real_distribution che genera numeri [0,10)nell'intervallo, con altri motori e distribuzioni commentati ( vederlo dal vivo ):

#include <iostream>
#include <iomanip>
#include <string>
#include <map>
#include <random>

int main()
{
    std::random_device rd;

    //
    // Engines 
    //
    std::mt19937 e2(rd());
    //std::knuth_b e2(rd());
    //std::default_random_engine e2(rd()) ;

    //
    // Distribtuions
    //
    std::uniform_real_distribution<> dist(0, 10);
    //std::normal_distribution<> dist(2, 2);
    //std::student_t_distribution<> dist(5);
    //std::poisson_distribution<> dist(2);
    //std::extreme_value_distribution<> dist(0,2);

    std::map<int, int> hist;
    for (int n = 0; n < 10000; ++n) {
        ++hist[std::floor(dist(e2))];
    }

    for (auto p : hist) {
        std::cout << std::fixed << std::setprecision(1) << std::setw(2)
                  << p.first << ' ' << std::string(p.second/200, '*') << '\n';
    }
}

l'output sarà simile al seguente:

0 ****
1 ****
2 ****
3 ****
4 *****
5 ****
6 *****
7 ****
8 *****
9 ****

L'output varierà a seconda della distribuzione scelta, quindi se decidessimo di utilizzare std :: normal_distribution con un valore 2sia per mean che per stddev, ad esempio dist(2, 2)invece l'output sarebbe simile a questo ( vederlo dal vivo ):

-6 
-5 
-4 
-3 
-2 **
-1 ****
 0 *******
 1 *********
 2 *********
 3 *******
 4 ****
 5 **
 6 
 7 
 8 
 9 

La seguente è una versione modificata di alcuni dei codici presentati in N3551( vederlo dal vivo ):

#include <algorithm>
#include <array>
#include <iostream>
#include <random>

std::default_random_engine & global_urng( )
{
    static std::default_random_engine u{};
    return u ;
}

void randomize( )
{
    static std::random_device rd{};
    global_urng().seed( rd() );
}

int main( )
{
  // Manufacture a deck of cards:
  using card = int;
  std::array<card,52> deck{};
  std::iota(deck.begin(), deck.end(), 0);

  randomize( ) ;  

  std::shuffle(deck.begin(), deck.end(), global_urng());
  // Display each card in the shuffled deck:
  auto suit = []( card c ) { return "SHDC"[c / 13]; };
  auto rank = []( card c ) { return "AKQJT98765432"[c % 13]; };

  for( card c : deck )
      std::cout << ' ' << rank(c) << suit(c);

   std::cout << std::endl;
}

I risultati saranno simili a:

5H 5S AS 9S 4D 6H TH 6D KH 2S QS 9H 8H 3D KC TD 7H 2D KS 3C TC 7D 4C QH QC QD JD AH JC AC KD 9D 5C 2H 4H 9C 8C JH 5D 4S 7C AD 3S 8S TS 2C 8D 3H 6C JS 7S 6S

Incremento

Ovviamente anche Boost.Random è sempre un'opzione, qui sto usando boost :: random :: uniform_real_distribution :

#include <iostream>
#include <iomanip>
#include <string>
#include <map>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_real_distribution.hpp>

int main()
{
    boost::random::mt19937 gen;
    boost::random::uniform_real_distribution<> dist(0, 10);

    std::map<int, int> hist;
    for (int n = 0; n < 10000; ++n) {
        ++hist[std::floor(dist(gen))];
    }

    for (auto p : hist) {
        std::cout << std::fixed << std::setprecision(1) << std::setw(2)
                  << p.first << ' ' << std::string(p.second/200, '*') << '\n';
    }
}

rand ()

Se è necessario utilizzarlo rand(), possiamo andare alle domande frequenti C per una guida su Come posso generare numeri casuali in virgola mobile? , che sostanzialmente fornisce un esempio simile a questo per la generazione di un intervallo [0,1):

#include <stdlib.h>

double randZeroToOne()
{
    return rand() / (RAND_MAX + 1.);
}

e generare un numero casuale nell'intervallo tra [M,N):

double randMToN(double M, double N)
{
    return M + (rand() / ( RAND_MAX / (N-M) ) ) ;  
}

1
puoi sistemare i tuoi randMToNpls? notare che è [M,N]o aggiungere nuovamente + 1.da quanto sopra randZeroToOne. -> pensa a chiamarlo così:randMToN(0.0, 1.0);
BeyelerStudios

1
Fai anche attenzione alla divisione per zero a (N-M). Un buon modo per gestire questo errore si trova qui: stackoverflow.com/questions/33058848/…
Dr Beco,

61

Dai un'occhiata a Boost.Random . Potresti fare qualcosa del genere:

float gen_random_float(float min, float max)
{
    boost::mt19937 rng;
    boost::uniform_real<float> u(min, max);
    boost::variate_generator<boost::mt19937&, boost::uniform_real<float> > gen(rng, u);
    return gen();
}

Giocaci, potresti fare meglio a far passare lo stesso oggetto mt19937 invece di costruirne uno nuovo ogni volta, ma spero che tu abbia l'idea.


1
uniform_real utilizza un intervallo semi-aperto [min, max), il che significa che otterrai il tuo valore minimo ma non raggiungerai mai il valore massimo. È qualcosa da considerare, anche se in qualche modo si riesce a superare questo problema.
TehWan,

20
Questo fa ora parte di C ++ 11.
Tomas Andrle,

@Wolf in applicazioni pratiche le probabilità di colpire qualsiasi valore in virgola mobile specifico sono così basse che non importa se l'endpoint è incluso o escluso. Se è necessario max, ma possibile utilizzare un tipo aperto min, è possibile invertire l'intervallo facilmente: return min + max - gen();.
Mark Ransom,

26

In moderno c++è possibile utilizzare l' <random>intestazione fornita c++11.
Per ottenere casuali floatpuoi usare std::uniform_real_distribution<>.

È possibile utilizzare una funzione per generare i numeri e se non si desidera che i numeri siano sempre uguali , impostare il motore e la distribuzione in modo che siano static.
Esempio:

float get_random()
{
    static std::default_random_engine e;
    static std::uniform_real_distribution<> dis(0, 1); // rage 0 - 1
    return dis(e);
}

È ideale per posizionare gli floatoggetti in un contenitore come std::vector:

int main()
{
    std::vector<float> nums;
    for (int i{}; i != 5; ++i) // Generate 5 random floats
        nums.emplace_back(get_random());

    for (const auto& i : nums) std::cout << i << " ";
}

Esempio di output:

0.0518757 0.969106 0.0985112 0.0895674 0.895542

std::uniform_real_distribution<> dis(0, 1); // rage 0 - 1è tecnicamente errato, 1.0 non verrà mai generato, vedi en.cppreference.com/w/cpp/numeric/random/… To create a distribution over the closed interval [a,b], std::nextafter(b, std::numeric_limits<RealType>::max()) may be used as the second parameter.
Troyseph

1
Questa dovrebbe essere la risposta accettata, è il 2020 eccitante.
Alex

25

Chiama il codice con due floatvalori, il codice funziona in qualsiasi intervallo.

float rand_FloatRange(float a, float b)
{
    return ((b - a) * ((float)rand() / RAND_MAX)) + a;
}

Vale la pena ricordare che questo è un potenziale caso d'uso per fmaf()(o il fma()sovraccarico float in C ++) in C99 o C ++ 11, che potrebbe preservare una maggiore precisione. Come in, fmaf((float)rand() / RAND_MAX, b - a, a).
Tim Čas,

22

Se stai usando C ++ e non C, ricorda che nel rapporto tecnico 1 (TR1) e nella bozza C ++ 0x hanno aggiunto funzionalità per un generatore di numeri casuali nel file di intestazione, credo che sia identico a Boost. Libreria casuale e sicuramente più flessibile e "moderna" rispetto alla funzione di libreria C, rand.

Questa sintassi offre la possibilità di scegliere un generatore (come il mersenne twister mt19937) e quindi scegliere una distribuzione (normale, bernoulli, binomiale ecc.).

La sintassi è la seguente (spudorato preso in prestito da questo sito ):

  #include <iostream>
  #include <random>

  ...

  std::tr1::mt19937 eng;  // a core engine class 
  std::tr1::normal_distribution<float> dist;     

  for (int i = 0; i < 10; ++i)        
      std::cout << dist(eng) << std::endl;

2
Questo è ora in C ++ 11, inoltre è possibile inizializzare dist con valori min e max.
Étienne,

Mi sembra strano mettere il min e il max nell'inizializzatore e fornire il generatore quando si ottiene un valore - Preferirei se fosse il contrario, oh beh.
yo

6

Su alcuni sistemi (Windows con VC, attualmente), RAND_MAXè ridicolmente piccolo, i. e. solo 15 bit. Quando dividi per RAND_MAXte stai solo generando una mantissa di 15 bit invece dei 23 possibili bit. Questo può o meno essere un problema per te, ma in questo caso perdi alcuni valori.

Oh, ho appena notato che c'era già un commento per quel problema. Ad ogni modo, ecco un codice che potrebbe risolverlo per te:

float r = (float)((rand() << 15 + rand()) & ((1 << 24) - 1)) / (1 << 24);

Non testato, ma potrebbe funzionare :-)


Che dire di float r = (float) ((rand () << 9) | rand ()) / RAND_MAX? (anche non testato)
Trap

Argh, scusa, la divisione per RAND_MAX non ti porterà da nessuna parte ... il punto centrale di questo trucco era avere qualcosa di più grande di RAND_MAX ... risolto anche per me.
Joey,

2
Fai attenzione a comporre numeri casuali senza teoria ... le chiamate consecutive a rand () potrebbero non essere completamente indipendenti. Suggerimento: se si tratta di un generatore congruenziale lineare, guarda il bit basso per le chiamate consecutive: si alterna tra 0 e 1.
RBerteig

Lo so. Per alcune applicazioni, tuttavia, potrebbe essere sufficiente. Ma sì, probabilmente dovresti usare più di due sole chiamate in questo caso. In questo caso non esiste un proiettile d'argento, non puoi nemmeno fare affidamento sul fatto che sia un LCG. Altri PRNG hanno bit alti deboli. La soluzione Boost dovrebbe essere la migliore qui.
Joey,

(nb: il bit basso restituito da rand in MSVC non è il bit più basso dello stato RNG. Per le chiamate 100 rand () ottengo quanto segue: 110010000011111110101001001001101010111011011011101001111110010000000000010100011011000000100101100011. Java utilizza solo V a 32 bit e LC a 48 bit per farlo in modo simile)
Joey,


2

Finora non ero soddisfatto di nessuna delle risposte, quindi ho scritto una nuova funzione float casuale. Fa ipotesi bit per bit sul tipo di dati float. Ha ancora bisogno di una funzione rand () con almeno 15 bit casuali.

//Returns a random number in the range [0.0f, 1.0f).  Every
//bit of the mantissa is randomized.
float rnd(void){
  //Generate a random number in the range [0.5f, 1.0f).
  unsigned int ret = 0x3F000000 | (0x7FFFFF & ((rand() << 8) ^ rand()));
  unsigned short coinFlips;

  //If the coin is tails, return the number, otherwise
  //divide the random number by two by decrementing the
  //exponent and keep going. The exponent starts at 63.
  //Each loop represents 15 random bits, a.k.a. 'coin flips'.
  #define RND_INNER_LOOP() \
    if( coinFlips & 1 ) break; \
    coinFlips >>= 1; \
    ret -= 0x800000
  for(;;){
    coinFlips = rand();
    RND_INNER_LOOP(); RND_INNER_LOOP(); RND_INNER_LOOP();
    //At this point, the exponent is 60, 45, 30, 15, or 0.
    //If the exponent is 0, then the number equals 0.0f.
    if( ! (ret & 0x3F800000) ) return 0.0f;
    RND_INNER_LOOP(); RND_INNER_LOOP(); RND_INNER_LOOP();
    RND_INNER_LOOP(); RND_INNER_LOOP(); RND_INNER_LOOP();
    RND_INNER_LOOP(); RND_INNER_LOOP(); RND_INNER_LOOP();
    RND_INNER_LOOP(); RND_INNER_LOOP(); RND_INNER_LOOP();
  }
  return *((float *)(&ret));
}

7
approccio interessante, mi piacerebbe votare, ma davvero non capisco cosa sta succedendo
hasen

2

A mio avviso, la risposta di cui sopra fornisce un float "casuale", ma nessuno di essi è veramente un float casuale (ovvero manca una parte della rappresentazione float). Prima di correre nella mia implementazione, diamo un'occhiata al formato standard ANSI / IEEE per i float:

| segno (1 bit) | e (8 bit) | f (23 bit) |

il numero rappresentato da questa parola è (-1 * segno) * 2 ^ e * 1.f

si noti che il numero "e" è un numero distorto (con un bias di 127) che varia quindi da -127 a 126. La funzione più semplice (e in realtà più casuale) è semplicemente scrivere i dati di un int casuale in un float, così

int tmp = rand();
float f = (float)*((float*)&tmp);

nota che se lo fai float f = (float)rand();convertirai l'intero in un float (quindi 10 diventerà 10.0).

Quindi ora se vuoi limitare il valore massimo puoi fare qualcosa del genere (non sono sicuro che funzioni)

int tmp = rand();
float f = *((float*)&tmp);
tmp = (unsigned int)f       // note float to int conversion!
tmp %= max_number;
f -= tmp;

ma se guardi la struttura del float puoi vedere che il valore massimo di un float è (circa) 2 ^ 127 che è molto più grande del valore massimo di un int (2 ^ 32), escludendo così una parte significativa di i numeri che possono essere rappresentati da un float. Questa è la mia implementazione finale:

/**
 * Function generates a random float using the upper_bound float to determine 
 * the upper bound for the exponent and for the fractional part.
 * @param min_exp sets the minimum number (closest to 0) to 1 * e^min_exp (min -127)
 * @param max_exp sets the maximum number to 2 * e^max_exp (max 126)
 * @param sign_flag if sign_flag = 0 the random number is always positive, if 
 *              sign_flag = 1 then the sign bit is random as well
 * @return a random float
 */
float randf(int min_exp, int max_exp, char sign_flag) {
    assert(min_exp <= max_exp);

    int min_exp_mod = min_exp + 126;

    int sign_mod = sign_flag + 1;
    int frac_mod = (1 << 23);

    int s = rand() % sign_mod;  // note x % 1 = 0
    int e = (rand() % max_exp) + min_exp_mod;
    int f = rand() % frac_mod;

    int tmp = (s << 31) | (e << 23) | f;

    float r = (float)*((float*)(&tmp));

    /** uncomment if you want to see the structure of the float. */
//    printf("%x, %x, %x, %x, %f\n", (s << 31), (e << 23), f, tmp, r);

    return r;
}

l'utilizzo di questa funzione randf(0, 8, 0)restituirà un numero casuale compreso tra 0,0 e 255,0


1
hai un errore. rand ()% frac_mod non funzionerà poiché MAX_RAND è generalmente inferiore a (1 << 23).
DanielHsH,

Devo ammettere che non conosco la dimensione esatta di MAX_RAND. Tuttavia funzionerà comunque, è forse un'affermazione inutile, ma funzionerà comunque. 8% 10 = 8 quindi va bene, ma se MAX_RAND è sempre più piccolo allora (1 << 23) puoi effettivamente rimuoverlo.
user2546926

2
No, ti sbagli un po '. RandMax è in genere ~ 65.000. Ciò significa che da 23 bit ne fai solo 15 casuali. Gli altri saranno probabilmente zero. Riceverai numeri casuali ma di bassa precisione. Ad esempio, il tuo generatore casuale può generare 0,001 e 0,002 ma non può generare 0,0017. Quindi hai una distribuzione uniforme ma di bassa precisione (256 volte meno precisione del galleggiante).
DanielHsH,

Ci sono due errori in questa risposta. Riparare l'intervallo esponente: int e = (rand() % (max_exp - min_exp)) + min_exp_mod;e la mantissa: int f = (int)(frac_mod * (float)rand() / RAND_MAX);sostituendo le rispettive linee sopra. Nota che l'errore della mantissa è grave: per i RAND_MAXpiù piccoli 1 << 23dovresti solo randomizzare i bit significativi inferiori e ottenere sempre 0s per i bit più significativi!
BeyelerStudios

2

Se sai che il tuo formato in virgola mobile è IEEE 754 (quasi tutte le moderne CPU tra cui Intel e ARM), puoi creare un numero in virgola mobile casuale da un numero intero casuale usando metodi bit-wise. Questo dovrebbe essere preso in considerazione solo se non si ha accesso a C ++ 11 randomo Boost.Randomche sono entrambi molto migliori.

float rand_float()
{
    // returns a random value in the range [0.0-1.0)

    // start with a bit pattern equating to 1.0
    uint32_t pattern = 0x3f800000;

    // get 23 bits of random integer
    uint32_t random23 = 0x7fffff & (rand() << 8 ^ rand());

    // replace the mantissa, resulting in a number [1.0-2.0)
    pattern |= random23;

    // convert from int to float without undefined behavior
    assert(sizeof(float) == sizeof(uint32_t));
    char buffer[sizeof(float)];
    memcpy(buffer, &pattern, sizeof(float));
    float f;
    memcpy(&f, buffer, sizeof(float));

    return f - 1.0;
}

Questo darà una distribuzione migliore di una divisione usando.


8
Non sono sicuro del motivo per cui stai dicendo che ciò darebbe una "migliore distribuzione". In effetti, questo darà esattamente la stessa distribuzione di Just return (float)random23 / (1 << 23). (Sì, ho appena provato questo , modificando la tua funzione in modo da prenderla random32come parametro ed eseguendola per tutti i valori da zero fino a (1 << 23)-1. E sì, il tuo metodo in effetti dà esattamente gli stessi risultati della divisione per 1 << 23.)
Ilmari Karonen

1

Per C ++, può generare numeri float reali nell'intervallo specificato dalla distvariabile

#include <random>  //If it doesnt work then use   #include <tr1/random>
#include <iostream>

using namespace std;

typedef std::tr1::ranlux64_base_01 Myeng; 
typedef std::tr1::normal_distribution<double> Mydist;

int main() { 
       Myeng eng; 
       eng.seed((unsigned int) time(NULL)); //initializing generator to January 1, 1970);
       Mydist dist(1,10); 

       dist.reset(); // discard any cached values 
       for (int i = 0; i < 10; i++)
       {
           std::cout << "a random value == " << (int)dist(eng) << std::endl; 
       }

       return (0);
}

1
Hai appena copiato e incollato il codice da questa risposta? stackoverflow.com/a/1118739/1538531
Derek

In realtà No. Sono un po 'sorpreso di vedere quanto si assomiglino! Ma ho inizializzato il generatore di motore gennaio 1,1970.
Marco167,

Giusto. Ho notato che hai inizializzato il generatore all'epoca, ma dannatamente quel codice è simile!
Derek,

Trovo un po 'strano dare un esempio di TR1, puoi spiegare in quali casi qualcuno dovrebbe usare TR1 invece di C ++ 11?
Shafik Yaghmour,

0

rand () restituisce un int tra 0 e RAND_MAX. Per ottenere un numero casuale compreso tra 0,0 e 1,0, prima lanciare int return di rand () su un float, quindi dividere per RAND_MAX.


0
#include <cstdint>
#include <cstdlib>
#include <ctime>

using namespace std;

/* single precision float offers 24bit worth of linear distance from 1.0f to 0.0f */
float getval() {
    /* rand() has min 16bit, but we need a 24bit random number. */
    uint_least32_t r = (rand() & 0xffff) + ((rand() & 0x00ff) << 16);
    /* 5.9604645E-8 is (1f - 0.99999994f), 0.99999994f is the first value less than 1f. */
    return (double)r * 5.9604645E-8;
}

int main()
{
    srand(time(NULL));
...

Non ho potuto pubblicare due risposte, quindi ecco la seconda soluzione. log2 numeri casuali, grande inclinazione verso 0,0f ma è veramente un float casuale da 1,0f a 0,0f.

#include <cstdint>
#include <cstdlib>
#include <ctime>

using namespace std;

float getval () {
    union UNION {
        uint32_t i;
        float f;
    } r;
    /* 3 because it's 0011, the first bit is the float's sign.
     * Clearing the second bit eliminates values > 1.0f.
     */
    r.i = (rand () & 0xffff) + ((rand () & 0x3fff) << 16);
    return r.f;
}

int main ()
{
    srand (time (NULL));
...
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.