générateur de nombre aléatoire sous VC++ meilleur que rand ? - Programmation
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...
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 !
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.
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.
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 !
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...
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...
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
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]
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…
Marsh Posté le 17-04-2001 à 00:46:48
chin jo> Ben yavékad'-mander !
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...
Voici la doc MAN des fonction drand48. Tu devrais en déduire la doc des miennes assez facilement :
Citation : |
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.
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]
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...
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.
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 !