Сообщество Engee

Закон Бенфорда

Автор
avatar-maximsidorovmaximsidorov
Notebook

Закон Бенфорда

Рассматриваем реализацию закона Бенфорда на языке программирования Julia с использованием последовательности Фибоначчи.

Введение

Закон Бенфорда – это эмпирический закон, описывающий распределение первых цифр в наборах числовых данных из реальной жизни. Согласно этому закону, цифра 1 встречается в качестве первой значительно чаще, чем другие цифры (около 30%), а цифра 9 – реже всего (менее 5%). Закон имеет широкое применение в области аудита данных, выявления фальсификаций и экономических анализов.

В данном примере мы реализуем закон Бенфорда на языке программирования Julia. Также мы создадим последовательность чисел Фибоначчи, применим к ней закон, а затем построим график и выведем сравнение между эмпирическими данными и теоретическим распределением.

Реализация алгоритма

Правило Бенфорда задается формулой:

где - первая цифра от 1 до 9, а - вероятность этой цифры быть первой.

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

benford(numbers) -- вычисляет частоты первых цифр в наборе чисел.

Возвращает массив из 9 элементов, где i-й элемент показывает, какая доля чисел начинается с цифры i.

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
benford

Определяем итератор для генерации последовательности чисел Фибоначчи. Base.iterate реализуется таким образом, чтобы можно было работать с бесконечным потоком, используя big.Int для предотвращения переполнения.

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

Реализация протокола итерации для типа Fibonacci

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

Генерируем первые 1000 чисел из последовательности Фибоначчи

sample = Iterators.take(Fibonacci(), 1000)
Base.Iterators.Take{Fibonacci}(Fibonacci(), 1000)

Рассчитываем наблюдаемые частоты первых цифр и переводим их в проценты

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

Вычисляем теоретические значения согласно закону Бенфорда и переводим в проценты

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

Формируем таблицу для удобного сравнения данных

table = Real[1:9 observed expected]
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

Строим график баров для наблюдаемых данных и линии для ожидаемых

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)"]
)

Выводим результаты на консоль в хорошо отформатированном виде

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. Написали функцию для вычисления распределения первых цифр, применили её к выборке из первых 1000 чисел Фибоначчи и сравнили полученные данные с теоретическими значениями закона. Также были построены наглядные графики, демонстрирующие совпадение наблюдаемой статистики с математическим законом. Такие методы могут использоваться для проверки естественности данных или для выявления возможных искажений и мошенничества.

Пример разработан с использованием материалов Rosetta Code