10
votes

Pourrait aléatoire.Randint (1,10) revenir jamais 11?

Lors de la recherche pour cette question et lecture du code SourceCode dans aléatoire.py , j'ai commencé à me demander si randrange et Randint se comporte vraiment comme "annoncé". Je suis très enclin à croire, mais la façon dont je la lisez, randrange est essentiellement implémenté comme xxx

(supposant des valeurs entières pour Démarrer et stop ), donc randrange (1, 10) doit renvoyer un nombre aléatoire entre 1 et 9.

randint (départ , arrêtez) appelle randrange (démarrage, arrêt + 1) , renvoyant ainsi un nombre compris entre 1 et 10.

Ma question est maintenant:

si aléatoire () était toujours de retourner 1.0 , puis randint (1,10) retournerait 11 , n'est-ce pas?


4 commentaires

Au fait, int (aléatoire.random () * n) n'est toujours pas un moyen idéal pour générer des entiers qui sont distribués de manière uniforme dans la plage (n) ; Il y a un biais insignifiant pour le petit N mais devient important que n devient grand. J'ai ouvert un bug de Python pour cela à bugs.python.org/issue9025


@Mark Dickinson: Merci! C'est fascinant.


@Mark Dickinson: Ce bogue est fixé à compter d'aujourd'hui .


vrai, à condition que vous utilisiez python> = 3.2.


3 Réponses :


27
votes

de aléatoire.py et les docs: xxx

) indique que l'intervalle est exclusif exclusif 1.0. C'est-à-dire qu'il ne retournera jamais 1.0.

Ceci est une convention générale en mathématiques, [ et ] est inclusif, tandis que (< / code> et ) est exclusif et les deux types de parenthèses peuvent être mélangés sous forme (a, b] ou [a, b) . Jetez un coup d'œil à Wikipedia: intervalle (mathématiques) pour une explication formelle.


5 commentaires

Je n'avais pas attrapé que ) (et même si j'avais eu, je n'aurais pas connu sa signification, alors merci beaucoup pour cette réponse perspicace).


@Tim: FYI, il existe plusieurs conventions différentes. Une autre convention couramment utilisée consiste à invoquer les accolades carrées, de sorte que [a, b [ serait un intervalle demi-ouvert équivalent à [A, B) .


Ce n'est pas assez, car il n'est pas évident que 0.0 <= x <1.0 implique que 0 <= x * n dans l'arithmétique de point flottant: en général , le résultat de la multiplication ne sera pas exactement représentable, il sera donc arrondi. Et il pourrait être arrondi. C'est concevable que cet arrondi pourrait produire n , pour x très proche de (mais pas égal à) 1.0 . Heureusement, il est possible de montrer que, en supposant que ce soit à proximité, cela ne peut jamais arriver.


Point intéressant. Même s'applique aux autres langages de programmation?


Sûr; Ce n'est rien de particulièrement spécial pour python. Avec l'arithmétique à virgule flottante IEEE 754 en mode rond à proches, vous pouvez montrer que x * y pour tout x <1,0 et tout autre positif non minuscule < Code> y . (Cela peut échouer si y est sous-formulaire ou le plus petit nombre normal positif.)



3
votes

de la documentation Python:

Presque toutes les fonctions du module dépendent de la fonction de base aléatoire (), qui génère un flotté aléatoire uniformément dans la plage semi-ouverte [0,0, 1.0).

Comme presque tous les nombres de flotteurs ..


0 commentaires

12
votes

Autres réponses ont souligné que le résultat de aléatoire () est toujours strictement moins que 1.0 ; Cependant, ce n'est que la moitié de l'histoire.

si vous informez randrange (n) comme int (aléatoire () * n) , vous aussi besoin de savoir que pour Tout flot de python x satisfaisant 0.0 <= x <1.0 , et tout entieur positif n , c'est vrai que 0.0 <= x * n , de sorte que int (x * n) est strictement inférieur à n .

Il y a deux choses qui pourraient se tromper ici: tout d'abord, lorsque nous calculons x * n , n est implicitement converti en un flotteur. Pour assez gros n , cette conversion pourrait modifier la valeur. Mais si vous regardez la source Python, vous verrez qu'il utilise uniquement le int (aléatoire () * n) méthode pour n plus petit que 2 * * 53 (ici et ci-dessous, je suppose que la plate-forme utilise IEEE 754 doubles), qui est la gamme où la conversion de n sur un flotteur est garantie pour ne pas perdre d'informations (car n peut être représenté exactement comme un flotteur).

La deuxième chose qui pourrait se tromper est que le résultat de la multiplication x * n (qui est maintenant effectué en tant que produit de flotteurs, rappelez-vous) ne sera probablement pas exactement représentable, alors Il y aura des arrondis impliqués. Si x est suffisamment proche de 1.0 , il est concevable que l'arrondi arrondira le résultat jusqu'à n lui-même.

Pour voir que cela ne peut pas arriver, il suffit de considérer la plus grande valeur possible pour x , qui est (sur presque toutes les machines que Python exécute) 1 - 2 * * -53 . Nous devons donc montrer que (1 - 2 ** - 53) * n pour notre entier positif n , car il sera toujours vrai que aléatoire () * n <= (1 - 2 ** - 53) * n .

Preuve (Sketch) Soit k être l'entier unique k tel que 2 ** (k-1) . Ensuite, le prochain flotteur vers le bas de n est n-2 ** (k-53) . Nous devons montrer que n * (1-2 ** 53) (c'est-à-dire que la valeur réelle, non fondée, du produit) est plus proche de n-2 ** (K-53 ) que pour n , de sorte qu'il sera toujours arrondi. Mais un peu d'arithmétique montre que la distance entre n * (1-2 ** - 53) à n est 2 ** - 53 * n , tandis que la distance entre n * (1-2 ** - 53) à n - 2 ** (k-53) est (2 ** k - n) * 2 ** - 53 . Mais 2 ** k - n (parce que nous avons choisi k de sorte que 2 ** (k-1) ), donc le produit est plus près de n - 2 ** (k-53) , donc il sera sera arrondi (en supposant que c'est que La plate-forme fait une forme de ronde à la plus proche).

Nous sommes donc en sécurité. Ouf!


Addendadum (2015-07-04): Le ci-dessus assume IEEE 754 Binary64 Arithmétique, avec des cravates rondes à un mode arrondi. Sur de nombreuses machines, cette hypothèse est assez sûre. Toutefois, sur des machines x86 qui utilisent le X87 FPU pour le point flottant (par exemple, diverses saveurs de Linux 32 bits), il existe une possibilité de double arrondi dans la multiplication et permettant à aléatoire () * N pour arrondi up à N dans le cas où aléatoire () renvoie la plus grande valeur possible. Le plus petit n pour lequel cela peut arriver est n = 2049 . Voir la discussion à http://bugs.python.org/issue24546 pour plus.


0 commentaires