2015-01-21 126 views
1

我是计算机工程专业的学生,​​下一学期我将开始C课程。所以为了让自己准备一下,我开始自己学习C,偶然发现了一个有趣的任务,这个任务是为了我的乍一看,不是一个非常先进的水平而设计的。C中的Pascal三角形

任务是编写一个程序来计算给定位置的值,在帕斯卡的三角形。并且给出计算它的公式写为element = row! /(位置*(行 - !位置)!)

我已经写了,似乎工作正常,直到我与号测试它一个简单的控制台程序。

当使用第16行和第3位的程序尝试此程序时,它将计算值为0,虽然很明显不存在这样的值(实际上它应该计算值为560),但是此所有单元三角形应该是整数并且大于1。

我想我遇到一个问题存储和处理大数。阶乘函数似乎工作正常,我使用的公式工作,直到我尝试大量

到目前为止最好的解决方案是在这里找到 - How do you printf an unsigned long long int(the format specifier for unsigned long long int)?使用inttypes.h库类型uint64_t但它仍然没有给我需要的结果。

#include <stdio.h> 
#include <stdlib.h> 
#include <inttypes.h> 

void clear_input(void); 
uint64_t factorial(int x); 

int main() 
{ 
    // Printing 
    printf("This program computes the value of a given position in Pascal's Triangle.\n"); 
    printf("You will be asked for row and position of the value.\n"); 
    printf("Note that the rows and positions starts from 0.\n"); 
    printf("\n"); 
    printf("  1   * 0 \n"); 
    printf(" 1 1   * 1 \n"); 
    printf(" 1 2 1  * 2 \n"); 
    printf(" 1 3 3 1  * 3 \n"); 
    printf(" 1 4 6 4 1  * 4 \n"); 
    printf(" **************** \n"); 
    printf(" 0 1 2 3 4   \n"); 
    printf("\n"); 

    // Initializing 
    int row, pos; 

    // Input Row 
    printf("Enter the row: "); 
    scanf("%d", &row); 
    clear_input(); 

    // Input Position 
    printf("Enter the position in the row: "); 
    scanf("%d", &pos); 
    clear_input(); 

    // Initializing 
    uint64_t element, element_1, element_2, element_3, element_4; 

    // Previously written as -> element = (factorial(row))/(factorial(pos) * factorial(row - pos)); 
    // Doesn't fix the problem 
    element_1 = factorial(row); 
    element_2 = factorial(pos); 
    element_3 = factorial(row - pos); 
    element_4 = element_2 * element_3; 

    element = element_1/element_4; 

    // Print result 
    printf("\n"); 
    printf("%"PRIu64"\n", element_1); // Temporary output 
    printf("%"PRIu64"\n", element_2); // Temporary output 
    printf("%"PRIu64"\n", element_3); // Temporary output 
    printf("%"PRIu64"\n", element_4); // Temporary output 
    printf("\n"); 
    printf("The element is %"PRIu64"", element); 
    printf("\n"); 

    return 0; 
} 

void clear_input(void)           // Temporary function to clean input from the keyboard 
{ 
    while(getchar() != '\n'); 
} 

uint64_t factorial(int x)          // Function to calculate factorial 
{ 
    int f = 1, i = x; 
    if (x == 0) { 
     return 1; 
    } 
    while (i != 1) { 
     f = f * i; 
     i = i - 1; 
    } 
    return f; 
} 
+1

如果使用大量的(> 32位),然后使用'int'是会得到你不正确的结果。如果您正在使用'uint64_t'数据类型来指定返回类型,那么您需要使用相同的数据类型来计算您的计算。你的函数现在在内部使用了一个'int',并且结果被隐式地转换成'uint64_t',这对你没有什么帮助。 – 2015-01-21 01:20:23

回答

1

因子获得really big really fast(向下滚动一下以查看列表)。即使是64位的数字也只能是20!。所以在开始相乘之前,你必须做一些预处理。

总体思路是将分子和分母因子分解,并删除所有常见因素。由于Pascal三角形的结果总是整数,因此可以保证,除去所有常见因素后,分母将为1。

例如,假设您有row=35position=10。则计算是

element = 35!/10! * 25! 

35 * 34 * 33 * ... * 26 * 25 * 24 * ... * 3 * 2 * 1 
--------------------------------------------------- 
    10!    * 25 * 24 * ... * 3 * 2 * 1 

所以第一个简化是,在分母越大阶乘取消所有分子的小项。哪片叶子

35 * 34 * 33 * ... * 26 
----------------------- 
10 * 9 * 8 * ... * 1  

现在我们需要去除分子和分母中的其余公因子。它有助于将分子的所有数字放入一个数组中。然后,对于分母中的每个数字,计算greatest common divisor(gcd)并用gcd除分子和分母。

以下代码演示了该技术。

array[10] = { 35, 34, 33, 32, 31, 30, 29, 28, 27, 26 }; 

for (d = 10; d >= 2; d--) 
{ 
    temp = d; 
    for (i = 0; i < 10 && temp > 1; i++) 
    { 
     common = gcd(array[i], temp); 
     array[i] /= common; 
     temp /= common; 
    } 
} 

这里是代码一步

d=10 i=0 temp=10 array[0]=35 ==> gcd(35,10)=5, so array[0]=35/5=7 and temp=10/5=2 
d=10 i=1 temp=2 array[1]=34 ==> gcd(34, 2)=2, so array[1]=34/2=17 and temp=2/2=1 
inner loop breaks because temp==1 
d=9 i=0 temp=9 array[0]=7 ==> gcd(7,9)=1, so nothing changes 
d=9 i=1 temp=9 array[1]=17 ==> gcd(17,9)=1, so nothing changes 
d=9 i=2 temp=9 array[2]=33 ==> gcd(33,9)=3, so array[2]=11 and temp=3 
d=9 i=3       ==> gcd(32,3)=1 
d=9 i=4       ==> gcd(31,3)=1 
d=9 i=5 temp=3 array[5]=30 ==> gcd(30,3)=3, so array[5]=10 and temp=1 
inner loop breaks 

确实步当所有说的和做的阵列最终成为

array[10] = { 1, 17, 11, 1, 31, 1, 29, 14, 3, 26 } 

乘这些数字加起来,答案是183579396,整个计算可以使用32位整数进行。一般来说,只要答案符合32位,计算就可以用32位完成。

1

当你计算阶乘,即使你是返回一个64位整数,如果你要使用正规INT变量的中间计算它不会有所作为。更改为:

uint64_t factorial(uint64_t x) 
{ 
    uint64_t f = 1, i = x; 
    if (x == 0) { 
     return 1; 
    } 
    while (i != 1) { 
     f = f * i; 
     i = i - 1; 
    } 
    return f; 
} 

此外,请考虑如何重新排列方程,以便您不必计算真正的大中间值。例如,您可以重新排列为:

element =(factorial(row)/ factorial(pos))/ factorial(row-pos);

那么你不会将两个因子乘以一起并得到一个非常大的数字。另外,当计算阶乘(行)/阶乘(pos)时,可以消除将以阶乘(行)和阶乘(pos)为单位的项,因此不需要计算整个阶乘。

3

(我的C是生疏了,所以这可能不是超级准确)

你的阶乘函数返回一个uint64_t中,但它做定期整数计算。如果你把f和我改为uint64_t,我想你会避免你当前的整数溢出问题。

但是,你仍然会很快遇到溢出(uint64_t会在21左右溢出!)。为了避免这种情况,您可以使用算法更聪明一些。行= 16,位置= 3,你需要16! /(3!* 13!)。您可以取消大部分条款(16!/ 13!仅为14 * 15 * 16),并以14 * 15 * 16 /(1 * 2 * 3)结尾。这会让你的程序比第21行走得更远。

0

这将工作:

#include <stdio.h> 

int main() 
    { 
    printf ("\n"); 
    int n = 10; 
    int i; 
    int j; 
    int x[n]; 

    for (i = 0; i < n; i++) 
     x[i] = 0; 

    for (i = 1; i <= n; i++) 
     { 
     for (j = n - 1; j >= 1; j--) 
       x[j] = x[j-1] + x[j]; 

     x[0] = 1; 

     int s = n - i; 

     for (j = 0; j < s; j++) 
       printf (" "); 

     for (j = 0; j < n; j++) 
       { 
       if (x[j] != 0) 
        printf (" %3d", x[j]); 
       } 

     printf ("\n"); 
     } 

    printf ("\n"); 
    return 0; 
    }