是否存在遵循本福特定律的随机数分布?

10 投票
2 回答
3351 浏览
提问于 2025-04-17 13:53

Python有很多方法可以生成不同类型的随机数分布,具体可以查看random模块的文档。不过,这些方法的理解需要一些数学基础,尤其是涉及到的参数。

我想知道这些方法中有没有可以生成符合本福特定律的随机数的,适合的参数值是什么。简单来说,对于一组整数,这些整数的开头数字应该大约有30%的概率是'1',18%的概率是'2',等等。


根据John Dvorak的回答,我写了以下代码,结果看起来运行得很好。

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

2 个回答

0

只是随便玩玩。

这是一种效率较低,但对那些像我一样不太擅长数学的人来说,可能更容易理解的实现方式……

创建任何想要的分布其实很简单,你只需要在一个列表里填入你想要的物品的百分比,然后用 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)
22

本福德定律描述了一组数字的首位数字的分布情况,前提是这些数字是从一个很广的范围内选择的,并且是按照对数的方式来考虑的。如果你在一个十进制范围内准备一个对数均匀分布,它也会遵循这个定律。比如,10^[0,1) 就能产生这样的分布。

这样就能得到我们想要的分布:math.floor(10**random.random())

撰写回答