【问题标题】:Is there a random number distribution that obeys Benford's Law?是否存在服从本福德定律的随机数分布?
【发布时间】:2013-01-28 06:17:11
【问题描述】:

Python 有多种方法可以生成不同的随机数分布,请参阅documentation for the random module。不幸的是,如果没有适当的数学背景,尤其是考虑到所需的参数,它们就很难理解。

我想知道这些方法中是否有任何一种能够产生分布服从Benford's Law 的随机数,以及哪些参数值是合适的。即对于整数的总体,这些整数应该在大约 30% 的时间以“1”开头,大约 18% 的时间以“2”开头,等等。


使用John Dvorak's answer,我将以下代码放在一起,它似乎可以完美运行。

def benfords_range_gen(stop, n):
    """ A generator that returns n random integers
    between 1 and stop-1 and whose distribution
    meets Benford's Law i.e. is logarithmic.
    """
    multiplier = math.log(stop)
    for i in range(n):
        yield int(math.exp(multiplier * random.random()))

>>> from collections import Counter
>>> Counter(str(i)[0] for i in benfords_range_gen(10000, 1000000))
Counter({'1': 300696, '2': 176142, '3': 124577, '4': 96756, '5': 79260, '6': 67413, '7': 58052, '8': 51308, '9': 45796})

还出现了一个问题,即这是否在不同版本的 Python 之间工作一致。这不是一个容易回答的问题,因为随机数的性质——您希望每次运行都会有一些变化,有时在 random 库的不同版本之间会有一些变化。避免这种情况的唯一方法是在每次运行之间一致地播种随机数生成器。我已将其添加到我的测试中,并且在 Python 2.7.1、3.8.6 和 3.9.1 中得到了完全相同的结果。

>>> random.seed(7919)
>>> Counter(str(i)[0] for i in benfords_range_gen(10000, 1000000))
Counter({'1': 301032, '2': 176404, '3': 125350, '4': 96503, '5': 78450, '6': 67198, '7': 58000, '8': 51342, '9': 45721})

【问题讨论】:

  • 这不等于在 log(min) 和 log(max) 之间统一选择一个随机数并输出 10 提升到该数字吗?
  • 鉴于本福德定律,试试floor(10^random())
  • @JanDvorak:是的。对于 1,000,000 次试验,我得到:{1: 0.301143, 2: 0.175899, 3: 0.125316, 4: 0.097045, 5: 0.079359, 6: 0.066662, 7: 0.057795, 8: 0.050963, 9: 0.045818}。在 Python 中,它是 int(10**random.random())
  • @Blender 所以,看起来是正确的
  • @JanDvorak,如果您是认真的,请提出它作为答案。我喜欢它不依赖于任何花哨的分布模型,只是对通常的等概率的简单转换。

标签: python random benfords-law


【解决方案1】:

本福德定律描述了一组数字的第一个数字的分布,如果这些数字是从对数刻度上的一个很宽的范围内选择的。如果你准备一个十年内的对数均匀分布,它也将遵守法律。 10^[0,1) 将产生该分布。

这将产生所需的分布:math.floor(10**random.random())

【讨论】:

    【解决方案2】:

    只是在玩。

    对于像我这样不太喜欢数学的人来说,这是一种效率低得多但可能更可见的实现......

    创建任何所需分布的一种简单方法是用所需百分比的项目填充列表,然后使用random.choice(<list>),因为这会返回列表中项目的统一选择。

    import random
    probs = [30.1, 17.6, 12.5, 9.7, 7.9, 6.7, 5.8, 5.1, 4.6]
    nums = [1, 2, 3, 4, 5, 6, 7, 8, 9]
    population = sum([[n] * int(p * 10) for n, p in zip(nums, probs)], [])
    
    max_value = 100
    min_value = 1
    result_pop = []
    target_pop_size = 1000
    while len(result_pop) < target_pop_size:
        s = str(random.choice(population))
        while True:
            r = random.randint(min_value, max_value)
            if str(r).startswith(s):
                break
        result_pop.append(r)
    

    【讨论】:

      猜你喜欢
      • 2013-04-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-13
      • 1970-01-01
      • 1970-01-01
      • 2015-06-01
      相关资源
      最近更新 更多