2015-01-21 121 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; 
    }