Алгоритм нахождения наименьшего N такого, что N!делится на простое число, возведенное в степень - PullRequest
6 голосов
/ 13 июня 2011

Существует ли эффективный алгоритм для вычисления наименьшего целого числа 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

Используя предложения, предложенные Fiver и Hammar, я использовал квазибинарный поиск для решения проблемы, но не совсем так, как они предлагали.Используя усеченную версию второй формулы выше, я вычислил верхнюю границу N как произведение k и p (используя только первое слагаемое).Я использовал 1 в качестве нижней границы.Используя классический алгоритм двоичного поиска, я вычислил среднюю точку между этими двумя значениями и вычислил, какое k будет использовать это значение средней точки как N во второй формуле, на этот раз со всеми используемыми терминами.

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

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

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

Ответы [ 4 ]

4 голосов
/ 13 июня 2011

ОК, это забавно.

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

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

Вы делаете это от старшей к младшей значащей цифры.Итак, сначала найдите наибольшее j такое, что f (j) <= k.Затем вычислим частное и остаток от k / f (j).Сохраните частное как q_j, а остаток как r.Теперь вычислим частное и остаток от r / f (j-1).Сохраните частное как q_ {j-1}, а остаток снова как r.Теперь вычислим частное и остаток от r / f (j-2).И так далее. </p>

Это генерирует последовательность 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.Это ваш N.

Пример: k = 9, p = 3. Так что f (i) = (3 ^ i - 1) / 2. f (1) = 1, f (2) = 4,f (3) = 13. Таким образом, наибольшее j с f (j) <= 9 равно i = 2 с f (2) = 4. Возьмите частное и остаток от 9 / 4. Это частное от 2 (которое являетсяцифра вместо нашего 2) и остаток от 1. </p>

Для этого остатка от 1 найдите частное и остаток от 1 / f (1).Коэффициент равен 1, остаток равен нулю, так что мы закончили.

Итак, q_2 = 2, q_1 = 1. 2 * 3 ^ 2 + 1 * 3 ^ 1 = 21, что является правильным N.

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

[update]

Код всегда помогает уточнить.Сохраните следующий скрипт 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: $0 <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;
1 голос
/ 13 июня 2011

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

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

Рассмотрим

I = (p n )!

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

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

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

(n 2 + n) / 2> = k

, который, если я помню, правильно дает нам квадратное уравнение

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


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

EDIT:

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

0 голосов
/ 13 июня 2011

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...