Generate Random Number Between 0.1 And 1.0. Python
Solution 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.
Solution 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 inrange(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 insorted(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
defrand_range(low=0,high=1,size=1):
a=np.nextafter(low,float('-inf'))
b=np.nextafter(high,float('inf'))
defr():
defrn():
return a+(b-a)*random.random()
_rtr=rn()
while _rtr > high:
_rtr=rn()
if _rtr<low:
_rtr=low
return _rtr
return [r() for i inrange(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))
iflen(se)<25:
for i,e inenumerate(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:
11.0000000000000000e+00589, 5.8900% low value end point
21.0000000000000002e+00544, 5.4400%
31.0000000000000004e+00612, 6.1200%
41.0000000000000007e+00569, 5.6900%
51.0000000000000009e+00593, 5.9300%
61.0000000000000011e+00580, 5.8000%
71.0000000000000013e+00565, 5.6500%
81.0000000000000016e+00584, 5.8400%
91.0000000000000018e+00603, 6.0300%
101.0000000000000020e+00589, 5.8900%
111.0000000000000022e+00597, 5.9700%
121.0000000000000024e+00591, 5.9100%
131.0000000000000027e+00572, 5.7200%
141.0000000000000029e+00619, 6.1900%
151.0000000000000031e+00593, 5.9300%
161.0000000000000033e+00592, 5.9200%
171.0000000000000036e+00608, 6.0800% high value end point
On the values requested by the OP, it also produces a uniform distribution:
import matplotlib.pyplotas 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
Solution 3:
Random.uniform()
is just:
defuniform(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 + 3assert0.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 ** Nf= 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).
Solution 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
defmyRandom():
whileTrue:
r = random()
if r >= 0.1:
return r
if r == 0.0:
return1.0
Solution 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
Post a Comment for "Generate Random Number Between 0.1 And 1.0. Python"