找到最小 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
根据 Fiver 和 Hammar 提出的建议,我使用准二进制搜索来解决问题,但并不完全按照他们建议的方式。使用上面第二个公式的截断版本,我将 N 的上限计算为 k 和 p 的乘积(仅使用第一项)。我使用 1 作为下限。使用经典的二分搜索算法,我计算了这两个值之间的中点,并使用该中点值作为第二个公式中的 N 来计算 k 的值,这次使用了所有项。
如果计算出的 k 太小,我会调整下限并重复。太大了,我首先测试一下在 midpoint-1 计算的 k 是否小于所需的 k。如果是这样,则返回中点作为最接近的 N。否则,我调整高点并重复。
如果计算出的 k 相等,我测试 midpoint-1 处的值是否等于 midpoint 处的值。如果是这样,我将高点调整为中点并重复。如果 midpoint-1 小于所需的 k,则返回中点作为所需的答案。
即使 k 值非常大(10 位或更多数字),这种方法也能以 O(n log(n)) 的速度运行。
Is there an efficient algorithm to compute the smallest integer N such that N! is divisible by p^k where p is a relatively small prime number and k, a very large integer. In other words,
factorial(N) mod p^k == 0
If, given N and p, I wanted to find how many times p divides into N!, I would use the well-known formula
k = Sum(floor(N/p^i) for i=1,2,...
I've done brute force searches for small values of k but that approach breaks down very quickly as k increases and there doesn't appear to be a pattern that I can extrapolate to larger values.
Edited 6/13/2011
Using suggestions proposed by Fiver and Hammar, I used a quasi-binary search to solve the problem but not quite in the manner they suggested. Using a truncated version of the second formula above, I computed an upper bound on N as the product of k and p (using just the first term). I used 1 as the lower bound. Using the classic binary search algorithm, I computed the midpoint between these two values and calculated what k would be using this midpoint value as N in the second formula, this time with all the terms being used.
If the computed k was too small, I adjusted the lower bound and repeated. Too big, I first tested to see if k computed at midpoint-1 was smaller than the desired k. If so, midpoint was returned as the closest N. Otherwise, I adjusted the highpoint and repeated.
If the computed k were equal, I tested whether the value at midpoint-1 was equal to the value at midpoint. If so, I adjusted the highpoint to be the midpoint and repeated. If midpoint-1 was less than the desired k, the midpoint was returned as the desired answer.
Even with very large values for k (10 or more digits), this approach works O(n log(n)) speeds.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
好吧,这很有趣。
定义 f(i) = (p^i - 1) / (p - 1)
将 k 写成一种有趣的“基数”,其中位置 i 的值就是这个 f(i)。
您可以从最高有效数字到最低有效数字执行此操作。首先,找到满足 f(j) <= k 的最大 j。然后计算 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^i) 中有 p 有多少个因子!”。一旦你找到了最大的 i,j 使得 j*f(i) 小于 k,并且意识到你真正在做的是找到小于 N 的最大的 j*p^i,剩下的就变得不那么重要了。例如,在我们的 p=3 示例中,我们得到 1-9 的乘积贡献的 4 个 p,10-18 的乘积贡献的另外 4 个 p,以及 21 的乘积贡献的另外一个。前两个只是 p^ 的倍数2; f(2) = 4 告诉我们,p^2 的每个倍数都会为乘积贡献 4 个以上的 p。
[更新]
代码总是有助于澄清。将以下 perl 脚本保存为
foo.pl
并以foo.pl
运行它
。请注意,**
是 Perl 的幂运算符,bdiv
计算 BigInts(无限精度整数)的商和余数,use bigint
告诉 Perl到处使用 BigInt。OK this is kind of fun.
Define f(i) = (p^i - 1) / (p - 1)
Write k in a kind of funny "base" where the value of position i is this f(i).
You do this from most-significant to least-significant digit. So first, find the largest j such that f(j) <= k. Then compute the quotient and remainder of k / f(j). Store the quotient as q_j and the remainder as r. Now compute the quotient and remainder of r / f(j-1). Store the quotient as q_{j-1} and the remainder as r again. Now compute the quotient and remainder of r / f(j-2). And so on.
This generates a sequence q_j, q_{j-1}, q_{j-2}, ..., q_1. (Note that the sequence ends at 1, not 0.) Then compute q_j*p^j + q_{j-1}*p^(j-1) + ... q_1*p. That's your N.
Example: k = 9, p = 3. So f(i) = (3^i - 1) / 2. f(1) = 1, f(2) = 4, f(3) = 13. So the largest j with f(j) <= 9 is i = 2 with f(2) = 4. Take the quotient and remainder of 9 / 4. That's a quotient of 2 (which is the digit in our 2's place) and remainder of 1.
For that remainder of 1, find the quotient and remainder of 1 / f(1). Quotient is 1, remainder is zero, so we are done.
So q_2 = 2, q_1 = 1. 2*3^2 + 1*3^1 = 21, which is the right N.
I have an explanation on paper for why this works, but I am not sure how to communicate it in text... Note that f(i) answers the question, "how many factors of p are there in (p^i)!". Once you find the largest i,j such that j*f(i) is less than k, and realize what you are really doing is finding the largest j*p^i less than N, the rest kind of falls out of the wash. In our p=3 example, for instance, we get 4 p's contributed by the product of 1-9, 4 more contributed by the product of 10-18, and one more contributed by 21. Those first two are just multiples of p^2; f(2) = 4 is telling us that each multiple of p^2 contributes 4 more p's to the product.
[update]
Code always helps to clarify. Save the following perl script as
foo.pl
and run it asfoo.pl <p> <k>
. Note that**
is Perl's exponentiation operator,bdiv
computes a quotient and remainder for BigInts (unlimited-precision integers), anduse bigint
tells Perl to use BigInts everywhere.使用您提到的公式,给定固定
p
和N = 1,2...
的k
值序列是非递减的。这意味着您可以使用二分搜索的变体来查找给定所需k
的N
。N = 1
开始,计算k
。N
直到k
大于或等于您想要的k
以获得上限。k
。Using the formula you mentioned, the sequence of
k
values given fixedp
andN = 1,2...
is non-decreasing. This means you can use a variant of binary search to findN
given the desiredk
.N = 1
, and calculatek
.N
untilk
is greater or equal than your desiredk
to get an upper bound.k
.为什么不尝试使用您提到的第二个公式进行二分搜索来寻找答案?
您只需要考虑 N 的值,其中 p 除以 N,因为如果不是,则为 N!和(N-1)!除以 p 的同次幂,因此 N 不可能是最小的。
Why don't you try binary search for the answer, using the second formula you mentioned?
You only need to consider values for N, for which p divides N, because if it doesn't, then N! and (N-1)! are divided by the same power of p, so N can't be the smallest one.
考虑
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
(PS有谁知道如何在markdown中显示偏旁符号?)
编辑:
这是错误的。让我看看是否可以挽救它......
Consider
I = (pn)!
and ignore prime factors other than p. The result looks like
I = pn * pn-1 * pn-2 * ... * p * 1
I = pn + (n-1) + (n-2) + ... 2 + 1
I = p(n2 +n)/2
So we're trying to find the smallest n such that
(n2 +n)/2 >= k
which if I remember the quadratic equation right gives us
N = pn, where n >= (sqrt(1+8k) -1)/2
(P.S. Does anyone know how to show the radical symbol in markdown?)
EDIT:
This is wrong. Let me see if I can salvage it...