[英]Calculating pascals triangle with large number of rows and parity
我正在使用一個簡單的遞歸函數打印出帕斯卡三角形,但是程序的復雜性似乎是指數級的(可能是錯誤的嗎?),並且我想打印大量的帕斯卡三角形行(超過50行),但是這樣做我目前的功能似乎不太可能,因為它開始阻塞約30多個行。
我還想計算條目的奇偶校驗並打印它們的奇偶校驗而不是數字,所以我不確定是否丟失了某些東西,也不需要計算條目的整個值來找到它的奇偶校驗,所以我可以使用規則?
public static long pascalsTriangle(int row, int col)
{
if(row == 0 || col == 0 || col == row) return 1;
else if(col == 1 || col == row-1) return row;
else return pascalsTriangle(row - 1, col -1) + pascalsTriangle(row - 1, col);
}
public static void main(String[] args)
{
int num = 40;
for(int row = 0; row <= num; row++)
{
for(int space = num; space > row; space--) System.out.printf(" ");
for(int col = 0; col <= row; col++)
{
String str_n = (pascalsTriangle(row, col) % 3 == 0)? "odd " : "even";
System.out.printf("%6s", str_n);
}
System.out.println();
}
}
代碼的問題在於您正在執行大量冗余計算。 要計算40個選擇20,您需要計算39個選擇19和39個選擇20,這涉及兩次計算38個選擇19。 您多次計算較低的值以產生每個輸出。 您的代碼大約需要n步才能產生n值,而Pascal三角形的第k行中的條目總計為2 ^ k,因此您嘗試執行2 ^ 40〜10 ^ 12步。
相反,您可以將計算出的值存儲在二維數組中。 這稱為記憶或動態編程。 要計算選擇b,這大約需要(b + 1)(a-b + 1)個步驟,比選擇b的步驟要快得多,但是當您計算多個條目時,甚至這些步驟中的大多數都已保存。 為了減小遞歸深度,即使您只想要一個值,也可能要按順序從0到40行計算行。
由於條目變大,您可能會遇到一些溢出問題。 使用java.math.BigInteger變量而不是longs。 當您發現第67行及以后的行中的條目大於2 ^ 63時,這變得很有必要。
要計算奇偶校驗,只需跟蹤較早值的奇偶校驗即可。
有更快的方法來計算帕斯卡三角形的輸入和奇偶校驗。 例如,a選擇b是a!/(b!(ab)!),您可以沿行更好地遞歸計算(a選擇0)= 1,a選擇b =(a選擇min(b,ab) ),以及(a選擇b)=(a選擇(b-1))*(a-b + 1)/ b。 有更多更快的方法來確定奇偶校驗 ,但是您應該首先查看模式。
這是一個選擇b的Java記憶版本:
import java.math.*;
public static class Binomial
{
private static BigInteger[][] memoized = new BigInteger[1001][1001];
public static BigInteger comb(int n, int k)
{
if (k>n || n<0 || k<0)
return BigInteger.valueOf(0);
if (k > n-k)
return comb(n,n-k);
if (n>1000 || k>1000) // we could expand the memoized array dynamically
return BigInteger.valueOf(-1);
if (memoized[n][k] != null)
return memoized[n][k];
// we got here because we haven't computed it yet
BigInteger result;
if (n==0 && k==0)
result = BigInteger.valueOf(1);
else
result= comb(n-1,k-1).add(comb(n-1,k));
memoized[n][k] = result;
return result;
}
}
每次計算值時,都會重新計算該值所需的每個父級,一直返回到根。 您只需要計算一行的數據就是上一行的數據。 另外,如果只需要奇偶校驗,則只需要存儲每一行的偶/奇值。 因此,除非您真的要使用遞歸,否則我的解決方案將基於以下內容:
public static int[] calcNextRow(int[] prevRow) {
int[] newRow = new int[prevRow.length + 1];
newRow[0] = 1;
for (int i = 1; i < prevRow.length; ++i) {
newRow[i] = (prevRow[i-1] + prevRow[i]) % 2;
}
newRow[prevRow.length] = 1;
return newRow;
}
然后,您可以遞歸的方式使用它,或者直接從循環中調用它:
public static void main() {
int[] prevRow = { 1 };
printRow(prevRow);
for (int row = 0; row < 10; ++row) {
int[] nextRow = calcNextRow(prevRow);
printRow(nextRow);
prevRow = nextRow;
}
}
如果要計算實際值,則只需除去% 2
:
newRow[i] = prevRow[i-1] + prevRow[i];
雖然顯然您需要將int
換成long
或將BigInteger
換成更大的樹。
干杯,
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.