我正在尝试编写一个组合算法来获取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);
}
这是实现这一目标的最快方法吗?有没有办法加快速度?也许以递归方式写出来?
答案 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);
}
这里有两个潜在的问题,
Factorial
函数比循环计算组合计数这些是否是实际问题取决于您的应用程序。你写道,结果最终是一个数组,大概是为了避免重新计算,所以初始计算的速度不那么重要。但是,溢出问题可能很好。为避免这些,请按Pascal的三角形choose(n+1,k) = choose(n,k) + choose(n,k-1)
递归计算数组条目,其中choose(n,k) = 0
如果k < 0
或k > n
。或者,您可以为choose(n,0) = 1
计算以choose(n,k) = choose(n,k-1)*(n+1-k)/k
和1 <= 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);
}