Faire de l'aléatoire convenablement

Dans un intervalle

a marqué ce sujet comme résolu.

C'est forcément en C que tu dois écrire ton code ? En C++ Mersenne-Twister est standard depuis C++11, en Fortran tu as random_number avec un vrai générateur de nombrs alératoires dans gfortran et ifort. Et avec rust, c'est dans une lib sympa !

Je sais pas coder en C++ (je fais du C with class, donc autant faire du C), j'aime pas le Fortran (oui, je sais, tout les codes scientifiques de l'univers sont écrit en Fortran1, je lis le Fortran et j'aime pas ça) et je connais pas le Rust. Et je savais très bien que quelqu'un allais encore me sortir le C++11 et son (ses?) super-générateur-de-nombre-aléatoire-de-la-mort-qui-tue-disponible-en-25-milliards-de-possibilités-que-tu-sais-pas-quoi-choisir-et-les-défenseurs-de-C++-me-tapent-sur-le-système-avec-ça.2

C'est le genre de commentaires qui m'ennuient profondément. Je fais du C si je veux, et c'est pas la question. Sinon, j'aurais pas posé la question et j'aurais fait du Python (pas pour rien que je cherche l'équivalent C de la fonction Python correspondante).

Edit @Kass'Peuk: Je tape parce que on m'as encore sorti le classique "change de langage, le mien est mieux, t'as vu". Le reste est de la mauvaise foi tout à fait assumée.


  1. et j'exagère à peine. 

  2. sérieusement, sans être mathématicien et savoir manifestement quoi correspond à quoi, comment tu fais un choix correct ? Et je suis chimiste, pas mathématicien, mon cours de stat' c'est arrêté bien avant ça ! 

+1 -1

Yep, mais la page wikipédia précise que sous Linux, l'appel à /dev/random est bloquant. À priori, j'ai besoin de générer une quantité importante de nombre aléatoire à la seconde (bon, pas des millions, mais une centaine dans le meilleur des cas), et j'ai peur que la sortie ne suive pas1. D'autant qu'à terme, c'est pour faire tourner sur un super-calculateur et plus sur ma machine, donc je ne peut pas prévoir le temps de réaction. Mais c'est une alternative.

pierre_24

Hum c'est curieux, j'en suis à plusieurs milliers d'octets à la seconde avec /dev/random chez moi. Les cas où /dev/random se bloquait je ne l'ai rencontré qu'en embarqué en fait (où là, ça peut être la fin des temps pour générer une clé GPG…).

Cela dépend de la machine et de son utilisation pour la rapidité du processus. Car le noyau se base sur énormément d'éléments pour générer l'entropie et le matériel tout comme les processeurs récents ont parfois des puces dédiées à cette question ce qui est vraiment confortable. Après en contexte supercalculateur c'est en effet délicat à prédire…


  1. en tout cas, cat /dev/random renvoie très peu de valeur à la seconde, peut être trois caractères sur ma machine. 

+0 -0

Hum c'est curieux, j'en suis à plusieurs milliers d'octets à la seconde avec /dev/random chez moi. Les cas où /dev/random se bloquait je ne l'ai rencontré qu'en embarqué en fait (où là, ça peut être la fin des temps pour générer une clé GPG…).

Renault

Je dois avoir une machine pourrie pour générer de l'entropie, alors, ou m'y prendre très mal. Le code suivant:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// gcc -o r r.c -std=c99
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char ** argv) {

    FILE* r = fopen("/dev/random", "r");

    for (unsigned int j = 0; j < 400; ++j) {
        fgetc(r);
    }

    fclose(r);
}

Me donne,

1
2
3
4
5
$ time ./r

real    1m39.703s
user    0m0.000s
sys 0m0.004s

Et si je change un peu (j'ai pas été gentil avec fgetc(), d'accord),

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// gcc -o r r.c -std=c99
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char ** argv) {

    FILE* r = fopen("/dev/random", "r");

    char buff[4];

    for (unsigned int j = 0; j < 100; ++j) {
        fgets(buff, 4, r);
    }

    fclose(r);
}

J'obtient,

1
2
3
4
5
$ time ./r

real    0m37.711s
user    0m0.000s
sys 0m0.004s

Je dois très mal m'y prendre ^^

HS :

(@pierre_24 : c'est pas parce que les générateurs ne te servent pas tous qu'il faut taper sur les gens qui en ont besoin, t'as une composition générateur + distribution. Pour le premier, la doc fournit un "default" si tu sais pas quoi prendre et que tu t'en fous. Et après, pour la distribution, ben tu sais de quoi t'as besoin - en tout cas d'après ton sujet - et le mot "uniform" est relativement compréhensible).

@pierre_24: c'est ta machine le problème, le même code chez moi s'exécute instantanément. Avec 10 000 itérations de boucle de ton deuxième code, il me faut 1 seconde.

À noter que /dev/urandom (version non bloquante de /dev/random) peut certainement suffire.

Cela dépend de la criticité du problème. Je ne sais pas à quel point cela peut être inefficace dans son cas d'usage.

+0 -0

Si tu n'as pas le choix, j'ai collecté quelques codes Monte-Carlo et j'en ai au moins un en C, avec une implémentation de générateur de nombres aléatoires. je dois pouvoir t'en passer une partie, à vérifier avec la licence.

Idem. J'ai une version avec SFMT (licence BSD 3-clause) qui tient en quelques lignes et qui s'utilise aussi facilement que rand.

+0 -0

Je sais pas coder en C++ (je fais du C with class, donc autant faire du C), j'aime pas le Fortran et je connais pas le Rust.

Pas de problème ! Je demandais juste au cas où, parce la gestion des nombre aléatoires est plus simple dans tous ces langages, et contrairement à Python ils gardent la vitesse d’exécution du C. Mais si tu a choisit le C en connaissance de cause personne ne te demande de changer !

je sais, tout les codes scientifiques de l'univers sont écrit en Fortran

Tellement … Le code sur lequel je bosse au labo est en C++, et ça pose déjà des problèmes à certaines personnes =/

Et je savais très bien que quelqu'un allais encore me sortir le C++11 et son (ses?) super-générateur-de-nombre-aléatoire-de-la-mort-qui-tue-disponible-en-25-milliards-de-possibilités-que-tu-sais-pas-quoi-choisir-et-les-défenseurs-de-C++-me-tapent-sur-le-système-avec-ça sérieusement, sans être mathématicien et savoir manifestement quoi correspond à quoi, comment tu fais un choix correct ? Et je suis chimiste, pas mathématicien, mon cours de stat' c'est arrêté bien avant ça !

Je suis chimiste aussi, je n'ai jamais eu de cours de stat de ma vie. Pour le choix, j'ai fait au plus simple: j'ai demandé à mon directeur de thèse quoi utiliser.

C'est le genre de commentaires qui m'ennuient profondément. Je fais du C si je veux, et c'est pas la question. Sinon, j'aurais pas posé la question et j'aurais fait du Python (pas pour rien que je cherche l'équivalent C de la fonction Python correspondante).

Désolé si je t'ai ennuyé, ce n'étais vraiment pas mon but.

+4 -0

Salut,

Et si je change un peu (j'ai pas été gentil avec fgetc(), d'accord)

pierre_24

A priori, cela n'a aucune incidence. Cela en aurait une si tu utilisais l'appel système read(), mais les fonctions standard C utilises un tampon pour stocker les données (sous Linux il me semble qu'elles sont lues par bloc de 8192 octets). Les 400 lectures de caractères se font normalement toutes depuis le tampon.

Note, même si cela ne change rien sous Linux, tu devrais normalement ouvrir le fichier en mode binaire avec rb.

J'ai aussi lu à propos de drand48(), mais je suis pas sous OpenBSD.

pierre_24

Voilà peut-être le problème. :p
Plus sérieusement, voici le code de la fonction sous OpenBSD (pour les curieux, tout se situe sur leur CVS dans le dossier /src/lib/libc/stdlib/).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
double
drand48(void)
{
    if (__rand48_deterministic == 0) {
        unsigned short rseed[3];

        arc4random_buf(rseed, sizeof rseed);
        return ldexp((double) rseed[0], -48) +
               ldexp((double) rseed[1], -32) +
               ldexp((double) rseed[2], -16);
    }
    return erand48(__rand48_seed);
}

La fonction arc4random_buf() (elle existe aussi sous Linux, mais elle ne fonctionne pas de la même manière que sous OpenBSD) repose sur l'appel système getentropy() (qui porte bien son nom) et rempli le tampon fourni avec le nombre de bytes spécifiés. Pour le reste, la fonction ldexp() multiplie le premier argument par deux élevé à la puissance du deuxième argument (autrement dit a * (2^b)).

Pour ce qui est de l'obtention de nombres aléatoires, je rejoins Aabu quant à l'utilisation du pseudo-périphérique /dev/urandom qui, à l'inverse de son homologue sans « u », n'est pas bloquant et produit une suite pseudo-aléatoire (comme rand(), mais a priori en mieux) en utilisant un peu de l'entropie récoltée par le noyau.

La fonction Linux ioctl sert à ça (accéder aux fichiers spéciaux) il me semble.

Bermudes

Elle est surtout utilisée lors de la manipulation des terminaux, pour le reste, les fonctions habituelles font l'affaire. ;)

+0 -0

Tu fais erreur sur ce point. Comme déjà dit, RAND_MAX fait bien partie de l'intervalle, et sa valeur est usuellement de la forme 2n−1 (l'implémentation étant alors nombre_suivant = (nombre_actuel * A + B) & RAND_MAX)

Oui, je l'ai dit aussitôt que j'ai constaté mon erreur.

+0 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte