I am trying to write an algorithm that will 'fill' a vehicle to it's capacity based on different input combinations. The problem is solved, but it is far too slow to be usable for a greater amount of combinations.
For example: I have a vehicle with a capacity of 10, I have different combinations of types(attributes) of seats that can fill the vehicle differently(ambulatory, or normal seat capacity of 1 is default). Also, each combination that is not equal to 10(greater than 10 is removed) will be filled in with ambulatory capacity, or just a normal seat. Like so:
const a = [ {
name: 'wheelchair',
capacity: 0,
total: 3
}, {
name: 'walker',
capacity: 2,
total: 5
}, {
name: 'service animal',
capacity: 2,
total: 5
}];
It is also worth noting in the example above that the wheelchair can only be added 3 times per combination because it has a total of 3(max). The 0 capacity for the wheelchair is an example of a designated spot for this particular attribute that doesn't take up any of the other ambulatory seats.
I have tried a few different approaches for this and my algorithm works fine for this particular combination, or even if I add a few more. But, if I add an attribute with a total of 10 and capacity of one, this will increase the total possibilities by an order of magnitude and slow down the algorithm immensely. In my approach I am finding the different permutations and then filtering out the duplicates to find the combinations, if there were a way to find only the combinations, perhaps it would reduce the calculation load, but I can't think of a way. I have a specific way the output needs to look, which is the output at the bottom however, I have control over the input and can change if necessary. Any ideas or help is greatly appreciated.
This code was modified from this answer https://stackoverflow.com/a/21640840/6025994
// the power set of [] is [[]]
if(arr.length === 0) {
return [[]];
}
// remove and remember the last element of the array
var lastElement = arr.pop();
// take the powerset of the rest of the array
var restPowerset = powerSet(arr);
// for each set in the power set of arr minus its last element,
// include that set in the powerset of arr both with and without
// the last element of arr
var powerset = [];
for(var i = 0, len = restPowerset.length; i < len; i++) {
var set = restPowerset[i];
// without last element
powerset.push(set);
// with last element
set = set.slice(); // create a new array that's a copy of set
set.push(lastElement);
powerset.push(set);
}
return powerset;
};
var subsetsLessThan = function (arr, number) {
// all subsets of arr
var powerset = powerSet(arr);
// subsets summing less than or equal to number
var subsets = new Set();
for(var i = 0, len = powerset.length; i < len; i++) {
var subset = powerset[i];
var sum = 0;
const newObject = {};
for(var j = 0, len2 = subset.length; j < len2; j++) {
if (newObject[subset[j].name]) {
newObject[subset[j].name]++;
} else {
newObject[subset[j].name] = 1;
}
sum += subset[j].seat;
}
const difference = number - sum;
newObject.ambulatory = difference;
if(sum <= number) {
subsets.add(JSON.stringify(newObject));
}
}
return [...subsets].map(subset => JSON.parse(subset));
};
const a = [{
name: 'grocery',
capacity: 2,
total: 5
}, {
name: 'wheelchair',
capacity: 0,
total: 3
}];
const hrStart = process.hrtime();
const array = [];
for (let i = 0, len = a.length; i < len; i++) {
for (let tot = 0, len2 = a[i].total; tot < len2; tot++) {
array.push({
name: a[i].name,
seat: a[i].capacity
});
}
}
const combinations = subsetsLessThan(array, 10);
const hrEnd = process.hrtime(hrStart);
// for (const combination of combinations) {
// console.log(combination);
// }
console.info('Execution time (hr): %ds %dms', hrEnd[0], hrEnd[1] / 1000000)
Expected results are all combinations of results passed in to be less than vehicle capacity, so it is a combination less than sum algorithm essentially. For instance, the expected result of the code I have posted would be -->
[{"ambulatory":10},{"wheelchair":1,"ambulatory":10},{"wheelchair":2,"ambulatory":10},{"wheelchair":3,"ambulatory":10},{"grocery":1,"ambulatory":8},{"grocery":1,"wheelchair":1,"ambulatory":8},{"grocery":1,"wheelchair":2,"ambulatory":8},{"grocery":1,"wheelchair":3,"ambulatory":8},{"grocery":2,"ambulatory":6},{"grocery":2,"wheelchair":1,"ambulatory":6},{"grocery":2,"wheelchair":2,"ambulatory":6},{"grocery":2,"wheelchair":3,"ambulatory":6},{"grocery":3,"ambulatory":4},{"grocery":3,"wheelchair":1,"ambulatory":4},{"grocery":3,"wheelchair":2,"ambulatory":4},{"grocery":3,"wheelchair":3,"ambulatory":4},{"grocery":4,"ambulatory":2},{"grocery":4,"wheelchair":1,"ambulatory":2},{"grocery":4,"wheelchair":2,"ambulatory":2},{"grocery":4,"wheelchair":3,"ambulatory":2},{"grocery":5,"ambulatory":0},{"grocery":5,"wheelchair":1,"ambulatory":0},{"grocery":5,"wheelchair":2,"ambulatory":0},{"grocery":5,"wheelchair":3,"ambulatory":0}]