Алгоритм поиска наименьшего N такой, что N! делиться на основную подняли к власти

существует ли эффективный алгоритм вычисления наименьшего целого числа N, такого, что N! делится на p^k, где p-относительно небольшое простое число, а k-очень большое целое число. Другими словами,

factorial(N) mod p^k == 0

Если, учитывая N и p, я хотел найти, сколько раз p делится на N!, Я бы использовал известную формулу

k = Sum(floor(N/p^i) for i=1,2,...

Я сделал поиск грубой силы для небольших значений k, но этот подход очень быстро ломается по мере увеличения k, и, похоже, быть шаблон, который я могу экстраполировать на большие значения.

редактировать 6/13/2011

Если вычисленный k был слишком мал, я отрегулировал нижнюю границу и повторил. Слишком большой, я сначала проверил, был ли K, вычисленный в средней точке-1, меньше желаемого k. Если это так, средняя точка была возвращена как ближайшая N. В противном случае я отрегулировал высокую точку и повторил.

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

даже при очень больших значениях для k (10 или более цифр) этот подход работает со скоростью o(n log(n)).

4 ответов


используя формулу, которую вы упомянули, последовательность k значения заданы фиксированные p и N = 1,2... не уменьшается. Это означает, что вы можете использовать вариант двоичного поиска, чтобы найти N дали нужные k.

  • Начнем с N = 1 и расчета k.
  • двойной N до k больше или равно, чем ваш желаемый k чтобы получить верхнюю границу.
  • выполните двоичный поиск на оставшемся интервале, чтобы найти k.

OK это своего рода весело.

определить f (i) = (p^i - 1) / (p - 1)

напишите k в виде забавной "базы", где значение позиции i-это f(i).

вы делаете это от наиболее значимой до наименее значимой цифры. Итак, сначала найдите наибольшее j такое, что f (j)

это генерирует последовательность q_j, q_{j-1}, q_{j-2}, ..., q_1. (Обратите внимание, что последовательность заканчивается на 1, а не 0.) Затем вычислите q_j * p^j + q_{j-1}*p^(j-1) + ... q_1*p. Это твоя Н.

пример: k = 9, p = 3. Итак, f (i) = (3^i - 1) / 2. f (1) = 1, f(2) = 4, f(3) = 13. Таким образом, наибольшее j с f(j)

для этого остатка 1 Найдите фактор и остаток 1 / f(1). Частное 1, остаток равен нулю, поэтому мы сделали.

так q_2 = 2, q_1 = 1. 2*3^2 + 1*3^1 = 21, - право Н.

у меня есть объяснение на бумаге, почему это работает, но я не уверен, как передать его в тексте... Обратите внимание, что f (i) отвечает на вопрос: "сколько факторов p существует в (p^i)!". Как только вы найдете самое большое i, j такое, что j*f(i) меньше k, и поймете, что на самом деле вы находите самое большое j*p^i меньше N, остальной вид выпадает из стирки. В нашем примере р=3, например, мы получаем 4 р, внесенных продуктом 1-9, еще 4, внесенных продуктом 10-18, и еще один, внесенный 21. Эти первые два являются кратными p^2; f (2) = 4 говорит нам, что каждое кратное p^2 вносит 4 больше p в товар.

[обновление]

код всегда помогает прояснить. Сохраните следующий скрипт perl как foo.pl и запустите его как foo.pl <p> <k>. Обратите внимание, что ** является оператором возведения в степень Perl,bdiv вычисляет фактор и остаток для BigInts (целых чисел с неограниченной точностью) и use bigint говорит Perl использовать BigInts везде.

#!/usr/bin/env perl

use warnings;
use strict;
use bigint;

@ARGV == 2
    or die "Usage:  <p> <k>\n";

my ($p, $k) = map { Math::BigInt->new($_) } @ARGV;

sub f {
    my $i = shift;
    return ($p ** $i - 1) / ($p - 1);
}

my $j = 0;
while (f($j) <= $k) {
    $j++;
}
$j--;

my $N = 0;

my $r = $k;
while ($r > 0) {
    my $val = f($j);
    my ($q, $new_r) = $r->bdiv($val);
    $N += $q * ($p ** $j);
    $r = $new_r;
    $j--;
}

print "Result: $N\n";

exit 0;

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

вам нужно только рассмотреть значения для N, для которых p делит N, потому что если это не так, то N! и (N-1)! делятся на одну и ту же силу p, поэтому N не может быть наименьшим.


считают

I = (pn)!

и игнорировать простые факторы, отличные от p. Результат выглядит как

I = pn * pn-1 * pn-2 * ... * p * 1
I = pn + (n-1) + (n-2) + ... 2 + 1
I = p(n2 +n) / 2

Итак, мы пытаемся найти наименьшее n такое, что

(n2 +n) / 2 > = k

что если я помните, что квадратичное уравнение справа дает нам

N = pn, где n > = (sqrt (1+8k) -1)/2


(П. С. кто-нибудь знает, как показать Радикальной символ в уценке?)

EDIT:

это неправильно. посмотрим, смогу ли я спасти его...