簡體   English   中英

寫一個更快的組合算法

[英]Write a faster combinatorics algorithm

我正在嘗試編寫一個組合算法,以獲得nk所有可能組合而不重復。

公式是:

n!/(k!(n-k)!)); 

結果以數組結尾。 我實際寫的是這樣的:

function Factorial($x)
{
    if ($x < 1)
    {
        echo "Factorial() Error: Number too small!";
    )

    $ans = 1;
    for ($xx = 2; $xx >= $x; $xx++)
    {
        $ans = $ans * $xx;
    }

    return($ans);
}

function Combination($selectcount,$availablecount)
{
    $ans = Factorial($availablecount) / (
        Factorial($availablecount - $selectcount) * Factorial($selectcount)
    );

    return ($ans);
}

這是實現這一目標的最快方法嗎? 有沒有辦法加快速度? 也許以遞歸方式寫出來?

我認為問題是計算C(n,k),這可以在不計算階乘的情況下完成,訣竅是首先要注意

C(n,k) = (n*(n-1)...(n-k+1)) / (1*2*...*k) = (n/1)*(n-1/2)*...(n-k+1/k)

也為了效率

C(n,k) = C(n,n-k), therefore choose which ever is smaller k or n-k

如果有錯誤,請隨意編輯,因為我已經從C轉換它,我不知道PHP

function nCk($n, $k)
{
    if( $n-$k<$k )
        $k = $n-$k;
    $ans = 1;
    for( $i=1; $i<=$k; ++$i )
    {
        $ans = ($ans*($n-$i+1))/$i;
    }
    return $ans;
}

IMO不值得優化,除非由於浮點限制而使用HEAVY:170! = 7.257415615308E + 306,下一個階乘(171!)超出浮點范圍。 我猜這個遞歸會減慢進程(但沒有測試過)。

function Factorial($x)
{
    if ($x < 1)
    {
        echo "Factorial() Error: Number too small!";
    }

那是錯的, 0! = 1 定義了0! = 1 ,因此測試應該是$x < 0

    $ans = 1;
    for ($xx = 2; $xx >= $x; $xx++)

你錯了條件,它必須是$xx <= $x

function Combination($selectcount,$availablecount)
{
    $ans = Factorial($availablecount) / (
        Factorial($availablecount - $selectcount) * Factorial($selectcount)
    );

    return ($ans);
}

你有兩個潛在的問題,

  1. 調用Factorial函數比在這里計算組合計數的循環要慢
  2. 階乘變得非常快,因此在不需要的地方冒險溢出和不准確

這些是否是實際問題取決於您的應用。 你寫道,結果最終是一個數組,大概是為了避免重新計算,所以初始計算的速度不那么重要。 但是,溢出問題可能很好。 為了避免這些,按照Pascal三角形遞歸計算數組條目, choose(n+1,k) = choose(n,k) + choose(n,k-1) ,其中如果k < 0 choose(n,k) = 0 k < 0k > n 或者,您可以計算以choose(n,0) = 1開頭的每一行choose(n,0) = 1choose(n,k) = choose(n,k-1)*(n+1-k)/k1 <= k <= n 兩種方法都避免了大的中間n! 從而為更廣泛的數字提供准確的結果。

這是我設法獲得階乘循環的最快速度:

function Factorial($factVal) {
    if ($factVal < 0) {
        die("Factorial() Error: Number too small!");
    }

    $factorial = 1;
    while ($factVal > 1) {
        $factorial *= $factVal--;
    }
    return $factorial ;
}

您實際上不需要計算完整的分子和分母。 例如:

C(7,2) = 7! / (2! * (7-2)!) = 7! / (2! * 5!) = (7 * 6) / (2 * 1)

也就是說,分母中的最大因子取消了分子的階乘的最低部分。 所以,例如,如果k> n / 2,你需要做的就是將數字從k + 1乘以n然后除以(nk)! 這節省了相當於計算全因子的大量工作。

這是這種方法的草案:

function Combination($selectcount,$availablecount)
{
    $remainder = $availablecount - $selectcount;
    if ($remainder > $selectcount) {
        $tmp = $remainder;
        $remainder = $selectcount;
        $selectcount = $tmp;
    }
    $ans = 1;
    while ($availablecount > $selectcount) {
        $ans *= $availablecount;
        $availablecount--;
    }
    while ($remainder > 1) {
        $ans /= $remainder;
        $remainder--;
    }

    return ($ans);
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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