OVH Cloud OVH Cloud

demande d'explication

21 réponses
Avatar
PasDeSpam
j'utilise ce code mais je le comprend pas :(

// random generator function:
ptrdiff_t myrandom (ptrdiff_t i) { return rand()%i;}

une fonction "myrandom" qui prend comme argument un ptrdiff_t et qui
retourne un ptrdiff_t (pas d'argument par defaut)


// pointer object to it:
ptrdiff_t (*p_myrandom)(ptrdiff_t) = myrandom;

un pointeur sur la fonction


son utilisation
random_shuffle (random_position.begin(), random_position.end(),
p_myrandom);

ou est l'argument?

merci pou vos explications.

10 réponses

1 2 3
Avatar
Sylvain SF
Richard Delorme a écrit :

et je ne vois pas de forte raison à cela, on pourrait permuter
chaque élément avec n'importe quel autre (et non pas avec un des
éléments précédents).



Pour avoir une distribution aléatoire. Si à chaque fois tu
échanges un élément avec n'importe quel autre, tu vas avoir n^n
différents mélanges. Or, il y a exactement n! ordres possible.
Au moins que n! est facteur de n^n (ce qui n'est pas possible),
il y aurait certains ordres qui ressortent plus que d'autres.



On peut appliquer le même raisonnement à rand() % n; qui ne retourne un
nombre (pseudo-)aléatoire entre [0, n[, que si (RAND_MAX + 1) est un
multiple de n. Dans le cas extrème ou n vaut RAND_MAX, 0 a deux fois
plus de chance d'être tiré qu'un nombre parmi 1 à RAND_MAX - 1.



c'est un des biais de la méthode.
un autre est la génération d'un "petit" nombre aléatoire (inférieure
à 8 ou 16) ici un modulo appliqué sur la sortie de rand() est très
biaisé; une meilleure distribution serait de générer 32 ou 64 bits
de random, de compter les bits à '1' et d'appliquer le modulo sur
ce nombre.

Sylvain.
Avatar
Sylvain SF
James Kanze a écrit :

random_shuffle(myvector.begin(), myvector.end(),
pointer_to_unary_function<int, int>(myrandom));



Pourquoi ?



pour vérifier le prototype de 'myrandom' !?...

Il s'en suit que l'idiome plus ou moins classique serait :

struct MyRandom {
ptrdiff_t operator()( ptrdiff_t i ){
return rand() % i ;
}
};
random_shuffle( v.begin(), v.end(), MyRandom() );



là c'est propre !

Sinon, on s'en passerait du typedef en général. Même avec la
fonction,
random_shuffle( v.begin(), v.end(), myRandom ) ;
suffit largement, et est plus idiomatique.



oui, la lecture initiale du proto de random_shuffle m'a
"troublé" et ma première écriture ne compilait pas avec
"cela" (avec quelque chose qui avait cette intention mais
où j'avais plus sûrement tapé une ânerie).

C'est probablement lié à l'histoire.
[...] Est-ce que le contrat a toujours été tel ?



certes.

Une dernière remarque : rand() n'est pas un bon choix ici comme
fonction de génération aléatoire.



ça ! ;-)

Selon la norme, rand() renvoie une valeur entre 0 et RAND_MAX,
et RAND_MAX n'est pas forcément supérieur à 32767.



oui, surprenant de ne pas trouver de fonction travaillant sur
LONG_MAX (sur 31 bits) et LLONG_MAX (63 bits), j'ai mal lu ou
cela n'existe pas ?

Alors, pour peut que le tableau devient un peu grand...



un vector est compatible avec des indexes 64 bits ?

(dans vector, ce n'est pas l'argument que je ne trouve pas
mais les types internes, dont la définition de difference_type).

Sylvain.
Avatar
James Kanze
On May 24, 6:38 pm, Sylvain SF wrote:
James Kanze a écrit :



[...]
> Sinon, on s'en passerait du typedef en général. Même avec la
> fonction,
> random_shuffle( v.begin(), v.end(), myRandom ) ;
> suffit largement, et est plus idiomatique.



oui, la lecture initiale du proto de random_shuffle m'a
"troublé" et ma première écriture ne compilait pas avec "cela"
(avec quelque chose qui avait cette intention mais où j'avais
plus sûrement tapé une ânerie).



Est-ce que ta fonction avait été surchargée ? Du fait que
random_shuffle est un template, et que le type du troisième
paramètre est un template, la résolution du surcharge ne
fonctionne pas.

> C'est probablement lié à l'histoire.
> [...] Est-ce que le contrat a toujours été tel ?



certes.



Certes ? Tu as vérifié dans les versions anciennes
(pré-standard) de la STL ?

[...]
> Selon la norme, rand() renvoie une valeur entre 0 et RAND_MAX,
> et RAND_MAX n'est pas forcément supérieur à 32767.



oui, surprenant de ne pas trouver de fonction travaillant sur
LONG_MAX (sur 31 bits) et LLONG_MAX (63 bits), j'ai mal lu ou
cela n'existe pas ?



Il y en aurait dans la prochaine version de la norme. En
attendant, boost::random est ton ami.

> Alors, pour peut que le tableau devient un peu grand...



un vector est compatible avec des indexes 64 bits ?



Un vector peut, en principe, contenir jusqu'à
numeric_limits<vector<>::size_type>::max() éléments.
vector<>::size_type dépend de l'allocator, par défaut, c'est un
size_t (qui a 64 bits sur ma machine), mais on peut (encore, en
principe) créer un allocator qui définit un size_type encore
plus grand.

(dans vector, ce n'est pas l'argument que je ne trouve pas
mais les types internes, dont la définition de
difference_type).



Il les reprend de l'allocator.

--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Avatar
James Kanze
On May 24, 5:52 pm, Richard Delorme wrote:
James Kanze a écrit :



>> 2 remarques:
>> - ce n'est pas la taille du vector qui est transmis à myrandom mais
>> l'indice courant; ainsi lors du parcours des éléments du vector,
>> l'élement i est échangé avec un des éléments de rang 0 à i (non N)
>> -- et je ne vois pas de forte raison à cela, on pourrait permuter
>> chaque élément avec n'importe quel autre (et non pas avec un des
>> éléments précédents).



> Pour avoir une distribution aléatoire. Si à chaque fois tu
> échanges un élément avec n'importe quel autre, tu vas avoir n^n
> différents mélanges. Or, il y a exactement n! ordres possible.
> Au moins que n! est facteur de n^n (ce qui n'est pas possible),
> il y aurait certains ordres qui ressortent plus que d'autres.



On peut appliquer le même raisonnement à rand() % n; qui ne
retourne un nombre (pseudo-)aléatoire entre [0, n[, que si
(RAND_MAX + 1) est un multiple de n. Dans le cas extrème ou n
vaut RAND_MAX, 0 a deux fois plus de chance d'être tiré qu'un
nombre parmi 1 à RAND_MAX - 1.



Certe. Mais tu peux résoudre ce problème facilement. Alors que
la seule solution pour le problème de RAND_MAX, c'est de ne pas
utiliser rand().

--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Avatar
James Kanze
On May 24, 6:13 pm, Sylvain SF wrote:
Richard Delorme a écrit :
>>> et je ne vois pas de forte raison à cela, on pourrait
>>> permuter chaque élément avec n'importe quel autre (et
>>> non pas avec un des éléments précédents).



>> Pour avoir une distribution aléatoire. Si à chaque fois tu
>> échanges un élément avec n'importe quel autre, tu vas avoir
>> n^n différents mélanges. Or, il y a exactement n! ordres
>> possible. Au moins que n! est facteur de n^n (ce qui n'est
>> pas possible), il y aurait certains ordres qui ressortent
>> plus que d'autres.



> On peut appliquer le même raisonnement à rand() % n; qui ne
> retourne un nombre (pseudo-)aléatoire entre [0, n[, que si
> (RAND_MAX + 1) est un multiple de n. Dans le cas extrème ou
> n vaut RAND_MAX, 0 a deux fois plus de chance d'être tiré
> qu'un nombre parmi 1 à RAND_MAX - 1.



c'est un des biais de la méthode.
un autre est la génération d'un "petit" nombre aléatoire
(inférieure à 8 ou 16) ici un modulo appliqué sur la sortie de
rand() est très biaisé;



Ça dépend beaucoup de la qualité du générateur. Avec un bon
générateur, un petit nomber doit montrer nettement moins de
biais qu'un grand.

une meilleure distribution serait de générer 32 ou 64 bits de
random, de compter les bits à '1' et d'appliquer le modulo sur
ce nombre.



Si tu peux générer vraiment les bits d'une façon aléatoire,
utiliser les n premiers bits pour un nombre d'n bits est aussi
bon que n'importe quelle autre solution.

Dans mon cas, j'utilise :

int32_t const limit = max() - max() % n ;
int32_t result = next() ;
while ( result >= limit ) {
result = next() ;
}
return result % n ;

Avec un générateur de base que j'ai écris moi-même, pour être
sûr de sa qualité.

--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Avatar
Sylvain SF
James Kanze a écrit :

C'est probablement lié à l'histoire.
[...] Est-ce que le contrat a toujours été tel ?





certes.



Certes ? Tu as vérifié dans les versions anciennes
(pré-standard) de la STL ?



non, je disais, "certes ces arguments sont recevables".

(dans vector, ce n'est pas l'argument que je ne trouve pas
mais les types internes, dont la définition de
difference_type).



Il les reprend de l'allocator.



en effet, comme un ptrdiff_t (justement) sur mon implémentation
donc un int64.

merci.
Sylvain.
Avatar
Richard Delorme
Sylvain SF a écrit :
Richard Delorme a écrit :

et je ne vois pas de forte raison à cela, on pourrait permuter
chaque élément avec n'importe quel autre (et non pas avec un des
éléments précédents).



Pour avoir une distribution aléatoire. Si à chaque fois tu
échanges un élément avec n'importe quel autre, tu vas avoir n^n
différents mélanges. Or, il y a exactement n! ordres possible.
Au moins que n! est facteur de n^n (ce qui n'est pas possible),
il y aurait certains ordres qui ressortent plus que d'autres.



On peut appliquer le même raisonnement à rand() % n; qui ne retourne
un nombre (pseudo-)aléatoire entre [0, n[, que si (RAND_MAX + 1) est
un multiple de n. Dans le cas extrème ou n vaut RAND_MAX, 0 a deux
fois plus de chance d'être tiré qu'un nombre parmi 1 à RAND_MAX - 1.



c'est un des biais de la méthode.



Oui, mais on peut faire sans biais.

un autre est la génération d'un "petit" nombre aléatoire (inférieure
à 8 ou 16) ici un modulo appliqué sur la sortie de rand() est très
biaisé;



Ceci n'arrive que sur les générateurs de mauvaise qualité (qui
malheureusement existent). Pour éviter ce problème, on préconise
généralement d'utiliser la formule : n * rand() / (RAND_MAX + 1.0).

une meilleure distribution serait de générer 32 ou 64 bits
de random, de compter les bits à '1' et d'appliquer le modulo sur
ce nombre.



Je ne pense pas. Le nombre de 1 parmi n bits aléatoires suit une
distribution binomiale (les valeurs centrales autour de n/2 sont plus
probables) or il vaut mieux une distribution uniforme.

--
Richard
Avatar
Sylvain SF
Richard Delorme a écrit :

un autre est la génération d'un "petit" nombre aléatoire (inférieure
à 8 ou 16) ici un modulo appliqué sur la sortie de rand() est très
biaisé;



Ceci n'arrive que sur les générateurs de mauvaise qualité (qui
malheureusement existent). Pour éviter ce problème, on préconise
généralement d'utiliser la formule : n * rand() / (RAND_MAX + 1.0).



cela dépend du "rand()".

pour un générateur produisant des mots 32 bits (eg Mersenne Twister),
on calcule bien: (int)((double) m * (double) n / (double) ULONG_MAX);
où 'm' est le mot généré.

mais pour un générateur produisant des octets, cela n'est pas valide.

une meilleure distribution serait de générer 32 ou 64 bits
de random, de compter les bits à '1' et d'appliquer le modulo sur
ce nombre.



Je ne pense pas. Le nombre de 1 parmi n bits aléatoires suit une
distribution binomiale (les valeurs centrales autour de n/2 sont plus
probables) or il vaut mieux une distribution uniforme.



les valeurs centrales sont plus probables, mais sont entre-elles
équiprobables; lorsque (cas extrême) n vaut 1 ou 2, les sommes
(de bits à '1') paires ou impaires sont uniformes; tandis que
les nombres pairs et impairs sur 0..RAND_MAX seront beaucoup
moins uniformément générés.

Sylvain.
Avatar
Richard Delorme
Sylvain SF a écrit :
Richard Delorme a écrit :

un autre est la génération d'un "petit" nombre aléatoire (inférieure
à 8 ou 16) ici un modulo appliqué sur la sortie de rand() est très
biaisé;



Ceci n'arrive que sur les générateurs de mauvaise qualité (qui
malheureusement existent). Pour éviter ce problème, on préconise
généralement d'utiliser la formule : n * rand() / (RAND_MAX + 1.0).



cela dépend du "rand()".

pour un générateur produisant des mots 32 bits (eg Mersenne Twister),
on calcule bien: (int)((double) m * (double) n / (double) ULONG_MAX);
où 'm' est le mot généré.

mais pour un générateur produisant des octets, cela n'est pas valide.



RAND_MAX vaut au minimum 2^15 - 1


une meilleure distribution serait de générer 32 ou 64 bits
de random, de compter les bits à '1' et d'appliquer le modulo sur
ce nombre.



Je ne pense pas. Le nombre de 1 parmi n bits aléatoires suit une
distribution binomiale (les valeurs centrales autour de n/2 sont plus
probables) or il vaut mieux une distribution uniforme.



les valeurs centrales sont plus probables, mais sont entre-elles
équiprobables;



Non.

lorsque (cas extrême) n vaut 1 ou 2, les sommes
(de bits à '1') paires ou impaires sont uniformes; tandis que
les nombres pairs et impairs sur 0..RAND_MAX seront beaucoup
moins uniformément généré



Ça peut marcher pour certains cas, mais ça ne marche pas d'une manière
générale. Par exemple, en générant 32 bits aléatoire, la somme des bits
à 1 modulo 7 donne les probabilités suivantes :
mod 7 proba
0 0.1406
1 0.1492
2 0.1530
3 0.1492
4 0.1406
5 0.1337
6 0.1337

--
Richard
Avatar
James Kanze
On May 25, 4:31 pm, Richard Delorme wrote:
Sylvain SF a écrit :
> Richard Delorme a écrit :



>>>> et je ne vois pas de forte raison à cela, on pourrait
>>>> permuter chaque élément avec n'importe quel autre (et
>>>> non pas avec un des éléments précédents).



>>> Pour avoir une distribution aléatoire. Si à chaque fois tu
>>> échanges un élément avec n'importe quel autre, tu vas
>>> avoir n^n différents mélanges. Or, il y a exactement n!
>>> ordres possible. Au moins que n! est facteur de n^n (ce
>>> qui n'est pas possible), il y aurait certains ordres qui
>>> ressortent plus que d'autres.



>> On peut appliquer le même raisonnement à rand() % n; qui ne
>> retourne un nombre (pseudo-)aléatoire entre [0, n[, que si
>> (RAND_MAX + 1) est un multiple de n. Dans le cas extrème ou
>> n vaut RAND_MAX, 0 a deux fois plus de chance d'être tiré
>> qu'un nombre parmi 1 à RAND_MAX - 1.



> c'est un des biais de la méthode.



Oui, mais on peut faire sans biais.



Plutôt : on peut éliminer le biais en aval.

> un autre est la génération d'un "petit" nombre aléatoire
> (inférieure à 8 ou 16) ici un modulo appliqué sur la sortie
> de rand() est très biaisé;



Ceci n'arrive que sur les générateurs de mauvaise qualité (qui
malheureusement existent). Pour éviter ce problème, on
préconise généralement d'utiliser la formule : n * rand() /
(RAND_MAX + 1.0).



Ce qui n'améliore pas la chose. Au contraire. Avec un générateur
à congruence linéaire (l'algorithme le plus courant), une valeur
très basse serait toujours suivie d'une valeur plutôt haute.
Avec l'expression que tu donne, on est garanti de ne jamais
avoir deux valeurs très basses de suite. (Aussi, tu as oublié
des parenthèse. Si tu multiplies avant la division, c'est une
multiplication d'entiers, et si RAND_MAX s'approche à INT_MAX --
ce qui doit être le cas avec un bon générateur -- il y a une
forte risque de débordement.)

--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
1 2 3