对于5个数字的集合[1,2,3,4,5],从中取出3个,不分先后,共有多少种取法?
在解决多规格商品查询的问题时,这个算法是可行的解决方案之一
分析
3取2 = 3组
4取2 = 6组
5取2 = 10组
4取3 = 4组
5取3 = 10组
6取3 = 20组
从简单开始
function one($count){
for ($i=1;$i<=$count;$i++){
$rst[] = [$i];
}
return $rst;
}
//运行one(3)输出 [[1],[2],[3]]
n取2
function two($count){
$rst = one($count - 1);//用到前面 n 取1的结果
foreach($rst as $v){
for ($i=$v[0]+1;$i<=$count;$i++){
$v[1] = $i;
$result[] = $v;
}
}
return $result;
}
//运行 two(4) 输出[[1,2],[1,3],[1,4],[2,3],[2,4],[3,4]]
n取3
function three($count){
$rst = two($count - 1);//用到前面 n 取2的结果
foreach($rst as $v){
for ($i=$v[1]+1;$i<=$count;$i++){
$v[2] = $i;
$result[] = $v;
}
}
return $result;
}
//运行 three(5) 输出的结果和人为穷举相同
通过递归把这三种情况扩展
-
我们必须也用变量代表要取出的数量,也就是 n 取 m,所以输入的参数就是 n 和 m
-
我们要以 p 取1的结果作为基础推出上层结果,所以递归深入的边际条件就是,m-递归次数=1
-
我们每次向上层返回的结果,取出的个数并不固定,所以也必须用变量表示下标,以此来组合新的结果(这是一个观察结论)
最终的递归函数
function getAnswer($amount, $need){
if($need == 1){
for ($i=1;$i<=$amount;$i++){
$rst[] = [$i];
}
return $rst;
}else{
$rst = getAnswer($amount-1, $need-1);
foreach($rst as $v){
for ($i=$v[$need-2]+1;$i<=$amount;$i++){
$v[$need-1] = $i;
$result[] = $v;
}
}
return $result;
}
}
//测试了几种常见情况,返回结果均正确
最终的应用层
-
等待穷举的数组
-
需要取出的个数
function getFinallyAnswer($array, $pick){
$amount = count($array);
$sub = getAnswer($amount, $pick);
foreach ($sub as $k => $v){
foreach ($v as $per_sub){
$rst[$k][] = $array[$per_sub - 1];
}
}
return $rst;
}
//getFinallyAnswer([7,8,9,10], 3)
//我们可以尝试一下从[7,8,9,10]中任取3个,结果完全正确