写一个更快的组合算法

时间:2011-12-27 23:30:58

标签: php algorithm recursion combinatorics

我正在尝试编写一个组合算法来获取k n的所有可能组合而不重复。

公式为:

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);
}

这是实现这一目标的最快方法吗?有没有办法加快速度?也许以递归方式写出来?

5 个答案:

答案 0 :(得分:6)

我认为问题是计算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;
}

答案 1 :(得分:2)

IMO不值得优化,除非由于浮点限制而使用HEAVY:170! = 7.257415615308E + 306,下一个阶乘(171!)超出浮点范围。 我猜这个递归会减慢过程(但没有经过测试)。

答案 2 :(得分:2)

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

这是错误的,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. 要慢
  3. 阶乘变得非常快,因此在不需要的地方存在溢出和不准确的风险
  4. 这些是否是实际问题取决于您的应用程序。你写道,结果最终是一个数组,大概是为了避免重新计算,所以初始计算的速度不那么重要。但是,溢出问题可能很好。为避免这些,请按Pascal的三角形choose(n+1,k) = choose(n,k) + choose(n,k-1)递归计算数组条目,其中choose(n,k) = 0如果k < 0k > n。或者,您可以为choose(n,0) = 1计算以choose(n,k) = choose(n,k-1)*(n+1-k)/k1 <= k <= n开头的每一行。两种方法都避免使用大型中间n!,从而为更广泛的数字提供准确的结果。

答案 3 :(得分:1)

这是我设法获得阶乘循环的最快速度:

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

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

答案 4 :(得分:1)

您实际上不需要计算完整的分子和分母。例如:

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

也就是说,分母中的最大因子取消了分子阶乘的最低部分。因此,例如,如果k> n / 2,你需要做的就是将数字从k + 1乘以n,再除以(n-k)!这节省了相当于计算全因子的大量工作。

这是这种方法的草案:

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);
}
相关问题