簡體   English   中英

計算具有大量行和奇偶校驗的帕斯卡三角形

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM