Закон Бенфорда
Рассматриваем реализацию закона Бенфорда на языке программирования Julia с использованием последовательности Фибоначчи.
Введение
Закон Бенфорда – это эмпирический закон, описывающий распределение первых цифр в наборах числовых данных из реальной жизни. Согласно этому закону, цифра 1 встречается в качестве первой значительно чаще, чем другие цифры (около 30%), а цифра 9 – реже всего (менее 5%). Закон имеет широкое применение в области аудита данных, выявления фальсификаций и экономических анализов.
В данном примере мы реализуем закон Бенфорда на языке программирования Julia. Также мы создадим последовательность чисел Фибоначчи, применим к ней закон, а затем построим график и выведем сравнение между эмпирическими данными и теоретическим распределением.
Реализация алгоритма
Правило Бенфорда задается формулой:
где - первая цифра от 1 до 9, а - вероятность этой цифры быть первой.
"""
Вычисляет вероятность первой цифры по закону Бенфорда.
"""
P(d) = log10(1 + 1 / d)
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
Определяем итератор для генерации последовательности чисел Фибоначчи. 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)
Рассчитываем наблюдаемые частоты первых цифр и переводим их в проценты
observed = benford(sample) .* 100
Вычисляем теоретические значения согласно закону Бенфорда и переводим в проценты
expected = P.(1:9) .* 100
Формируем таблицу для удобного сравнения данных
table = Real[1:9 observed expected]
Строим график баров для наблюдаемых данных и линии для ожидаемых
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)
Заключение
Мы рассмотрели реализацию закона Бенфорда на языке Julia. Написали функцию для вычисления распределения первых цифр, применили её к выборке из первых 1000 чисел Фибоначчи и сравнили полученные данные с теоретическими значениями закона. Также были построены наглядные графики, демонстрирующие совпадение наблюдаемой статистики с математическим законом. Такие методы могут использоваться для проверки естественности данных или для выявления возможных искажений и мошенничества.
Пример разработан с использованием материалов Rosetta Code