générateur de nombre aléatoire sous VC++ meilleur que rand ?

générateur de nombre aléatoire sous VC++ meilleur que rand ? - Programmation

Marsh Posté le 02-04-2001 à 14:49:25    

La portée de rand n'est que de RAND_MAX, et j'ai besoin de nombres aléatoire entre 0 et 1 ( 1 inclus ou exclus suivant les fonctions ) ayant une meilleure "finesse" que 1/RAND_MAX. (J'entends par la que le pas entre deux aléatoires consécutifs doit être de moins de 1/RAND_MAX.)  
Voici ce que j'utilise pour le moment :  
 
void initialiser_aleatoire(void)
{
 time_t t;
 srand((unsigned)time(&t));
}
 
double aleatoire_ie(void)
{
 return(((double)rand()/(double)((RAND_MAX)+1)));  /* entre 0 et 0.99999..., juste car x/x+1 = 1- 1/x+1 */
}
 
double aleatoire_ii(void)
{
 return(((double)rand()/(double)RAND_MAX));    /* entre 0 et 1 */
}
 
Je pensais mettre deux aleatoires en binaire bout a bout et lire le nombre ainsi formé, mais vu la portée de RAND_MAX qui est codé sur 16 bits, je dois avoir un bit de signe au début qu'il faudrait que j'élimine. Si quelqu'un a une autre méthode (rapide,  c'est pour une simul de monte carlo et la fonction va être appelée plusieurs milliards de fois)
Merci !

Reply

Marsh Posté le 02-04-2001 à 14:49:25   

Reply

Marsh Posté le 02-04-2001 à 16:05:09    

il me semble que rand renvoie un int donc 32 bits...
sinon si rand renvoie un 16bits signe positif, donc 15bits
fait (supposant int 32 bits)
int rnd1 = rand();
int rnd2 = rand();
int rnd = ((rnd1<<15)|rnd2); // verifier << ou >>
sur 30 bits donc positif...
 
PS il n'y a plus (beaucoup) de machines pour lesquelles int est 16 bits...

Reply

Marsh Posté le 02-04-2001 à 21:22:59    

... je m'interroge :  
 
dans stdlib.h j'ai :  
 
/* Maximum value that can be returned by the rand function. */
#define RAND_MAX 0x7fff
 
que je n'ai pas réussi à modifier. (refuse de recompiler le .h)
 
sur un vieux borland C j'ai vu qu'il existait la fonction random qui pourrait fonctionner, je vais tester.
 
Concernant la représentation de rand_max : je croyais que c'était 16 bits avec un bit de signe soit 2^15 -1 combinaisons ou bien de 0 a 32867
Veux tu dire que cela serait en fait 32 bits (car int en VC++6)avec 17 bits à éliminer lors de mon décalage ? Désolé de mon piètre niveau en info, je suis de formation physique/microelectronique et je n'ai plus de cours de programmation...
 
Merci pour ta réponse !

Reply

Marsh Posté le 03-04-2001 à 02:11:55    

Bonjour à toutes et à tous,
 
En sachant que rand() retourne une valeur positive entre [0;RAND_MAX], on peut écrire :
 
Pour une résolution de 30 bits (si RAND_MAX=0x7fff) :
 
double rm,x1,x2,x;
rm=RAND_MAX;
x1=rand();
x2=rand();
x=( x1*(rm+1)+x2 ) / ( rm*(rm+1)+rm );
 
Pour une résolution de 45 bits :
 
double rm,x1,x2,x3,x;
rm=RAND_MAX;
x1=rand();
x2=rand();
x3=rand();
x=( (x1*(rm+1)+x2)*(rm+1)+x3 ) / ( (rm*(rm+1)+rm)*(rm+1)+rm );
 
On remarque bien que :
- si tous les xi sont tous à 0, alors x sera 0
- si tous les xi sont à RAND_MAX, alors x sera 1 (voir simplification des expressions).
 
Donc x sera un nombre aléatoire de [0;1]
 
Salutations.

Reply

Marsh Posté le 03-04-2001 à 05:17:35    

chin jo> Ce n'est pas forcément une bonne idée de mettre "bout-à-bout" 2 nombres aléatoires. En fait, tout dépend de ce que tu fais.
 
De combien de nombres aléatoires as-tu besoin ? Si c'est quelques dizaines ou quelques centaines, tu peux y aller, pas de problème. Si c'est plusieurs milliers ou dizaines de milliers, là, il faut que tu changes d'échelle. Un générateur 16 bits n'est plus suffisant, et il faut passer au générateur 48 bits. Si tu es sur UNIX, tu en as un quelque part dans les librairies standard. Si tu es sur NT, dommage... il va falloir t'en programmer un.
 
Si tu es intéressé, je peux te donner ce qu'il faut, j'ai déjà été confronté au problème.

Reply

Marsh Posté le 03-04-2001 à 11:03:14    

bonjour a tous !  
tfj57 : pas mal, je n'avais pas pensé à cette méthode, je regarderai ce que ca donne en temps de calcul et en efficacité
 
BifaceMcLeOD : Le prg devra tourner sous NT et 98...
La simulation en question porte sur une emission de particules et je tire au minimum 4 aleatoires par particule * entre 40 millions et 5 milliards de particules suivant la précision voulue dans la simul ! ( mais la fonction rand "de base" parait suffir pour 3 de ces 4 tirages )
merci a tous !

Reply

Marsh Posté le 03-04-2001 à 14:47:46    

chin jo a écrit a écrit :

... je m'interroge :  
 
dans stdlib.h j'ai :  
 
/* Maximum value that can be returned by the rand function. */
#define RAND_MAX 0x7fff
 
que je n'ai pas réussi à modifier. (refuse de recompiler le .h)
 
sur un vieux borland C j'ai vu qu'il existait la fonction random qui pourrait fonctionner, je vais tester.
 
Concernant la représentation de rand_max : je croyais que c'était 16 bits avec un bit de signe soit 2^15 -1 combinaisons ou bien de 0 a 32867
Veux tu dire que cela serait en fait 32 bits (car int en VC++6)avec 17 bits à éliminer lors de mon décalage ? Désolé de mon piètre niveau en info, je suis de formation physique/microelectronique et je n'ai plus de cours de programmation...
 
Merci pour ta réponse !




Oui rand renvoie un int si je ne me trompe pas. donc 32 bits au moins sur la plupart des machines actuelles.
 
il ne faut jamais modifier les headers standard, jamais
 
J'ai peur que les obj borland ne soient pas compatible avec les obj Microsoft...
 
la solution de tfj57 est la meme que la mienne avec des multiplication et des additions, donc plus longue d'execution (+ portable bien que...) a moins que le compilo ne change de lui meme tout cela en decalages de bits sur des entiers (pas dit).
 
par contre je serais curieux de connaitre les raisons de BifaceMcLeod, il s'agit sans doute de la suite qui n'est pas vraiment aleatoire, et alors la probabilite d'avoir 0 en 30 bits est plus faible car la probabilite d'avoir deux zero (15 bits) qui se suivent est plus faible que celle d'avoir deux zero (15 bits) et donc il apparait des differences de probabilites entre les differentes valeurs obtenues.
 
une autre solution pourrait etre alors :
unsigned int *random = (int *)(rnd()<<2);
ce qui revient a lire 32 bits aleatoirement en memoire...
Je ne suis pas sur que ca ne provoque pas la colere du DrWatson...

Reply

Marsh Posté le 03-04-2001 à 16:51:54    

BENB a écrit a écrit :

 
 
une autre solution pourrait etre alors :
unsigned int *random = (int *)(rnd()<<2);
ce qui revient a lire 32 bits aleatoirement en memoire...
Je ne suis pas sur que ca ne provoque pas la colere du DrWatson...




 
Ben y en a qui se font pas chier, aller lire comme ca n'importe ou dans la memoire...

Reply

Marsh Posté le 03-04-2001 à 20:26:27    

Les fonctions srand() et rand() de stdlib.h sont définies comme suit (pour VC++ 6.0 : Win32 Console Application) :
 
int x=1;
 
void srand(int n)
{
     x=n;
}
 
int rand()
{
      x=x*0x343fd+0x269ec3;
      return((x>>16)&0x7fff);
}
 
Comparer avec les fonctions originales, vous allez voir !!!
 
Vu la complexité de ces fonctions, je pense donc qu'il ne faut pas se casser la tête avec ces fonctions originales, il suffit de les redéfinir, par exemple comme suit :
 
unsigned int x=1;
 
// Initialisation
void srandx(unsigned int n)
{
      x=n;
}
 
// pour un nombre 32 bits
inline unsigned int randx()
{
      x=x*0x343fd+0x269ec3;
      return(x);
}
 
// pour un nombre entre [0;1[
inline double drandx()
{
      const double rm=4294967296.0;
      x=x*0x343fd+0x269ec3;
      return(x/rm);
}
 
On peut aussi modifier les constantes ...
 
Salutations

Reply

Marsh Posté le 04-04-2001 à 00:43:05    

chin jo a écrit a écrit :

BifaceMcLeOD : Le prg devra tourner sous NT et 98...
La simulation en question porte sur une emission de particules et je tire au minimum 4 aleatoires par particule * entre 40 millions et 5 milliards de particules suivant la précision voulue dans la simul ! ( mais la fonction rand "de base" parait suffir pour 3 de ces 4 tirages )
merci a tous !




Oui, clairement, un générateur de nombres aléatoires 16 bits ne peut plus être considéré comme vraiment aléatoire avec un tel nombre de tirages.
 
Un rappel : les générateurs dits "pseudo-alétoires" utilisent une suite (mathématique) pour générer ces nombres. Mais il y a un moment où on atteint la limite du caractère apparemment aléatoire, et là, avec un tel nombre de tirages, tu la dépasses allègrement !
Tu vas alors te retrouver alors avec des motifs de nombres qui se répètent régulièrement, et les nombres eux-mêmes n'ont plus grand chose d'aléatoire (certains nombres reviennent sans cesse alors que d'autres semblent "inatteignables" par le générateur)...

 

[edit]--Message édité par BifaceMcLeOD--[/edit]

Reply

Marsh Posté le 04-04-2001 à 00:43:05   

Reply

Marsh Posté le 04-04-2001 à 00:55:59    

chin jo> Check your mail...

Reply

Marsh Posté le 17-04-2001 à 00:32:41    

Suite du problème…
 
Rappel :
 
J'ai besoin de nombres aléatoires pour une simulation (émission et dépôt de particules). La fonction  rand() est appelée plusieurs fois pour chaque particule simulée, pour des caractéristiques différentes (position, vitesse, énergie, angle...). Le nombre de caractéristique par particule variant suivant la particule considérée une "périodicité" due à  
la faible portée de rand_max comparée au nombre de particules simulées n'est pas trop gênante. Cependant  pour l'une des caractéristiques il me faut un nombre entre 0 et 1 (inclus) avec un pas plus fin que 1/rand_max  
 
Après tests toutes les solutions de type décalage  
 
int aleatoire_30(void)
{
 int rnd1,rnd2,rnd_30;
 
 rnd1=rand();
 //initialiser_aleatoire();
 rnd2=rand();
 rnd_30=(rnd1<<15|rnd2);
 return(rnd_30);
}
 
sont fausses tant que l'on ne fait pas une réinitialisation entre les appels à rand() ;
 et ce même en ne conservant que 8 bits des int que rand() renvoie.
tirage d'un rand sur 24 bits en 3*8bits avec  
 
int aleatoire_24(void)
{
 int rnd1,rnd2,rnd3,rnd_30,cst=255;
 
 rnd1=(rand()&cst);
 rnd2=(rand()&cst);
 rnd3=(rand()&cst);
 rnd_24=((rnd3<<16)|(rnd2<<8)|(rnd1));
 return(rnd_24);
}
 
Une périodicité ressort du fait des appels consécutifs a rand().
La réinitialisation est délicate à effectuer car elle est coûteuse en temps de calcul et il faut faire plus raffiné qu'un appel d'horloge simple car la fonction est appelée à très haute fréquence.
 
Biface McLeO.D.
-> J'essaierai volontiers ton code (en fait je compte même beaucoup sur lui...) mais mes quelques connaissances de C ne m'ont pas permis de comprendre comment l'inclure dans ma simul !  
Si tu pouvais me dire comment appeler la fonction qui me renvoie un aléatoire sur 48 bit dans mon " C/C++ source file " (visual C++ 6.0, c'est un petit peu fort pour moi et pour faire juste du C, mais je ne désespère pas de faire une interface plus tard) et où placer les fichiers de ton archive je t'en serais très reconnaissant.
 
Merci à tous pour votre aide…

Reply

Marsh Posté le 17-04-2001 à 00:46:48    

chin jo> Ben yavékad'-mander !  :sarcastic:  :D  
 
Bon, c'est vrai que ça manque un peu de doc utilisateur, ce que je t'ai envoyé. Normalement un "man drand48" doit résoudre le problème, mais sur NT, évidemment... :o  
 
Voici la doc MAN des fonction drand48. Tu devrais en déduire la doc des miennes assez facilement :

Citation :


C Library Functions                                   drand48(3C)
 
NAME
     drand48,  erand48,  lrand48,  nrand48,   mrand48,   jrand48,
     srand48,  seed48,  lcong48  - generate uniformly distributed
     pseudo-random numbers
 
SYNOPSIS
     #include <stdlib.h>
 
     double drand48(void)
 
     double erand48(unsigned short xi[3] );
 
     long lrand48(void)
 
     long nrand48(unsigned short xi[3] );
 
     long mrand48(void)
 
     long jrand48(unsigned short xi[3] );
 
     void srand48(long seedval);
 
     unsigned short *seed48(unsigned short seed16v[3] );
 
     void lcong48(unsigned short param[7] );
 
DESCRIPTION
     This family of  functions  generates  pseudo-random  numbers
     using  the  well-known linear congruential algorithm and 48-
     bit integer arithmetic.
 
     Functions  drand48()  and  erand48()   return   non-negative
     double-precision floating-point values uniformly distributed
     over the interval [0.0, 1.0].
 
     Functions lrand48() and nrand48() return  non-negative  long
     integers uniformly distributed over the interval [0, 2**31].
 
     Functions  mrand48()  and  jrand48()  return   signed   long
     integers uniformly distributed over the interval [-2**31 , 2
    **31 ].
 
     Functions srand48(), seed48(), and lcong48() are initializa-
     tion  entry  points,  one  of which should be invoked before
     either  drand48(),  lrand48(),  or  mrand48()   is   called.
     (Although  it  is not recommended practice, constant default
     initializer  values  will  be  supplied   automatically   if
     drand48(), lrand48(), or mrand48() is called without a prior
     call to an initialization entry point.) Functions erand48(),
     nrand48(),  and  jrand48()  do not require an initialization
     entry point to be called first.
 
 
 
 
SunOS 5.7           Last change: 22 Jan 1993                    1
 
 
 
 
 
 
C Library Functions                                   drand48(3C)
 
 
 
     All the routines work by generating  a  sequence  of  48-bit
     integer  values,  Xi  , according to the linear congruential
     formula
 
      X n+1= (aX n+c)  mod m n>=0.
 
     The parameter m = 2**48; hence 48-bit integer arithmetic  is
     performed. Unless lcong48() has been invoked, the multiplier
     value a and the addend value c are given by
 
               a = 5DEECE66D16 = 2736731631558
               c = B16 = 138 .
 
     The value  returned  by  any  of  the  functions  drand48(),
     erand48(),  lrand48(), nrand48(), mrand48(), or jrand48() is
     computed by first generating  the  next  48-bit  Xi  in  the
     sequence.  Then the appropriate number of bits, according to
     the type of data item to be returned, are  copied  from  the
     high-order  (leftmost)  bits  of Xi and transformed into the
     returned value.
 
     The functions drand48(), lrand48(), and mrand48() store  the
     last  48-bit  Xi generated in an internal buffer. Xi must be
     initialized prior to being invoked. The functions erand48(),
     nrand48(), and jrand48() require the calling program to pro-
     vide storage for the  successive  Xi  values  in  the  array
     specified  as  an  argument  when the functions are invoked.
     These routines do not have to be  initialized;  the  calling
     program  must place the desired initial value of Xi into the
     array and pass it as an argument. By using  different  argu-
     ments,  functions  erand48(), nrand48(), and jrand48() allow
     separate modules of a  large  program  to  generate  several
     independent  streams  of pseudo-random numbers, that is, the
     sequence of numbers in each stream will not depend upon how
     many times the routines have been called to generate numbers
     for the other streams.
 
     The initializer function srand48() sets  the  high-order  32
     bits  of  Xi  to  the 32 bits contained in its argument. The
     low-order 16 bits of Xi  are  set  to  the  arbitrary  value
     330E16 .
 
     The initializer function seed48() sets the value  of  Xi  to
     the  48-bit  value specified in the argument array. In addi-
     tion, the previous value of  Xi  is  copied  into  a  48-bit
     internal  buffer,  used  only  by seed48(), and a pointer to
     this buffer is the value returned by seed48(). This returned
     pointer,  which can just be ignored if not needed, is useful
     if a program is to be restarted from a given point  at  some
     future  time  - use the pointer to get at and store the last
     Xi value, and then use  this  value  to  reinitialize  using
 
 
 
SunOS 5.7           Last change: 22 Jan 1993                    2
 
 
 
 
 
 
C Library Functions                                   drand48(3C)
 
 
 
     seed48() when the program is restarted.
 
     The initialization function lcong48()  allows  the  user  to
     specify  the  initial  Xi  the  multiplier  value a, and the
     addend value c. Argument array elements  param[0-2]  specify
     Xi, param[3-5] specify the multiplier a, and param[6] speci-
     fies the 16-bit addend c. After lcong48() has been called, a
     subsequent call to either srand48() or seed48() will restore
     the ``standard'' multiplier and  addend  values,  a  and  c,
     specified above.
 
ATTRIBUTES
     See attributes(5) for descriptions of the  following  attri-
     butes:
     ___________________________________________________________
    |       ATTRIBUTE TYPE        |       ATTRIBUTE VALUE       |
    |_____________________________|_____________________________|
    | MT-Level                    | Safe                        |
    |_____________________________|_____________________________|
 
SEE ALSO
     rand(3C), attributes(5)


 
Ah oui : désolé, les Man pages, c'est en anglais... Mais si tu as besoin d'aide pour ça, je peux t'aider aussi.

Reply

Marsh Posté le 17-04-2001 à 00:58:20    

Bon, si j'ai bien compris, dans ton cas, tu dois pouvoir te contenter des fonctions srand48() et drand48().
Les fonctions qui attendent un tableau de 3 entiers en argument sont utiles si tu veux faire utiliser un générateur aléatoire indépendant à différents threads en parallèle. Ce tableau est une sorte de "contexte" pour ces fonctions, qu'elles utilisent d'une fois sur l'autre ; mais tu n'as pas besoin d'aller voir ce qu'il contient, tu te contentes de l'initialiser avec rand48() et de le passer à chaque appel qui génère un nouveau nombre aléatoire.
 
Mais de toute façon, si tu n'a qu'un seul thread (ce qui a l'air d'être le cas), le module est capable de gérer ça pour toi.
 
Pour utiliser mon module, tu peux mettre les 5 fichiers dans ton répertoire de travail (o2_rand48.c et int48.cc sont les 2 fichiers à faire compiler au compilateur). Et dans chaque fichier C/C++ qui utilise le générateur aléatoire (il ne devrait pas y en avoir des masses), tu mets un :
#include "rand48.h"
 
Ensuite, tu peux utiliser ces fonctions comme si elles faisaient partie de la librairie standard (et donc remplacer les appels actuels à srand() et rand() par des appels à srand48() et drand48()).

 

[edit]--Message édité par BifaceMcLeOD--[/edit]

Reply

Marsh Posté le 17-04-2001 à 10:40:28    

trop gentil !!!
je teste (demain, je ne suis pas à mon bureau aujourd'hui) ET j'essaye de comprendre avec la doc. La reconnaissance éternelle n'est pas loin...

Reply

Marsh Posté le 21-05-2001 à 19:51:33    

up ! D'autres topics référencent celui-ci...

Reply

Marsh Posté le 21-05-2001 à 20:28:38    

C'est un peu hors sujet, mais certains chipsets Intel incluent un générateur de nombres aléatoires, appelé RNG.
Son accès n'est pas immédiat, il faut développer sa propre interface ...
Plus d'infos sur : http://developer.intel.com/design/security/rng/rng.htm
 
Voilà c'était juste pour info.

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed