Generate random number between 0.1 and 1.0. Python

回眸只為那壹抹淺笑 提交于 2020-02-12 08:50:50

问题


I'm trying to generate a random number between 0.1 and 1.0. We can't use rand.randint because it returns integers. We have also tried random.uniform(0.1,1.0), but it returns a value >= 0.1 and < 1.0, we can't use this, because our search includes also 1.0.

Does somebody else have an idea for this problem?


回答1:


How "accurate" do you want your random numbers? If you're happy with, say, 10 decimal digits, you can just round random.uniform(0.1, 1.0) to 10 digits. That way you will include both 0.1 and 1.0:

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

To be precise, 0.1 and 1.0 will have only half of the probability compared to any other number in between and, of course, you loose all random numbers that differ only after 10 digits.




回答2:


You could do this:

>>> 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)]

or, use numpy's uniform:

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

nextafter will produce the platform specific next representable floating pointing number towards a direction. Using numpy's random.uniform is advantageous because it is unambiguous that it does not include the upper bound.


Edit

It does appear that Mark Dickinson's comments is correct: Numpy's documentation is incorrect regarding the upper bound to random.uniform being inclusive or not.

The Numpy documentation states All values generated will be less than high.

This is easily disproved:

>>> 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

Nor is the result uniform over this limited range:

>>> 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

However, combining J.F. Sebastian and Mark Dickinson's comments, I think this works:

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)]

If run with the minimal spread of values in Mark's comment such that there are very few discrete floating point values:

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))

It produces the desired uniform distribution inclusive of end points:

 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

On the values requested by the OP, it also produces a uniform distribution:

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()   

Output




回答3:


Random.uniform() is just:

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()

where self.random() returns a random number in the range [0.0, 1.0).

Python (as well as many other languages) uses floating point to represent real numbers. How 0.1 is represented is described in detail in the docs:

from __future__ import division

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

It allows to find a random number in [0.1, 1] (inclusive) using randint() without losing precision:

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

randint(n, m) returns a random integer in [n, m] (inclusive) therefore the above method can potentially return all floating points numbers in [0.1, 1].

An alternative is to find the smallest x such that x > 1 and use:

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

x should be the smallest value to avoid losing precision and to reduce number of calls to uniform() e.g.:

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

Both solutions preserve uniformness of the random distribution (no skew).




回答4:


With the information you've given (including comments thus far), I still fail to see how the university is going to test your program such that it will make a difference if 1.0 appears or not. (I mean, if you're required to generate random floats, how can they require that any particular value appears?)

OK, so putting the craziness of your requirements aside:

The fact that the lower bound for your random floats is higher than 0 gives you a disturbingly elegant way to use random.random, which guarantees return values in the interval [0.0, 1.0): Simply keep calling random.random, throwing away any values less than 0.1, except 0.0. If you actually get 0.0, return 1.0 instead.

So something like

from random import random

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



回答5:


You can use random.randint simply by doing this trick:

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

if you want more decimals, just change the interval to:

(1000,10000) 4 digits (10000,100000) 5 digits etc




回答6:


Are you unable to use random.random()? This gives a number between 0.0 and 1.0, though you could easily set up a way to get around this.

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

This code would give you a number that is between 0.1 and 1.0, inclusive (0.1 and 1.0 are both possible solutions). Hope this helps.

*I assumed you only wanted numbers to the tenths place. If you want it different, where I typed round(number, 1) change 1 to 2 for hundredths, 3 for thousandths, and so on.




回答7:


The standard way would be random.random() * 0.9 + 0.1 (random.uniform() internally does just this). This will return numbers between 0.1 and 1.0 without the upper border.

But wait! 0.1 (aka ¹/₁₀) has no clear binary representation (as ⅓ in decimal)! So You won't get a true 0.1 anyway, simply because the computer cannot represent it internally. Sorry ;-)




回答8:


According to the Python 3.0 documentation:

random.uniform(a, b) Return a random floating point number N such that a <= N <= b for a <= b and b <= N <= a for b < a.

Thus, random.uniform() does, in fact, include the upper limit, at least on Python 3.0.

EDIT: As pointed out by @Blender, the documentation for Python 3.0 seems to be inconsistent with the source code on this point.

EDIT 2: As pointed out by @MarkDickinson, I had unintentionally linked to the Python 3.0 documentation instead of the latest Python 3 documentation here which reads as follows:

random.uniform(a, b) Return a random floating point number N such that a <= N <= b for a <= b and b <= N <= a for b < a.

The end-point value b may or may not be included in the range depending on floating-point rounding in the equation a + (b-a) * random().




回答9:


Try random.randint(1, 10)/100.0




回答10:


In numpy, you can do the following:

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


来源:https://stackoverflow.com/questions/16288749/generate-random-number-between-0-1-and-1-0-python

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!