繁体   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