Engee 文档
Notebook

本福德定律

我们正在考虑使用斐波那契数列在Julia编程语言中实现本福德定律。

导言

本福德定律是一个经验定律,它描述了现实生活中数字数据集中第一位数字的分布。 根据这一规律,数字1作为第一个出现的频率明显高于其他数字(约30%),数字9频率最低(小于5%)。 该法在数据审计、欺诈检测和经济分析领域具有广泛的应用.

在这个例子中,我们用Julia编程语言实现了Benford定律。 我们还将创建一个斐波那契数列,将定律应用于它,然后绘制并比较经验数据与理论分布。

算法实现

本福德规则由公式给出:

哪里 -第一个数字是从1到9,和 -这个数字是第一个的概率。

In [ ]:
"""
Вычисляет вероятность первой цифры по закону Бенфорда.
"""
P(d) = log10(1 + 1 / d)
Out[0]:
P

benford(numbers) --计算一组数字中第一位数字的频率。

返回一个由9个元素组成的数组,其中第i个元素指示数字的哪个部分以数字i开头。

In [ ]:
function benford(numbers)
    # Получает первую цифру числа n (самую правую цифру после обращения)
    firstdigit(n) = last(digits(n))

    # Инициализируем массив счетчиков для каждой цифры (1–9)
    counts = zeros(9)

    # Для каждого числа увеличиваем соответствующий счетчик
    foreach(n -> counts[firstdigit(n)] += 1, numbers)

    # Нормируем частоты, чтобы их сумма была равна 1
    counts ./ sum(counts)
end
Out[0]:
benford

我们定义一个迭代器来生成斐波那契数列。 Base.iterate 它的实现方式是,可以使用 big.Int 防止溢出。

In [ ]:
# Создаем тип для обозначения итератора Фибоначчи
struct Fibonacci end

类型的迭代协议的实现 Fibonacci

In [ ]:
Base.iterate(::Fibonacci, (a, b) = big.((0, 1))) = b, (b, a + b)

从斐波那契数列生成前1000个数字

In [ ]:
sample = Iterators.take(Fibonacci(), 1000)
Out[0]:
Base.Iterators.Take{Fibonacci}(Fibonacci(), 1000)

我们计算第一位数字的观察频率并将其转换为百分比。

In [ ]:
observed = benford(sample) .* 100
Out[0]:
9-element Vector{Float64}:
 30.099999999999998
 17.7
 12.5
  9.6
  8.0
  6.7
  5.6000000000000005
  5.3
  4.5

我们根据本福德定律计算理论值并将其转换为百分比。

In [ ]:
expected = P.(1:9) .* 100
Out[0]:
9-element Vector{Float64}:
 30.10299956639812
 17.609125905568124
 12.493873660829994
  9.691001300805642
  7.918124604762482
  6.694678963061322
  5.799194697768673
  5.115252244738129
  4.575749056067514

创建表格以方便数据比较

In [ ]:
table = Real[1:9 observed expected]
Out[0]:
9×3 Matrix{Real}:
 1  30.1  30.103
 2  17.7  17.6091
 3  12.5  12.4939
 4   9.6   9.691
 5   8.0   7.91812
 6   6.7   6.69468
 7   5.6   5.79919
 8   5.3   5.11525
 9   4.5   4.57575

我们绘制观测数据的柱线和预期数据的线。

In [ ]:
using Plots

plot(
    [observed expected];
    title = "Benford's Law",
    seriestype = [:bar :line],
    linewidth = [0 5],
    xticks = 1:9,
    xlabel = "first digit",
    ylabel = "frequency %",
    label = ["1000 Fibonacci numbers" "P(d)=log10(1+1/d)"]
)
Out[0]:

我们以格式良好的形式将结果输出到控制台。

In [ ]:
using Printf

println("Benford's Law\nFrequency of first digit\nin 1000 Fibonacci numbers")
println("digit observed  expected")
foreach(i -> @printf("%3d%9.2f%%%9.2f%%\n", table[i, :]...), 1:9)
Benford's Law
Frequency of first digit
in 1000 Fibonacci numbers
digit observed  expected
  1    30.10%    30.10%
  2    17.70%    17.61%
  3    12.50%    12.49%
  4     9.60%     9.69%
  5     8.00%     7.92%
  6     6.70%     6.69%
  7     5.60%     5.80%
  8     5.30%     5.12%
  9     4.50%     4.58%

结论

我们已经审查了本福德法律在Julia语言中的实施情况。 我们编写了一个函数来计算第一位数字的分布,将其应用于前1,000个斐波那契数的样本,并将获得的数据与定律的理论值进行比较。 还构建了视觉图,证明了观察到的统计数据与数学定律的重合。 这些方法可用于验证数据的自然性或识别可能的扭曲和欺诈。

该示例是使用[Rosetta代码]的材料开发的(https://rosettacode.org/wiki/Benford's_law