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
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.
In numpy, you can do the following:
import numpy
numpy.random.uniform(0.1, numpy.nextafter(1, 2))
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).
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().
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.
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