I am trying to figure out how to get a list of courses (using PHP) (the value of the course in credit hours) that combined will equal or go over (by the lowest amount if nothing equals exactly when combined) using the least possible amount of courses.
I do not need all possible combinations I just need the first exact match or first closest (while going over the input) to exact match when found.
Example:
Grabbing a list of courses from the database
[0] => Array([title] => Course A [hours] => 5)
[1] => Array([title] => Course B [hours] => 3)
[2] => Array([title] => Course C [hours] => 10)
[3] => Array([title] => Course D [hours] => 8)
[4] => Array([title] => Course E [hours] => 5)
[5] => Array([title] => Course F [hours] => 14)
Example input (total hours) and excepted output (using the array index value)
Input: 10
Output: [2] (least amount of courses and equals input)
Input: 9
Output: [2] (least amount of courses and is closest value going over since nothing else can equal it)
Input: 12
Output: [1,2] (least amount of courses and is closest value going over since nothing else can equal it)
Input: 15
Output: [0,2] (least amount of courses and equals input)
Note: When a course has the same hours as another course I would like it to use the first found result from the array, I may randomize the array to get a possible different combination if more then one would be the same end result. But I am only looking for 1 end result each time I run this.
Input: 20
Output: [0,2,4] (least amount of courses and equals input, priority goes to the total being equal and then least amount of courses)
Anyone have a clue on how something like this would be written (using PHP). I am not good with coming up with recursive functions I don't think there is anything built in that would do this but maybe someone has already done something similar to this? Any help would be greatly appreciated.
UPDATE
I am using the subset_sum function posted here https://stackoverflow.com/a/43351998/1385783
So far this seems to be doing pretty well but always open to suggestions. Still testing out different situations. It basically converts the database results into a simple array then finds the hours that sum up to equal the input total or I iterate and try a higher number one by one till 5 over the starting number. So that takes care or prioritizing exact matches then closest match going over. Once I get the hours that sum up I don't have an association with what database record they were so I go back through the list and match up hours from the database records with the hours that made the cut. I have a shuffle method that will mix up the courses that have the same hours if needed, otherwise it always picks the first course from the database records that match the hours. If you notice any glaring issues please let me know. Suggestions to make it better always appreciated.
/*$courses holds the database return that looks similar to
[0] => Array([title] => Course A [hours] => 5)
[1] => Array([title] => Course B [hours] => 3)
[2] => Array([title] => Course C [hours] => 10)
[3] => Array([title] => Course D [hours] => 8)
[4] => Array([title] => Course E [hours] => 5)
[5] => Array([title] => Course F [hours] => 14)*/
$courses = $result->result_array();
$n=[];
foreach($courses as $course)
{
$n[] = $course['hours'];
}
// this will look for an exact match and if not found will add 1 hour at a time up to 5 extra hours
for($x=0;$x<5;$x++)
{
$path = subset_sum($n, $hours_needed+$x);
if(!empty($path))
break;
}
// how many hours is it matching
echo 'Hours:'.($hours_needed+$x);
if(!empty($path))
{
// grab one path
$best_path = $path[0];
// now we need to pick one of these hour paths and go back through the course list and pick out courses that match the hours
$course_path = [];
// do we want to randomize possible results with courses that have the same hours?
//shuffle($courses);
foreach($courses as $course)
{
$found = array_search($course['hours'], $best_path);
if($found !== false)
{
$course_path[] = $course;
// now remove this match from the path so it doesn't get used again
unset($best_path[$found]);
}
}
dd($course_path);
} else
{
echo 'No path found';
}