我正在解决编程问题,最后问题归结为计算以下术语:
n!/(n1!n2!n3!....nm!)
n<50000
(n1+n2+n3...nm)<n
我被告知最终答案将适合8字节。我正在使用C ++。我该如何计算呢。我能够提出一些技巧,但没有具体和概括。
编辑: 我不想使用外部库。
EDIT1: 添加条件和结果肯定是64位int。
答案 0 :(得分:5)
如果结果保证是整数,则使用因式表示。
通过theorem of Legendre,您可以通过范围(2,n)中素数的指数序列表达所有这些因子。
通过从分子中扣除分母中的阶乘的指数,您将获得整个商的指数。然后计算将减少到永远不会溢出8个字节的素数乘积。
例如,
25! = 2^22.3^10.5^6.7^3.11^2.13.17.19.23
15! = 2^11.3^6.5^3.7^2.11.13
10! = 2^8.3^4.5^2.7
产量
25!/(15!.10!) = 2^3.5.11.17.19.23 = 3268760
<3>的指数由
找到25/3 + 25/9 = 10
15/3 + 15/9 = 6
10/3 + 10/9 = 4
答案 1 :(得分:2)
如果所有输入(不一定是输出)都是由整数组成,您可以尝试计算素因子。您创建一个大小为sqrt(n)的数组,并用n:
中每个素数因子的计数填充它vector <int> v = vector <int> (sqrt(n)+1,0);
int m = 2;
while (m <=n) {
int i = 2;
int a = m;
while (a >1) {
while (a%i ==0) {
v[i] ++;
a/=i;
}
i++;
}
m++;
}
然后迭代n_k(1&lt; = k&lt; = m)并减少每个素因子的计数。这几乎与上面的代码相同,只是你用v [i] - 替换v [i] ++。当然,您需要使用先前获得的矢量v来调用它。
之后,向量v包含表达式中素数因子的计数列表,您只需将结果重建为
int result = 1;
for (int i = 2; i < v.size(); v++) {
result *= pow(i,v[i]);
}
return result;
注意:你应该使用long long int而不是int,但为了简单起见我坚持使用int
编辑:正如另一个答案中所提到的,最好使用勒让德定理更快地填充/取消矢量v。
答案 2 :(得分:0)
你可以做的是使用对数的属性:
log(AB) = log(A) + log(B)
log(A/B) = log(A) - log(B)
和
X = e^(log(X))
因此,您可以先计算数量的对数,然后取幂:
log(N!/(n1!n2!...nk!)) = log(1) + ... + log(N) - [log(n1!) - ... log(nk!)]
然后展开log(n1!)
等等,这样你就可以用单个数字的对数来编写所有内容。然后取结果的指数来获得阶乘的初始值。
As @ T.C。提到,这种方法可能不准确,尽管在典型的情况下,你会减少许多术语。或者,您将每个阶乘扩展为一个列表,该列表将术语存储在其产品中,例如: 6!
将存储在列表{1,2,3,4,5,6}
中。您对分母术语也这样做。然后开始删除常用元素。最后,您可以使用gcd并将所有内容减少为互质因子,然后计算结果。