是否有一种有效的算法来计算最小整数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))速度。
答案 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)
使用您提到的公式,给定固定k
和p
的{{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.有谁知道如何在降价中显示激进的符号?)
修改:
这是错误的。让我看看能否挽救它......