Биномиальный коэффициент по модулю 142857

Как вычислить биномиальный коэффициент по модулю 142857 для больших n и r. Есть что-то особенное в 142857? Если вопрос по модулю p, где p является простым, тогда мы можем использовать теорему Лукаса, но что должно быть сделано для 142857.

Ответ 1

Алгоритм:

  • факторизует базу в основные степени; 142857 = 3 ^ 3 × 11 × 13 × 37
  • вычислить результат по модулю каждой простой степени
  • объединить результаты, используя теорему китайского останова.

Чтобы вычислить (n above k) mod p^q:

Источник: http://www.dms.umontreal.ca/~andrew/PDF/BinCoeff.pdf, теорема 1

define (n!)_p как произведение чисел 1..n, которые не делятся на p

определить n_j как n после стирания j наименее значимых цифр в базе p

определить r как n - k

define e_j как число переносов при добавлении k+r, не считая переносов из j младших разрядов, вычисление в базе p

определите s как 1, если p=2 & q>=3 и -1 иначе

затем (n above k) mod p^q := p^e_0 * s^e_(q-1) * concatenate(j=d..0)( (n_j!)_p / ((k_j!)_p*(r_j!)_p) ), при этом каждый член конкатенации вычисляет одну базовую цифру результата, наименьшее j вычисляет наименее значимые ненулевые цифры.

Ответ 2

Вы можете рассчитать C(n,k) % m в O(n) время для произвольного m.

Трюк состоит в том, чтобы вычислить n!, k! и (n-k)! в качестве векторов первичной мощности, вычесть их позже и перенести остаток по модулю m. Для C(10, 4) мы делаем следующее:

10! = 2^8 * 3^4 * 5^2 * 7^1
 4! = 2^3 * 3^1
 6! = 2^4 * 3^2 * 5^1

Следовательно

C(10,4) = 2^1 * 3^1 * 5^1 * 7^1

Мы можем легко вычислить это mod m, так как нет делений. Хитрость заключается в вычислении разложения n! и друзей в линейном времени. Если мы прекомпретируем простые числа до n, мы можем сделать это эффективно следующим образом: Ясно, что для каждого четного числа в произведении 1*2*...*9*10 мы получаем коэффициент 2. Для каждого четвертого числа мы получаем вторую и так далее. Следовательно, число 2 факторов в n! составляет n/2 + n/4 + n/8 + ... (где / - настил). Мы делаем то же самое для остальных простых чисел и потому, что существуют O(n/logn) простые числа меньше n, и мы выполняем O(logn) для каждого, разложение является линейным.

На практике я бы назвал его более неявным следующим образом:

func Binom(n, k, mod int) int {
    coef := 1
    sieve := make([]bool, n+1)
    for p := 2; p <= n; p++ {
        if !sieve[p] {
            // Sieve of Eratosthenes
            for i := p*p; i <= n; i += p {
                sieve[i] = true
            }
            // Calculate influence of p on coef
            for pow := p; pow <= n; pow *= p {
                cnt := n/pow - k/pow - (n-k)/pow
                for j := 0; j < cnt; j++ {
                    coef *= p
                    coef %= mod
                }
            }
        }
    }
    return coef
}

Это включает в себя сито эратосфенов, поэтому время работы nloglogn, а не n, если простые числа были предварительно рассчитаны или просеяны с более быстрым ситом.

Ответ 3

Что особенно важно в отношении 142857, так это то, что 7 * 142857 = 999999 = 10 ^ 6 - 1. Это фактор, который возникает из маленькой теоремы Ферма с а = 10 и р = 7, что дает модулярную эквивалентность 10 ^ 7 == 10 (mod 7). Это означает, что вы можете работать по модулю 999999 по большей части и сводить к окончательному модулю, делясь на 7 в конце. Преимущество этого заключается в том, что модульное деление очень эффективно в представлениях основания вида 10 ^ k при k = 1,2,3,6. Все, что вы делаете в таких случаях, - это добавить группы цифр; это обобщение изгнание девяток.

Эта оптимизация действительно имеет смысл, если у вас есть умножение на аппаратное обеспечение-10. Который действительно должен сказать, что он работает хорошо, если вы должны сделать это с помощью бумаги и карандаша. Поскольку эта проблема недавно появилась на онлайн-конкурсе, я представляю, что именно происхождение вопроса.