
不久前又看了本福特定律的介绍视频,一时兴起研究了一番。这里给出一些我的相关理解和证明,供于参考。
原视频:

原理
引用某百科的介绍,本福特定律指一堆从实际生活得出的数据中,以1为首位数字的数的出现概率约为总数的三成,而以2、3等开头的数则逐级向下递减。而实际上这些概率是可以被计算出来的。在n进制中,以数m起头的数出现的概率为 。据此计算出10进制以1为首的概率即为
。
这里需要说明的是,本福特定律是一个经验性的定理,并不是所有生活中的数据都满足此条件。例如生活中极为常见的正态分布(如人身高体重的分布)、二项式分布(如扔硬币正面向上的次数)和均匀分布(骰子点数频率分布)都不满足本福特定律。
如视频所说,本福特定律往往只对跨越几个数量级的数据才有效,例如斐波那契数列的分布。当然这也不绝对,比如对于数列 来说,本福特定律也都不满足。一般来说,最常见的满足本福特定律的概率分布满足以下概率密度函数:
其中,m,λ为任意正数。是不是一头雾水...?其实一些常见的序列都遵循这个概率分布。事实上,所有等比数列(指数函数)均符合此概率分布并满足本福特定律。第二部分中将给出具体证明。
让我们先假定上面的定理是正确的,那现实中的本福特定律就可以解释了。对于现实中的数据来说,如果能跨越多个数量级,那么它们一般都是以指数级增长的,它们的差往往也是指数级的。如果我们把常见的这些数据排序好绘制到坐标系上,我们往往都能发现指数函数能很好的拟合它们:

上证A股2000多条的股价数据
上图是上证A股2000多条的股价数据。对数图中,排除首尾极端数据后可使用一次函数拟合,这就意味着原始数据可使用指数函数拟合。经过统计,以1开头的数据共656条,总可用数据共2087条,约占0.314,也确实满足本福特定律。
所以总结来说,因为现实中能够跨越数量级的数据大多呈指数级增长,因此对其序列排序后大概率可以使用指数函数拟合,因此满足本福特定律。
证明
接下来就到证明的时候了!现在我们只需要证明等比数列满足本福特定律即可。不过在这之前,我们先证明一个引理:
引理1:对于任何能被 拟合的序列,如果对于给定正整数m,n,其反函数
满足
恒为常数,那么对应该序列的n进制表示中,其开头的数字小于m的数字占比应为C。
比如,对于 来说,给定m=2,n=10,因为对任意 x>0 有:
所以 序列里以1开头的数大约占0.192。
接下来开始证明。我们先以十进制为例,对于所有的正数,我们可以把它们按如下规则划分到不同的区间:
明显的,每个区间内的前10%的数字开头均为1,前20%的数字开头均小于2,以此类推。同样的,对于n进制,这些区间可以统一表示为 ,其中k为n的幂数倍。画个图:

f(x)
这里f(x)是拟合我们序列的函数,横坐标是序列编号,纵坐标为对应值。如图中用浅灰色线所示, 会把整个函数的值域划分成一个个的区间。
内的数字中,数字开头小于m的数字均落在
区间内,如图中土黄色所示。那么这些数一共有多少个呢?我们知道,横坐标是连续的序列编号,因此这些数对应图中绿色区间,一共有
个,所以在这个区间内,其开头的数字小于等于m的数字占比应为
。剩下的所有区间同理,对应的占比也是此式。我们再根据引理的条件,既然对所有 x>0,都有
恒为常数,自然就得出引理的结论了。至此,引理1证明完毕。
接下来证明指数函数满足引理1的条件:
定理1:指数函数 对给定m,n,满足
。
对此函数,可求反函数为 ,则有:
得证。
至此“等比数列满足本福特定律”这个结论已经呼之欲出了。由定理1和引理1,我们可知对于任何指数函数,在n进制中,第一个数小于m的概率为 ,同理,第一个数小于m+1的概率为
,两式相减,即可得出第一个数等于m的概率,即我们的本福特定律:
。证毕!
伪造
本福特定律可用于检查各种数据是否有造假,但是如果造假者有心的话,满足本福特定律的数据也是可以伪造的。我们完全可以根据上文的内容来实现“造假”的过程。
方法很简单,我们既然已经知道指数函数满足本福特定律,那么我们就可以从一个指数函数里选择一小段,再用均匀分布的随机数映射过去就可以了。下面是一个生成本福特定律数据的示例代码:
import math, random
def fake_benford(begin, end, n):
"""
generate fake benford data.
:param begin: min value
:param end: max value
:param n: total amount
:return: generated array
"""
def exp(v):
return begin * (1.3 ** (v * math.log(end / begin, 1.3)))
return [exp(random.random()) for _ in range(n)]
example = fake_benford(1, 100000, 1000000)