Сообщество Engee

Арифметическая производная

Автор
avatar-maximsidorovmaximsidorov
Notebook

Арифметическая производная

Этот пример демонстрирует реализацию и использование алгоритма арифметической производной на языке программирования Julia.

Введение

Арифметическая производная — это аналог обычной функции производной из математического анализа, но применяемый к целым числам. Она была предложена для изучения свойств чисел с точки зрения дифференцирования, подобно тому, как производные применяются к функциям. Формально, для натурального числа , обозначается , и определяется следующим образом:

  • Если – простое число, то
  • Если , то )

Она позволяет применять интуицию из дифференциального исчисления к числовым последовательностям и используется при решении некоторых задач теории чисел.

В этом коде реализуется рекурсивная версия такого определения с использованием разложения числа на простые множители.

Реализация функции арифметической производной D(n)

Для использования функции проверки на простоту и разложения на множители нужен пакет 'Primes'

import Pkg; Pkg.add("Primes")

using Primes
   Resolving package versions...
  No Changes to `~/.project/Project.toml`
  No Changes to `~/.project/Manifest.toml`
function D(n)
    # Если число отрицательное, вызываем D(-n) и меняем знак результата
    n < 0 && return -D(-n)
    
    # Для 0 и 1 значение D всегда равно 0
    n < 2 && return zero(n)
    
    # Если число простое, то его производная равна 1
    isprime(n) && return one(n)

    # Если число составное, используем формулу:
    # D(n) = Σ (e_i * n / p_i), где p_i -- простые множители, e_i -- их степени в разложении
    return typeof(n)(sum(e * n ÷ p for (p, e) in eachfactor(n)))
end
D (generic function with 1 method)

Функция D(n) вычисляет арифметическую производную числа n. Ниже приведён подробный анализ её реализации:

  • Обработка отрицательных чисел реализована через рекурсию с изменением знака.
  • Числа 0 и 1 возвращают 0, согласно определению.
  • Для простых чисел возвращается 1.
  • Для составных чисел используется разложение eachfactor(n) — получаем пары (prime, exponent).
  • Для каждого простого делителя c показателем степени суммируем ; результат приводится к типу входного аргумента.

Таким образом, функция полностью удовлетворяет формальному определению арифметической производной.

Вычисление арифметической производной

Вывод значений для диапазона от -99 до 100 в форматированном виде

foreach(p -> print(lpad(p[2], 5), p[1] % 10 == 0 ? "\n" : ""),
        pairs(map(D, -99:100)))
  -75  -77   -1 -272  -24  -49  -34  -96  -20 -123
   -1 -140  -32  -45  -22 -124   -1  -43 -108 -176
   -1  -71  -18  -80  -55  -39   -1 -156   -1  -59
  -26  -72   -1  -61  -18 -192  -51  -33   -1  -92
   -1  -31  -22  -92  -16  -81   -1  -56  -20  -45
  -14 -112   -1  -25  -39  -48   -1  -41   -1  -68
  -16  -21   -1  -60  -12  -19  -14  -80   -1  -31
   -1  -32  -27  -15  -10  -44   -1  -13  -10  -24
   -1  -21   -1  -32   -8   -9   -1  -16   -1   -7
   -6  -12   -1   -5   -1   -4   -1   -1    0    0
    0    1    1    4    1    5    1   12    6    7
    1   16    1    9    8   32    1   21    1   24
   10   13    1   44   10   15   27   32    1   31
    1   80   14   19   12   60    1   21   16   68
    1   41    1   48   39   25    1  112   14   45
   20   56    1   81   16   92   22   31    1   92
    1   33   51  192   18   61    1   72   26   59
    1  156    1   39   55   80   18   71    1  176
  108   43    1  124   22   45   32  140    1  123
   20   96   34   49   24  272    1   77   75  140

Здесь мы вычисляем арифметические производные всех целых чисел в диапазоне от -99 до 100, отображая их в удобочитаемом виде.

Команда map(D, -99:100) строит массив значений для каждого числа из интервала.

Функция pairs(...) создаёт пары индекс => значение, чтобы можно было отслеживать номер строки.

foreach(...) проходит по этим парам и выводит каждое значение p[2], отформатированное через lpad в поле из 5 символов. Если номер пары делится на 10 (p[1] % 10 == 0), происходит переход на новую строку \n.

Это даёт нам таблицу, показывающую поведение в небольшом интервале.

Вывод производных степеней десятки

Следующий блок рассчитывает и печатает значения для от 1 до 20:

for m in 1:20
    result = D(Int128(10)^m) ÷ 7  # Вычисление D(10^m), затем деление на 7
    println("D for 10^$(rpad(m, 3)) divided by 7 is $result")
end
D for 10^1   divided by 7 is 1
D for 10^2   divided by 7 is 20
D for 10^3   divided by 7 is 300
D for 10^4   divided by 7 is 4000
D for 10^5   divided by 7 is 50000
D for 10^6   divided by 7 is 600000
D for 10^7   divided by 7 is 7000000
D for 10^8   divided by 7 is 80000000
D for 10^9   divided by 7 is 900000000
D for 10^10  divided by 7 is 10000000000
D for 10^11  divided by 7 is 110000000000
D for 10^12  divided by 7 is 1200000000000
D for 10^13  divided by 7 is 13000000000000
D for 10^14  divided by 7 is 140000000000000
D for 10^15  divided by 7 is 1500000000000000
D for 10^16  divided by 7 is 16000000000000000
D for 10^17  divided by 7 is 170000000000000000
D for 10^18  divided by 7 is 1800000000000000000
D for 10^19  divided by 7 is 19000000000000000000
D for 10^20  divided by 7 is 200000000000000000000

Обратите внимание на:

  • Преобразование в Int128: необходимо потому, что при больших степенях десятки 10^m возможен выход за пределы стандартного типа Int.
  • Выражение D(Int128(10)^m) рассчитывает арифметическую производную числа 10^m.
  • Операция целочисленного деления ÷ нужна, чтобы получить целое число для красивого вывода.

Эта часть кода показывает, как поведение производной меняется для степеней десятки.

Заключение

Мы рассмотрели реализацию и работу алгоритма арифметической производной на языке Julia.

Реализация включает функцию D(n), соответствующую формальному определению для целых чисел. Была создана таблица значений производной для небольшого диапазона чисел, и проведен анализ поведения производной для степеней десятки. Такой подход может помочь в исследовании свойств арифметических производных, а также показывает мощь Julia в быстрой работе с числовыми задачами из теории чисел. Пример демонстрирует сочетание точности математических определений и эффективной работы с большими числами благодаря использованию специализированных пакетов (в данном случае — Primes). Также была показана практика читаемого представления результатов и обработки крайних случаев.

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