web-dev-qa-db-fra.com

Générer un nombre aléatoire compris entre 0.1 et 1.0. Python

J'essaie de générer un nombre aléatoire compris entre 0.1 et 1.0 . Nous ne pouvons pas utiliser Rand.randint car il renvoie des entiers . Nous avons également essayé random.uniform(0.1,1.0), mais il renvoie une valeur> = 0.1 et <1.0, nous ne peut pas utiliser ceci, car notre recherche inclut aussi 1.0.

Quelqu'un d'autre a-t-il une idée de ce problème?

21
user2320923

À quel point voulez-vous que vos chiffres soient "précis"? Si vous êtes satisfait, par exemple, de 10 chiffres décimaux, vous pouvez arrondir simplement random.uniform(0.1, 1.0) à 10 chiffres. De cette façon, vous allez inclure à la fois 0.1 et 1.0:

round(random.uniform(0.1, 1.0), 10)

Pour être précis, 0.1 et 1.0 auront seulement la moitié de la probabilité comparée à tout autre nombre entre les deux et bien sûr, vous perdez tous les nombres aléatoires qui ne diffèrent qu'après 10 chiffres.

18
Elmar Peise

Random.uniform() est juste:

def uniform(self, a, b):
    "Get a random number in the range [a, b) or [a, b] depending on rounding."
    return a + (b-a) * self.random()

self.random() renvoie un nombre aléatoire dans la plage [0.0, 1.0).

Python (ainsi que de nombreux autres langages) utilise flottant Point pour représenter des nombres réels La façon dont 0.1 est représenté est décrite en détail dans ledocs :

from __future__ import division

BPF = 53 # assume IEEE 754 double-precision binary floating-point format
N = BPF + 3
assert 0.1 == 7205759403792794 / 2 ** N

Il permet de trouver un nombre aléatoire dans [0.1, 1] (inclus) en utilisant randint() sans perdre en précision:

n, m = 7205759403792794, 2 ** N
f = randint(n, m) / m

randint(n, m) renvoie un entier aléatoire dans [n, m] (inclus) Par conséquent, la méthode ci-dessus peut potentiellement renvoyer tous points flottants nombres dans [0.1, 1].

Une alternative consiste à trouver la plus petite x telle que x > 1 et à utiliser:

f = uniform(.1, x)
while f > 1:
    f = uniform(.1, x)

x devrait être la plus petite valeur pour éviter de perdre en précision et pour réduire le nombre d'appels à uniform(), par exemple:

import sys
# from itertools import count

# decimal.Decimal(1).next_plus() analog
# x = next(x for i in count(1) for x in [(2**BPF + i) / 2**BPF] if x > 1)
x = 1 + sys.float_info.epsilon

Les deux solutions préservent l'uniformité de la distribution aléatoire ( pas de biais ).

8
jfs

Vous pourriez faire ceci:

>>> import numpy as np
>>> a=.1
>>> b=np.nextafter(1,2)
>>> print(b)
1.0000000000000002
>>> [a+(b-a)*random.random() for i in range(10)]

ou, utilisez l'uniforme de numpy :

np.random.uniform(low=0.1, high=np.nextafter(1,2), size=1)

nextafter produira le prochain numéro à virgule flottante représentable spécifique à la plate-forme dans une direction. L'utilisation de random.uniform de numpy est avantageuse car il ne fait aucun doute qu'elle n'inclut pas la limite supérieure. 


Modifier 

Il semble que les commentaires de Mark Dickinson soient corrects: La documentation de Numpy est incorrecte en ce qui concerne la borne supérieure de random.uniform, qu'elle soit inclusive ou non. 

La documentation Numpy indique All values generated will be less than high.

Ceci est facilement réfuté:

>>> low=1.0
>>> high=1.0+2**-49
>>> a=np.random.uniform(low=low, high=high, size=10000)
>>> len(np.where(a==high)[0])
640

Le résultat n’est pas non plus uniforme sur cette plage limitée:

>>> for e in sorted(set(a)):
...    print('{:.16e}: {}'.format(e,len(np.where(a==e)[0])))
... 
1.0000000000000000e+00: 652
1.0000000000000002e+00: 1215
1.0000000000000004e+00: 1249
1.0000000000000007e+00: 1288
1.0000000000000009e+00: 1245
1.0000000000000011e+00: 1241
1.0000000000000013e+00: 1228
1.0000000000000016e+00: 1242
1.0000000000000018e+00: 640

Cependant, en combinant les commentaires de J.F. Sebastian et Mark Dickinson, je pense que cela fonctionne:

import numpy as np
import random 

def Rand_range(low=0,high=1,size=1):
    a=np.nextafter(low,float('-inf'))
    b=np.nextafter(high,float('inf'))
    def r():
        def rn(): 
            return a+(b-a)*random.random()

        _rtr=rn()
        while  _rtr > high:
            _rtr=rn()
        if _rtr<low: 
            _rtr=low
        return _rtr     
    return [r() for i in range(size)]

Si exécuté avec la dispersion minimale de valeurs dans le commentaire de Mark, il y a très peu de valeurs à virgule flottante discrète:

l,h=1,1+2**-48
s=10000
rands=Rand_range(l,h,s)    
se=sorted(set(rands))
if len(se)<25:
    for i,e in enumerate(se,1):
        c=rands.count(e)
        note=''
        if e==l: note='low value end point'
        if e==h: note='high value end point'
        print ('{:>2} {:.16e} {:,}, {:.4%} {}'.format(i, e, c, c/s,note))

Il produit la distribution uniforme souhaitée, y compris les points finaux:

 1 1.0000000000000000e+00 589, 5.8900% low value end point
 2 1.0000000000000002e+00 544, 5.4400% 
 3 1.0000000000000004e+00 612, 6.1200% 
 4 1.0000000000000007e+00 569, 5.6900% 
 5 1.0000000000000009e+00 593, 5.9300% 
 6 1.0000000000000011e+00 580, 5.8000% 
 7 1.0000000000000013e+00 565, 5.6500% 
 8 1.0000000000000016e+00 584, 5.8400% 
 9 1.0000000000000018e+00 603, 6.0300% 
10 1.0000000000000020e+00 589, 5.8900% 
11 1.0000000000000022e+00 597, 5.9700% 
12 1.0000000000000024e+00 591, 5.9100% 
13 1.0000000000000027e+00 572, 5.7200% 
14 1.0000000000000029e+00 619, 6.1900% 
15 1.0000000000000031e+00 593, 5.9300% 
16 1.0000000000000033e+00 592, 5.9200% 
17 1.0000000000000036e+00 608, 6.0800% high value end point

Sur les valeurs demandées par l'OP, il produit également une distribution uniforme:

import matplotlib.pyplot as plt

l,h=.1,1  
s=10000
bin_count=20
rands=Rand_range(l,h,s)  
count, bins, ignored = plt.hist(np.array(rands),bin_count)   
plt.plot(bins, np.ones_like(bins)*s/bin_count, linewidth=2, color='r')
plt.show()   

Sortie

uniform

7
dawg

Avec les informations que vous avez données (y compris les commentaires à ce jour), je ne vois toujours pas comment l'université va tester votre programme de telle sorte que la différence de 1.0 apparaisse ou non. (Je veux dire, si vous devez générer random floats, comment peuvent-ils exiger que n'importe quelle valeur particulière apparaisse?)

OK, alors mettons de côté la folie de vos exigences:

Le fait que la limite inférieure de vos flottants aléatoires soit supérieure à 0 vous donne une manière étonnamment élégante d'utiliser random.random, qui garantit les valeurs de retour dans l'intervalle [0.0, 1.0): Continuez simplement à appeler random.random, en jetant les valeurs inférieures à 0,1, sauf 0.0. Si vous obtenez réellement 0.0, retournez 1.0 à la place.

Donc quelque chose comme

from random import random

def myRandom():
    while True:
        r = random()
        if r >= 0.1:
            return r
        if r == 0.0:
            return 1.0
3
John Y

Vous pouvez utiliser random.randint simplement en faisant cette astuce:

>>> float(random.randint(1000,10000)) / 10000
0.4362

si vous voulez plus de décimales, changez simplement l'intervalle en:

(1000,10000) 4 chiffres (10000,100000) 5 chiffres Etc.

2
jabaldonedo

La manière standard serait random.random() * 0.9 + 0.1 (random.uniform() en interne le fait exactement). Cela retournera des nombres compris entre 0.1 et 1.0 sans la bordure supérieure.

Mais attendez! 0.1 (aka ¹/₁₀) n'a pas de représentation binaire claire (comme en décimal)! De toute façon, vous n'obtiendrez pas un vrai 0.1, simplement parce que l'ordinateur ne peut pas le représenter en interne. Pardon ;-)

1
Alfe

Êtes-vous incapable d'utiliser random.random()? Cela donne un nombre compris entre 0,0 et 1,0, bien que vous puissiez facilement trouver un moyen de contourner ce problème.

import random
def randomForMe():
    number = random.random()
    number = round(number, 1)
    if (number == 0):
        number = 0.1

Ce code vous donnerait un nombre compris entre 0.1 et 1.0, inclus (0.1 et 1.0 sont les deux solutions possibles). J'espère que cela t'aides.

* J'ai supposé que vous ne vouliez que des chiffres à la dixième place. Si vous voulez que ce soit différent, où j'ai tapé round(number, 1), changez 1 en 2 pour les centièmes, 3 pour les millièmes, etc.

1
erdekhayser

Essayez random.randint (1, 10) /100.0

0
user2844536

Selon la documentation Python 3.0 :

random .uniform (a, b) Retourne un nombre aléatoire N tel que a <= N <= b pour a <= b et b <= N <= a pour b <a.

Ainsi, random.uniform() inclut en fait la limite supérieure, du moins sur Python 3.0.

EDIT: Comme l'a souligné @ Blender, la documentation de Python 3.0 semble être incompatible avec le code source sur ce point.

EDIT 2: Comme l'a souligné @MarkDickinson, j'avais involontairement lié à la documentation Python 3.0 au lieu de la dernière documentation Python 3 ici qui se lit comme suit:

random .uniforme (a, b) Retourne un nombre aléatoire N tel que que a <= N <= b pour a <= b et b <= N <= a pour b <a. 

Le point final la valeur b peut être incluse ou non dans la plage en fonction de arrondi en virgule flottante dans l'équation a + (b-a) * aléatoire ().

0
Simon

Numpy vous permet d'effectuer les tâches suivantes:

import numpy
numpy.random.uniform(0.1, numpy.nextafter(1, 2))
0
Mitar