找到N最小的算法N!可以被提升为权力的素数整除

时间:2011-06-13 01:12:08

标签: algorithm math numbers

是否有一种有效的算法来计算最小整数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的增加,这种方法很快就会崩溃,并且似乎没有一种模式可以推断为更大的值。

2011年6月13日编辑

使用Fiver和Hammar提出的建议,我使用准二分搜索来解决问题但不完全按照他们的建议。使用上面第二个公式的截断版本,我计算了N的上限作为k和p的乘积(仅使用第一项)。我用1作为下限。使用经典二进制搜索算法,我计算了这两个值之间的中点,并计算了在第二个公式中使用此中点值作为N的k,这次使用了所有项。

如果计算的k太小,我调整下限并重复。太大了,我首先测试看中间点1计算的k是否小于期望的k。如果是这样,中点返回为最接近的N.否则,我调整了高点并重复。

如果计算的k相等,我测试了中点-1处的值是否等于中点处的值。如果是这样,我将高点调整为中点并重复。如果中点-1小于期望的k,则返回中点作为期望的答案。

即使k(10位或更多位)的值非常大,此方法也可以使用O(n log(n))速度。

4 个答案:

答案 0 :(得分:4)

好的,这很有趣。

定义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)的商和余数。等等。

这生成序列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。所以f(j)<= 9的最大j是i = 2,f(2)= 4.取商数和9/4的余数。这是2的商(这是我们2位的数字)和其余的1。

对于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。

[更新]

代码总是有助于澄清。将以下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 :(得分:1)

使用您提到的公式,给定固定kp的{​​{1}}值序列不会减少。这意味着您可以使用二进制搜索的变体在给定所需N = 1,2...的情况下查找N

  • k开始,然后计算N = 1
  • 加倍k,直到N大于或等于您想要的k才能获得上限。
  • 在剩余的时间间隔内进行二元搜索以找到您的k

答案 2 :(得分:0)

为什么不使用你提到的第二个公式尝试二元搜索答案?

你只需要考虑N的值,p除以N,因为如果没有,那么N!和(N-1)!被p除以相同的幂,所以N不能是最小的。

答案 3 :(得分:-2)

考虑

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


(P.S.有谁知道如何在降价中显示激进的符号?)

修改:

这是错误的。让我看看能否挽救它......