一、递归
//从$input数组中取$m个数的组合算法
function comb($input, $m)
{
if ($m == 1) {
foreach ($input as $item) {
$result[] = array($item);
}
return $result;
}
for ($i = 0; $i <= count($input) - $m; $i++) {
$nextinput = array_slice($input, $i + 1);
$nextresult = comb($nextinput, $m - 1);
foreach ($nextresult as $one) {
$result[] = array_merge(array($input[$i]), $one);
}
}
return $result;
}
$input = range(1, 5);
print_r(comb($input, 2));
二、建模
/**
* [combinations optimize based on the combination()]
* @param int $length [一共要在多少个元素里选择]
* @param int $selectMany [选择多少个]
* @return [mixed] [返回组合数形如[0,1,2,3]的结果集]
*/
function combinations($length, $selectMany) {
if ($selectMany < 2 || $length < $selectMany)
return false;
//根据第5条,如果组合结果集不要求排序,则构建形如[3,0][4,0]的反向准二阶模型,以便优化
$step = $selectMany - 1; //区间内最大两个数的最小差
$maxOfStart = $step - 1;
$combinations = []; //组合数数组
//构建仅仅包含两个element的准二阶模型
for ($i = $length - 1, $count = 0; $i > $maxOfStart; --$i) {
for ($j = $i - $step; $j >= 0; --$j) {
$combinations[] = [$i, $j]; //保存组合数
++$count; //计算二阶模型的组合数总数
}
}
// //当然你也可以使用以下方式构建如[0,3][0,4]的正向二阶模型
// $step = $selectMany-1; //区间内最大两个数的最小差
// $maxOfStart = $length-$step;
// $combinations = []; //组合数数组
// //构建形如[0,3][0,4]...的二阶模型。
// for ($i=0,$count=0; $i < $maxOfStart ; ++$i) {
// for ($j=$step+$i; $j < $length; ++$j) {
// $combinations[] = [$i,$j];
// ++$count;
// }
// }
// get the all combinations;将二阶模型扩展成k阶模型,直至m阶模型,
for ($i = $step - 1, $endIndex = 1; $i > 0; --$i, ++$endIndex) { //$endIndex表示当前k阶模型的最后一个索引
$tempCombinations = []; //开一个临时组合数空间
//遍历所有k阶模型并递推构建k+1阶模型
for ($k = 0, $nums = 0; $k < $count; ++$k) {
//交换准k阶模型最后两个元素值,使其满足k阶模型的定义
$tmp = $combinations[$k][$endIndex];
$combinations[$k][$endIndex] = $combinations[$k][$endIndex - 1];
$combinations[$k][$endIndex - 1] = $tmp;
//获取k阶模型的最大值和第二最大值
$max = $combinations[$k][$endIndex];
$secMax = $combinations[$k][$endIndex - 1];
//构建并保存k+1阶模型
for ($g = $secMax + 1; $g < $max; ++$g) {
if ($max - $g >= $i) { //$i表示Ck+1(即k+1阶模型的可再容量)
$tmp = $combinations[$k];
$tmp[] = $g;
$tempCombinations[] = $tmp;
++$nums;
}
}
}
//更新K+1阶组合数的结果集信息,进入下一个递推阶段
$combinations = $tempCombinations;
$count = $nums;
}
return $combinations; //返回组合数结果集
}
var_dump(combinations(5, 2));
三、位移法
/*
二进制组合算法:
思路是开一个数组,其下标表示1到m个数,数组元素的值为1表示其下标
代表的数被选中,为0则没选中。
首先初始化,将数组前n个元素置1,表示第一个组合为前n个数。
然后从左到右扫描数组元素值的“10”组合,找到第一个“10”组合后将其变为
“01”组合,同时将其左边的所有“1”全部移动到数组的最左端。
当第一个“1”移动到数组的m-n的位置,即n个“1”全部移动到最右端时,就得
到了最后一个组合。
例如求5中选3的组合:
1 1 1 0 0 //1,2,3
1 1 0 1 0 //1,2,4
1 0 1 1 0 //1,3,4
0 1 1 1 0 //2,3,4
1 1 0 0 1 //1,2,5
1 0 1 0 1 //1,3,5
0 1 1 0 1 //2,3,5
1 0 0 1 1 //1,4,5
0 1 0 1 1 //2,4,5
0 0 1 1 1 //3,4,5
*/
//select n from m
function zh($m, $n) {
$result = [];
$arr = [];
for($i=0; $i<$m; $i++) {
$arr[$i] = ($i<$n) ? 1 : 0;
}
$kk = 1;
$result[] = $arr;
$num = 0;//统计1的个数
for($i=0; $i<$m-1;) {
if($arr[$i] == 1 && $arr[$i+1] == 0) {
$arr[$i] = 0;
$arr[$i+1] = 1;
//把左边所有的1都放到最左边
for($j=0; $j<$i; $j++) {
$arr[$j] = ($j<$num) ? 1 : 0;
}
$i = 0;
$num = 0;
$result[] = $arr;
$kk++;
} else {
if(1 == $arr[$i]) {
$num++;
}
$i++;
}
}
// return $kk;
return $result;
}
var_dump(zh(5,2));