2014-10-20 132 views
0
#include <stdio.h> 
int main(){ 
    int n, v; 
    printf("Please enter a value from 39 to 59: \n"); 
    scanf("%d", &n); 
    printf("Please enter a value from 3 to 7: \n"); 
    scanf("%d", &v); 

} 

當我得到了用戶的價值觀,我怎麼能執行這個階乘計算:如何計算這個階乘

n!/((n-v)! * v!)) 

我嘗試了不同的數據類型,但顯然沒有人能容納結果。

例如:n = 49,v = 6。其結果是:13,983,816,但我怎麼能得到它?

+2

請發表你如何試圖計算你的方程,而不是你如何得到參數。 – thelaws 2014-10-20 20:27:54

+1

您可以使用[GMP Bignum庫](https://gmplib.org/)等庫來表示大量數據。 – 2014-10-20 20:30:37

+1

如果您需要準確的結果,則需要使用一些擴展精度整數庫。如果不是,浮點有足夠的範圍,但結果不準確。 – 2014-10-20 20:33:36

回答

2

在評論中,你終於說過,你不需要確切的結果。

只使用浮點。您需要處理的最大中間結果是59!,大約是1.3868e80;類型double已經足夠大以保持該價值。

像寫一個函數:

double factorial(int n); 

(我想你知道如何實現它)並使用它。

如果您要做大量的這些計算,您可能需要通過將結果存儲在數組中來緩存結果。如果這樣定義的數組:

double fact[60]; 

那麼你可以的N!的價值fact[N] N個0至59存放 - 你可以填滿整個陣列中關於時間則需計算59!剛一旦。否則,您將在每次計算中進行幾十次浮點乘法和除法 - 如果您只做一次,這樣做很微不足道,但如果您這樣做,例如數千次或數百萬次,這可能很重要。

如果您需要確切的結果,您可以像其他人所建議的那樣使用擴展整數庫,如GNU MP。或者你可以使用一種內置支持任意長度整數的語言(比如Python)。

或者您可能可以按避免溢出的順序執行乘法和除法;我不知道如何做到這一點,但由於n!/((n-v)! * v!))是一個常見的公式,我強烈懷疑工作已經完成。

+0

非常感謝 – FoolishBeat 2014-10-21 02:17:56

5

最好的辦法是放棄通常基於遞歸的天真因子實現,並切換到返回伽馬函數自然對數的實現。 gamma(n) = (n-1)!

最重要的是natural log of gamma,因爲你可以重寫表達這樣的:

伽瑪功能階乘相關

ln(n!/(n-v)!v!) = ln(n!) - ln((n-v)!) - ln(v!) 

(n-v)! = gamma(n-v+1) 
n! = gamma(n+1) 
v! = gamma(v+1) 

所以

ln(n!/(n-v)!v!) = lngamma(n+1) - lngamma(n-v+1) - lngamma(v+1) 

您可以在Numerical Recipes中找到lngamma的實現。

lngamma返回一個雙精度值,所以它適用於更大的值。

不言而喻,你會拿exp()雙方得到你想要的原始表達。

+1

感謝您的幫助。我會試一試。 – FoolishBeat 2014-10-20 20:44:19

0

您不能以簡單的方式使用如59!這樣的長號碼。 然而,你可以使用特殊C庫,其與長數超過8個字節更大的工作,例如GMP

+0

還有其他方法嗎?謝謝 – FoolishBeat 2014-10-20 20:56:16

+0

另一種方式是@duffymo提到的,但它不會給你確切的數字,但只是像'1.3868311854568983573793901972039e + 80'我猜。像標準的計算器一樣。 – Seprum 2014-10-20 21:00:38

+0

我不需要確切的數字。只有15或16位小數點..這就是@@ – FoolishBeat 2014-10-20 21:12:19

3

@duffymo想法看起來太好玩忽視:使用lgamma()<math.h>

過去的結果可能是x=1e15,開始失去後面的有效數字。仍然樂於獲得1000000.0!

void factorial_expo(double x, double *significand, double *expo) { 
    double y = lgamma(x+1); 
    const static double ln10 = 2.3025850929940456840179914546844; 
    y /= ln10; 
    double ipart; 
    double fpart = modf(y, &ipart); 
    if (significand) *significand = pow(10.0, fpart); 
    if (expo) *expo = ipart; 
} 

void facttest(double x) { 
    printf("%.1f! = ", x); 
    double significand, expo; 
    factorial_expo(x, &significand, &expo); 
    int digits = expo > 15 ? 15 : expo; 
    if (digits < 1) digits++; 
    printf("%.*fe%.0f\n", digits, significand, expo); 
} 


int main(void) { 
    facttest(0.0); 
    facttest(1.0); 
    facttest(2.0); 
    facttest(6.0); 
    facttest(10.0); 
    facttest(69.0); 
    facttest(1000000.0); 
    return 0; 
} 

0.0! = 1.0e0 
1.0! = 1.0e0 
2.0! = 2.0e0 
6.0! = 7.20e2 
10.0! = 3.628800e6 
69.0! = 1.711224524281441e98 
1000000.0! = 8.263931668544735e5565708 
+0

+1只爲記住保持樂趣! – ryyker 2014-10-20 22:22:11