如何随机生成非常小的数字?

7
假设我想在范围 [10^-20, 0.1] 中随机生成一个数字,应该怎么做?
如果使用 numpy.random.uniform,好像最小值不能低于 10^-2:
In [2]: np.random.uniform(0.1, 10**(-20))
Out[2]: 0.02506361878539856

In [3]: np.random.uniform(0.1, 10**(-20))
Out[3]: 0.04035553250149768

In [4]: np.random.uniform(0.1, 10**(-20))
Out[4]: 0.09801074888377342

In [5]: np.random.uniform(0.1, 10**(-20))
Out[5]: 0.09778150831277296

In [6]: np.random.uniform(0.1, 10**(-20))
Out[6]: 0.08486347093110456

In [7]: np.random.uniform(0.1, 10**(-20))
Out[7]: 0.04206753781952958

或者我可以生成一个数组,例如:

In [44]: fac = np.linspace(10**(-20),10**(-1),100)

In [45]: fac
Out[45]: 
array([  1.00000000e-20,   1.01010101e-03,   2.02020202e-03,
         3.03030303e-03,   4.04040404e-03,   5.05050505e-03,
         6.06060606e-03,   7.07070707e-03,   8.08080808e-03,
         9.09090909e-03,   1.01010101e-02,   1.11111111e-02,
         1.21212121e-02,   1.31313131e-02,   1.41414141e-02,
         1.51515152e-02,   1.61616162e-02,   1.71717172e-02,
         1.81818182e-02,   1.91919192e-02,   2.02020202e-02,
         2.12121212e-02,   2.22222222e-02,   2.32323232e-02,
         2.42424242e-02,   2.52525253e-02,   2.62626263e-02,
         2.72727273e-02,   2.82828283e-02,   2.92929293e-02,
         3.03030303e-02,   3.13131313e-02,   3.23232323e-02,
         3.33333333e-02,   3.43434343e-02,   3.53535354e-02,
         3.63636364e-02,   3.73737374e-02,   3.83838384e-02,
         3.93939394e-02,   4.04040404e-02,   4.14141414e-02,
         4.24242424e-02,   4.34343434e-02,   4.44444444e-02,
         4.54545455e-02,   4.64646465e-02,   4.74747475e-02,
         4.84848485e-02,   4.94949495e-02,   5.05050505e-02,
         5.15151515e-02,   5.25252525e-02,   5.35353535e-02,
         5.45454545e-02,   5.55555556e-02,   5.65656566e-02,
         5.75757576e-02,   5.85858586e-02,   5.95959596e-02,
         6.06060606e-02,   6.16161616e-02,   6.26262626e-02,
         6.36363636e-02,   6.46464646e-02,   6.56565657e-02,
         6.66666667e-02,   6.76767677e-02,   6.86868687e-02,
         6.96969697e-02,   7.07070707e-02,   7.17171717e-02,
         7.27272727e-02,   7.37373737e-02,   7.47474747e-02,
         7.57575758e-02,   7.67676768e-02,   7.77777778e-02,
         7.87878788e-02,   7.97979798e-02,   8.08080808e-02,
         8.18181818e-02,   8.28282828e-02,   8.38383838e-02,
         8.48484848e-02,   8.58585859e-02,   8.68686869e-02,
         8.78787879e-02,   8.88888889e-02,   8.98989899e-02,
         9.09090909e-02,   9.19191919e-02,   9.29292929e-02,
         9.39393939e-02,   9.49494949e-02,   9.59595960e-02,
         9.69696970e-02,   9.79797980e-02,   9.89898990e-02,
         1.00000000e-01])

从数组中随机选择一个元素,但是我想澄清一下,如果第一个选项可行的话,请告诉我,因为我可能会忽略某些显而易见的东西。

1
实际上我不懂Python,但是逻辑在每种语言中都是相同的。生成介于10^10和10^11之间的随机数,然后将其除以10^12。 - kodmanyagha
3
我认为您遗漏了问题的统计数据……在均匀分布下,命中一个10^-20的数字的概率非常小。您是否需要采用对数分布? - Aaron
1
@jkrish:请注意,分布将会严重偏向。您想要均匀分布还是高概率低值? - Eric Duminil
@Aaron:你说得对。min(np.random.uniform(10**(-20), 0.1) for _ in range(10000000)) 返回的数字大约是 1E-9 - Eric Duminil
@jkrish:实际上,从文档中可以看出:“如果 high < low,则结果被正式定义为未定义,并且最终可能会引发错误,即不要依赖此函数在传递满足该不等式条件的参数时表现得如何。” - Eric Duminil
显示剩余6条评论
5个回答

11

你需要仔细思考你正在做什么。你要求在接近0.0和0.1之间均匀分布。平均结果将是0.05。这正是你得到的结果。看起来你想要指数的随机分布。

以下可能会满足你的需求:

import random

def rnd():
    exp = random.randint(-19, -1)
    significand = 0.9 * random.random() + 0.1
    return significand * 10**exp

[rnd() for _ in range(20)]

exp=-19significand=0.1时,最小可能值为0.1*10**-19 = 1**-20。而当exp=-1significand=1.0时,最大可能值为1.0*10**-1 = 0.1

注:从技术上讲,significand只能接近于1.0,因为random()受限于[0.0, 1.0)范围内,即包括0.0但不包括1.0。

[2.3038280595190108e-11,
 0.02658855644891981,
 4.104572641101877e-11,
 3.638231824527544e-19,
 6.220040206106022e-17,
 7.207472203268789e-06,
 6.244626749598619e-17,
 2.299282102612733e-18,
 0.0013251357609258432,
 3.118805901868378e-06,
 6.585606992344938e-05,
 0.005955900790586139,
 1.72779538837876e-08,
 7.556972406280229e-13,
 3.887023124444594e-15,
 0.0019965330694999488,
 1.7732147730252207e-08,
 8.920398286274208e-17,
 4.4422869312622194e-08,
 2.4815949527034027e-18]

请参见维基百科上的"科学计数法",了解有效数字和指数的定义。


5
根据numpy文档: low: float或浮点数数组,可选 输出区间的下限。生成的所有值都大于或等于low。默认值为0。
因此,降低“low”的值将产生更小的数字。
>>> np.random.uniform(0.00001, 10**(-20))
6.390804027773046e-06

2
你不是在减少 low,而是在减少 high。值不会超过 1E-5,也永远不会达到 0.1 - Eric Duminil
1
使用numpy.random.uniform(low=0.0, high=1.0, size=None)作为函数定义,@EricDuminil是否减小了low的值?另外从文档中可以看到:“如果high < low,则结果被官方定义为未定义,并且最终可能会引发错误,即不要依赖于传递满足该不等式条件的参数时此函数的行为。” - Nick stands with Ukraine
1
@NickA:是的,顺序应该颠倒。但是:max(np.random.uniform(0.00001, 10**(-20)) for _ in range(10000000)) 返回 9.999999414469333e-06,因此看起来 0.00001 被解释为“高”。 - Eric Duminil
1
这个答案的范围不涵盖[10^-20, 0.1],而是仅限于[10^-5, 10^-6]。另外,我惊讶地发现这没有抛出错误。因为“high”设置比“low”低。“如果high < low,则结果被官方定义为未定义,并且最终可能会引发错误,即不要依赖该函数在传递满足该不等式条件的参数时能够正常工作。” [https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.uniform.html] - André C. Andersen
我现在在评论中看到,参数本来就是反过来的。那么解决方案将是功能性的,但仍然限于[10^-20, 10^-5],平均约为0.5*10^-6。它可能永远不会看到低于10^-7的任何东西。话虽如此,OP似乎对此感到满意。 - André C. Andersen

3

生成一个1到10,000之间的随机数,然后将该数字除以100,000。


2
这是我的第一反应,尽管它不会在1到10000之间,但它将是介于“1”和“1e19”之间的数字,除以“1e20”。 - Nick stands with Ukraine
如果你想要覆盖[10^-20, 0.1]这个区间并获得非常小的数字,那么这个答案是错误的。如果使用均匀分布,它将主要生成围绕0.05(平均值)的数字,就像OP已经得到的一样。你永远不会看到像10^-19或10^-20这样的数字。 - André C. Andersen
@AndréChristofferAndersen 是的,但是强制数字朝着10^-20和0.1移动就不再是随机的了。 - Nick stands with Ukraine
@NickA 它是完全随机的。只是不是均匀随机的。OP要求在[10^-20,0.1]范围内的数字,这意味着一些结果应该接近数量级的下限。如果不是这样,那么设置这样的下限就没有意义。 - André C. Andersen
1
不妨在一个函数中加入 return 0.05 ;) - Nick stands with Ukraine

1
如果你在[0,0.1]范围内生成均匀随机数,则随机数小于10^-20的可能性为10^-19。它很可能永远不会发生。然而,如果你必须确保它不会发生(也许因为更小的数字会使你的代码崩溃),那么只需在[0,0.1]范围内生成均匀随机数,测试它们,并拒绝任何太小的数值,用同一个生成器替换并重新测试。这将“非常不可能”替换为“肯定不会发生”。
这种技术在蒙特卡罗模拟中更常见,其中你希望随机采样f(x,y)或f(x,y,z),其中坐标(x,y[,z])必须在具有复杂定义的某个区域或体积内,例如复杂机械部件的内部。技术是相同的。建立边界范围[xlow,xhigh],[ylow,yhigh]...,并在此边界框内生成均匀分布的随机坐标。然后检查这个随机位置是否在要采样的区域/体积内。如果不是,生成另一个随机元组并重新检查。

1

如果您想保持均匀分布并避免与浮点表示相关的问题,只需在0到9之间均匀地绘制20个整数,并使用十进制表示法“构建”您的结果(您仍将拥有均匀分布):

result = 0
digits = np.random.randint(0,10,20)
for idx,digit in enumerate(digits):
  result += digit*(10**idx)

这会给你一个介于0和10**19 -1之间的数字。你可以根据需要不同地解释结果。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接