php中从N中取M个数的组合

一、递归

//从$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));

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值