Алгоритм поиска наименьшего 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:
это неправильно. посмотрим, смогу ли я спасти его...