题目:
题库中有N道题,每道题都有一个分数,我们传入一个指定分数和指定题目数量,程序返回指定数量总分为指定分数的数组
例子:传入总分100,题目数量10,题库中返回10道题,其总分相加为100
解题思路
1.获得分数的一维数组,然后获得总分,通过函数sel_set计算出所有可能出现的组合,将组合数目和指定题数相同的返回
2.判断返回是否为空,为空则无相应题目,不为空则查出所有分数在返回的分数数组中数据
3.首先循环查出来的所有数据random_data ,然后循环分数数组score_array ,如果查出来的所有数据中有分数和分数数组中分数相同则保存在return中,然后销毁这个分数数组中的分数,然后将data的key保存在del中,下一次循环时先判断这个key是否存储过,没有存储过才继续保存到return中
4.如果存储的数组个数和分数数组中的数组个数相同则返回数据
public function test_sum(){
// 取出分数
$data=DB::table('table')->whereNotNull('score')->get()->toArray();
$data = array_column($data,'score');
// 取出考试设置
$exam_set = DB::table('table')->first();
$total_grade =$exam_set->total_grade;
$exam_number = $exam_set->exam_number;
// 排列组合
$score_array = $this->sel_set($total_grade,$data,$exam_number);
if (empty($score_array)){
print_r('当前题库无匹配!');
return "当前题库无匹配";
}
// 方法1 15道题 算法耗时:100 ms 内存:memory usage: 21.56 M
$random_data = table::whereIn('score',$score_array)->inRandomOrder()->get()->toArray();
$return = [];
$del = [];
$r_data_count = count($score_array);
foreach ($random_data as $key=>$val){
foreach ($score_array as $k => $v){
if ($val['score'] == $v && !in_array($key,$del)){
$return[] = $val;
unset($score_array[$k]);
$del[] = $key;
}
}
if (count($return) == $r_data_count){
break;
}
}
}
public function sel_set($need, $arr,$exam_number) {
//子集数2的数组元素数次方
$arr_count =count($arr);
// 修改
$return_list = [];
$set_count = pow(2, $arr_count);
//set_arr用来存放符合需求的子集
$set_arr = array();
//set_count个子集,所以循环set_count次
for( $i = 1; $i < $set_count; $i++ ) {
//tmp用来存放每次子集
$tmp = array();
//将子集对应编号转化二进制
$dec = decbin($i);
//数组集合有arr_count个元素,所以将二进制左补0为对应位,以便取数组元素
$dec = str_pad($dec, $arr_count, 0, STR_PAD_LEFT);
//对该二进制数循环 判断是否为1
for( $j = 0; $j < $arr_count; $j++ ) {
//如果当前位为1, 则将数组对应元素放入子集数组
if( 1 == $dec[$j] ) {
array_push($tmp, $arr[$j]);
}
}
//判断当前子集之和是否等于设定的定值,符合则存入set_arr
if( $need == array_sum($tmp) ) {
if (count($tmp) == $exam_number){
return $tmp;
}
}
}
//返回符合要求的集合或空集合
return $set_arr;
}



