Generate random number between 0.1 and 1.0. Python

后端 未结 10 1682
南方客
南方客 2020-12-29 05:23

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

相关标签:
10条回答
  • 2020-12-29 05:35

    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.

    0 讨论(0)
  • 2020-12-29 05:35

    In numpy, you can do the following:

    import numpy
    numpy.random.uniform(0.1, numpy.nextafter(1, 2))
    
    0 讨论(0)
  • 2020-12-29 05:42

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

    0 讨论(0)
  • 2020-12-29 05:44

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

    0 讨论(0)
  • 2020-12-29 05:51

    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.

    0 讨论(0)
  • 2020-12-29 05:51

    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

    uniform

    0 讨论(0)
提交回复
热议问题