在我的C代码中,我想计算1到100范围内数字的阶乘。对于小数字,该函数可以工作,但对于更大的数字,例如100!它返回不正确的结果。有什么办法在C中处理大数的阶乘?我正在使用的编译器是gcc v4.3.3。 我的代码如下:
#include <stdio.h>
#include <math.h>
double print_solution(int);
int main(void)
{
int no_of_inputs,n ;
int ctr = 1;
scanf("%d",&no_of_inputs); //Read no of inputs
do
{
scanf("%d",&n); //Read the input
printf("%.0f\n",print_solution(n));
ctr++;
}while(ctr <= no_of_inputs);
return 0;
}
double print_solution(int n)
{
if(n == 0 || n == 1)
return 1;
else
return n*print_solution(n-1);
}
答案 0 :(得分:37)
没有标准C数据类型可以准确处理大到100的数字!如果您通过图书馆或自己完成arbitrary precision integer arithmetic,则唯一的选择。
如果这只是一些爱好项目,我建议你自己尝试一下。这是一种有趣的运动。如果这与工作相关,请使用预先存在的库。
您通常会获得的最大C数据类型是64位整数。 100!大约是10 157 ,它将500位的大部分精确存储为整数。
答案 1 :(得分:17)
100阶乘是巨大的,准确地说是93326215443944152681699238856266700490715968264381621468592963895217 59999322991560894146397615651828625369792082722375825118521091686400 00000000000000000000。
也许你应该使用像GMP这样的bignum库。它有很好的文档,非常一致的界面,速度,如果你在Linux上你的发行版可能有一个包(我想我的默认安装它)
答案 2 :(得分:14)
要大致计算大数的阶乘,你可以这样:
n! = n * (n-1)! so log(n!) = log(n) + log(n-1!)
现在您可以使用动态编程来计算log(n!)并计算
N! as(base)^(log-value)
答案 3 :(得分:9)
如果您不想使用bigint库,那么使用stdlib可以做的最好就是使用long double
中的tgammal()
和math.h
:
long double fact(unsigned n)
{
return tgammal(n + 1);
}
这将使你100!
在x86上具有18位小数的精度(即80位long double
)。
确切的实现也不是那么复杂:
#include <math.h>
#include <stdio.h>
#include <string.h>
void multd(char * s, size_t len, unsigned n)
{
unsigned values[len];
memset(values, 0, sizeof(unsigned) * len);
for(size_t i = len; i--; )
{
unsigned x = values[i] + (s[i] - '0') * n;
s[i] = '0' + x % 10;
if(i) values[i - 1] += x / 10;
}
}
void factd(char * s, size_t len, unsigned n)
{
memset(s, '0', len - 1);
s[len - 1] = '1';
for(; n > 1; --n) multd(s, len, n);
}
int main(void)
{
unsigned n = 100;
size_t len = ceill(log10l(tgammal(n + 1)));
char dstr[len + 1];
dstr[len] = 0;
factd(dstr, len, n);
puts(dstr);
}
答案 4 :(得分:5)
每个人都在告诉你正确的答案,但还有几点。
您最初使用double来获得更宽范围的想法是行不通的,因为double不能精确地存储这些数据。它可以进行计算,但需要进行大量舍入。这就是bigint库存在的原因。
我知道这可能是教程或示例网站的一个例子,但是做无限递归会在某些时候咬你。您有一个基本上是迭代过程的递归解决方案。您将理解为什么当您尝试使用较大的值运行程序时,此站点的名称是什么(尝试10000)。
一种简单的迭代方法如下
int answer, idx;
for (answer = 1, idx = 1; idx <= no_of_inputs; idx++ ) {
answer = answer * idx;
}
printf("Factorial of %3d = %d\n", no_of_inputs, answer);
答案 5 :(得分:2)
这是我几年前解决谷歌谜语所做的,它使用GMP库http://gmplib.org/:
#include <stdio.h>
#include "gmp.h"
void fact(mpz_t r,int n){
unsigned int i;
mpz_t temp;
mpz_init(temp);
mpz_set_ui(r,1);
for(i=1;i<=n;i++){
mpz_set_ui(temp,i);
mpz_mul(r,r,temp);
}
mpz_clear(temp);
}
int main(void) {
mpz_t r;
mpz_init(r);
fact(r,188315);
/* fact(r,100); */
gmp_printf("%Zd\n",r);
mpz_clear(r);
return(0);
}
gcc -lgmp -o fact fact.c
./事实
答案 6 :(得分:1)
您可以尝试使用“unsigned long long”类型,但这是内置类型可以获得的最大值。 我建议(正如cletus已经提到的那样)要么采用已知的大数字实现,要么自己编写一个。 “这是一个很好的运动”x 2。
答案 7 :(得分:1)
如果您只想使用标准数据类型而不需要确切的答案,那么计算n的对数!而不是n!本身。 n的对数!很容易适合double
(除非n很大)。
答案 8 :(得分:1)
最多可容纳12个!适合32位整数。阶乘高达20!适合64位整数。在那之后,您在大多数计算机上用尽了所有比特。但是34!适合无符号的128位整数57!可以容纳256位整数,并且为98!适合无符号512位整数。要计算100!作为整数,至少需要525位。
此bc
脚本可计算阶乘(最多35个!但是您可以轻松更改限制):
#!/usr/bin/bc -l
define f(n) {
auto r, i
r = 1
for (i = 1; i <= n; i++)
{
r *= i;
print "n = ", i, ", log2 = ", l(r)/l(2), ", n! = ", r, "\n"
}
}
f(35)
quit
以及一些示例值:
# Key values
# n = 1, log2 = 0.00000000000000000000, n! = 1
# n = 2, log2 = 1.00000000000000000000, n! = 2
# n = 3, log2 = 2.58496250072115618147, n! = 6
# n = 4, log2 = 4.58496250072115618149, n! = 24
# n = 5, log2 = 6.90689059560851852938, n! = 120
# n = 6, log2 = 9.49185309632967471087, n! = 720
# n = 7, log2 = 12.29920801838727881834, n! = 5040
# n = 8, log2 = 15.29920801838727881836, n! = 40320
# n = 9, log2 = 18.46913301982959118130, n! = 362880
# n = 10, log2 = 21.79106111471695352921, n! = 3628800
# n = 11, log2 = 25.25049273335425078544, n! = 39916800
# n = 12, log2 = 28.83545523407540696694, n! = 479001600
# n = 13, log2 = 32.53589495221649912738, n! = 6227020800
# n = 14, log2 = 36.34324987427410323486, n! = 87178291200
# n = 15, log2 = 40.25014046988262176421, n! = 1307674368000
# n = 16, log2 = 44.25014046988262176426, n! = 20922789888000
# n = 17, log2 = 48.33760331113296117256, n! = 355687428096000
# n = 18, log2 = 52.50752831257527353551, n! = 6402373705728000
# n = 19, log2 = 56.75545582601885902935, n! = 121645100408832000
# n = 20, log2 = 61.07738392090622137726, n! = 2432902008176640000
# n = 21, log2 = 65.46970134368498166621, n! = 51090942171709440000
# ...
# n = 34, log2 = 127.79512061296909618950, n! = 295232799039604140847618609643520000000
# n = 35, log2 = 132.92440362991406264487, n! = 10333147966386144929666651337523200000000
# ...
# n = 57, log2 = 254.48541573017643505939
# n = 58, log2 = 260.34339672530400718017
# ...
# n = 98, log2 = 511.49178048020535201128
# n = 99, log2 = 518.12113710028496163045
# n = 100, log2 = 524.76499329005968632625
对于阶乘57!,58!,98!,99!,100!我已经省略了阶乘值,因为它分布在输出中的多行中,而且并不是那么重要。注意100!至少需要525位精度。
此代码在我的GitHub上的SOQ(堆栈溢出问题)存储库中,作为src/miscellany子目录中的文件factorial.bc
提供。
您可以使用double
或long double
来扩展值的范围,但会损失一些准确性。
答案 9 :(得分:0)
我猜那是因为你的int范围溢出,大约是。 20亿。如果使用unsigned int,最多可以获得40亿,但除此之外,您必须使用bigint library。
答案 10 :(得分:0)
100! = 933262154439441526816992388562667004907159682643816214685929 6389521759999322991560894146397156518286253697920827223758251185210 916864000000000000000000000000
你不能用int或long来表示这个大数字。
答案 11 :(得分:0)
这肯定是由于溢出造成的。你需要一种表示大数字的方法(unsigned long long
甚至不会覆盖25个!)。
答案 12 :(得分:0)
除了其他人的建议外,我建议您熟悉基本类型(int,long,long long,...)的存储限制,无论您实际使用的是什么计算机/平台。 (“如果有疑问,请打印更多!”)
之前的一张海报提到了80位精度限制,但这对于x86 CPU来说是特别的。
另一个人多次引用ISO C90,尽管C99是最新标准;即使许多编译器还没有完全实现C99,你可能会发现它们很可能至少支持long long,这应该对应于&gt; = 64位精度。
答案 13 :(得分:0)
这真是一个老问题。我看到大多数答案提示外部库或近似结果,显示内存限制。但是,请稍微考虑一下 - 您不必总是在编程中使用integer
或double
或unsigned long long
进行数学运算!
我使用int[]
来计算 Big Factorials 。这个小的 Java 代码可以(理论上)找出 任何数字的
public class BigFactorial {
public static int[] calculateFactorial(int inputNumber) {
int[] factorial = initializeFactorial(inputNumber);
for(int i=inputNumber-1, j, k; i>0; i--){
for(j=factorial.length-1, k=0; factorial[j] >= 0; j--){
k += i*factorial[j];
factorial[j] = k%10;
k /= 10;
}
factorial[j] = k%10;
k /= 10;
factorial[j-1] = k;
for(j=0; factorial[j]<1; j++){
factorial[j] = -1;
}
}
return factorial;
}
private static int[] initializeFactorial(int inputNumber){
int digits = (int) Math.ceil(inputNumber*Math.log10(inputNumber/2))+2;
int[] factorial = new int[digits+1];
for(int i=0; i<factorial.length; i++){
factorial[i] = -1;
}
for(int j=factorial.length-1, i=inputNumber; i>0; j--){
factorial[j] = i%10;
i /= 10;
}
return factorial;
}
public static void showOutput(int[] factorial){
int i=0;
while(factorial[i]<1){
i++;
}
for(; i<factorial.length; i++){
System.out.print(factorial[i]);
}
}
///test
public static void main(String[] args) {
int inputNumber = 5000;
showOutput(calculateFactorial(inputNumber));
}
}
答案 14 :(得分:0)
以下是您的问题的解决方案:
#include <stdio.h>
void factorial(int b){
int temp = 0, r, size = 0, x;
int arr[200] = {0};
int l_b = b-1;
while(b>0){
r = b%10;
arr[size++] = r;
b = b/10;
}
while(l_b >= 2){
int i=0;
while(size>0){
x = arr[i]*l_b+temp ;
arr[i++] = x%10;
temp = x/10;
size--;
}
while(temp>0){
arr[i++] = temp%10;
temp = temp/10;
}
size = i; --l_b;
}
for(int k=size-1;k>=0;k--)
printf("%d",arr[k]);//ok i'm taking space here
printf("\n");
}
int main(void) {
// your code goes here
int fact;
scanf("%d\n",&fact);
factorial(fact);
return 0;
}
答案 15 :(得分:-4)
我认为不要使用递归算法,它超级慢,即使它被缓存也会很慢。这只是你应该考虑的事情。
原因是当你调用fact(100)时你实际上没有运行100次,你实际上运行该函数 5050 次。哪个是坏的,如果它被缓存然后它可能是100次,但是,使用if语句运行函数调用然后运行循环仍然较慢。
double print_solution(int n)
{
double rval = 1;
unsigned int i;
for( i = 1; i <= n; i++ ) {
rval *= i;
}
return rval;
}
使用仲裁精度算法可以使它变得非常高,但是,您需要使用库来执行此操作,或者您可以创建自己的库,但这需要花费很多时间。