Javascript Knapsack Algorithm - find by lowest value - javascript

I have found an implementation of Knapsack problem. As you probably all know this algorithm is designed to find solution with the highest value of items that fits in backpack. I want to make it finds solution with the lowest values.
Here is the code:
var data= [
{name: 'map', weight: 9, value:150, pieces:1},
{name: 'compass', weight: 13, value: 35, pieces:1},
{name: 'water', weight:153, value:200, pieces:2},
{name: 'sandwich', weight: 50, value: 60, pieces:2},
{name: 'glucose', weight: 15, value: 60, pieces:2},
{name: 'tin', weight: 68, value: 45, pieces:3},
{name: 'banana', weight: 27, value: 60, pieces:3},
{name: 'apple', weight: 39, value: 40, pieces:3},
{name: 'cheese', weight: 23, value: 30, pieces:1},
{name: 'beer', weight: 52, value: 10, pieces:3},
{name: 'suntan, cream', weight: 11, value: 70, pieces:1},
{name: 'camera', weight: 32, value: 30, pieces:1},
{name: 'T-shirt', weight: 24, value: 15, pieces:2},
{name: 'trousers', weight: 48, value: 10, pieces:2},
{name: 'umbrella', weight: 73, value: 40, pieces:1},
{name: 'waterproof, trousers', weight: 42, value: 70, pieces:1},
{name: 'waterproof, overclothes',weight: 43, value: 75, pieces:1},
{name: 'note-case', weight: 22, value: 80, pieces:1},
{name: 'sunglasses', weight: 7, value: 20, pieces:1},
{name: 'towel', weight: 18, value: 12, pieces:2},
{name: 'socks', weight: 4, value: 50, pieces:1},
{name: 'book', weight: 30, value: 10, pieces:2}
];
function findBestPack() {
var m= [[0]]; // maximum pack value found so far
var b= [[0]]; // best combination found so far
var opts= [0]; // item index for 0 of item 0
var P= [1]; // item encoding for 0 of item 0
var choose= 0;
for (var j= 0; j<data.length; j++) {
opts[j+1]= opts[j]+data[j].pieces; // item index for 0 of item j+1
P[j+1]= P[j]*(1+data[j].pieces); // item encoding for 0 of item j+1
}
for (var j= 0; j<opts[data.length]; j++) {
m[0][j+1]= b[0][j+1]= 0; // best values and combos for empty pack: nothing
}
for (var w=1; w<=400; w++) {
m[w]= [0];
b[w]= [0];
for (var j=0; j<data.length; j++) {
var N= data[j].pieces; // how many of these can we have?
var base= opts[j]; // what is the item index for 0 of these?
for (var n= 1; n<=N; n++) {
var W= n*data[j].weight; // how much do these items weigh?
var s= w>=W ?1 :0; // can we carry this many?
var v= s*n*data[j].value; // how much are they worth?
var I= base+n; // what is the item number for this many?
var wN= w-s*W; // how much other stuff can we be carrying?
var C= n*P[j] + b[wN][base]; // encoded combination
m[w][I]= Math.max(m[w][I-1], v+m[wN][base]); // best value
choose= b[w][I]= m[w][I]>m[w][I-1] ?C :b[w][I-1];
}
}
}
var best= [];
for (var j= data.length-1; j>=0; j--) {
best[j]= Math.floor(choose/P[j]);
choose-= best[j]*P[j];
}
I have tried several things, but it didn't work for me, can you guys help me out?
Algorithm finds items that fits in container that has 400 capacity so it can contain items that has no more than 400 weight. It also try to fits items that have the best value. For example if 1 item has 10 weight and 5 value algorithm will take this item instead of item with 10 weight and 4 value. I want to make it reverse when it comes to value so algorithm will pick the item with 4 value instead of 5.

In general, the solution to the vanilla Knapsack problem for lowest values is trivial: you can simply choose to take nothing. If you're talking about lowest value for a certain weight threshold, then here's a hint: try negating all your values. In this case, the highest value knapsack is the least negative.

Related

I need to minus one key, of array of objects, from another key of another array of objects

I have next two arrays:
const firstArray = [{startDate: 5, number: 15}, {startDate: 25, number: 10}, {startDate: 26, number: 10}];
const secondArray= [{startDay: 2, endDay:10, number: 15}, {startDay: 20, endDay:30, number: 20}];
if startDate is between startDay and endDay, I have to minus firstArray number and secondArray number creating new key with result
As result, I have to put new key in firstArray with result:
const firstArray = [{startDate: 5, number: 15, result: 0}, {startDate: 25, number: 25, result: -10}, {startDate: 26, number: 25, result: 0}];
if I have more than one startDate in the same range(between startDay and endDay) I have to add to the last result of that range
The code I have for now:
firstArray.map(el => ({...el, result: el.number - here's number from the secondArray according to the requirements}))
Map won't work too well for looping through two arrays and changing values.
Just use a simple for loop, check your conditions between firstArray[i] and secondArray[i], then add the value to firstArray[i].result
const firstArray = [{startDate: 5, number: 15}, {startDate: 25, number: 20}];
const secondArray= [{startDay: 2, endDay:10, number: 10}, {startDay: 20, endDay:30, number: 20}];
for (let i = 0; i < Math.min(firstArray.length, secondArray.length); i++)
if (secondArray[i].startDay < firstArray[i].startDate && firstArray[i].startDate < secondArray[i].endDay)
firstArray[i].result = firstArray[i].number - secondArray[i].number;
console.log(firstArray);
When you are using two or more arrays, you need to know which index you are working with. Also, for best performance, avoid any kind callback functions, use simple loops instead:
const firstArray = [{startDate: 5, number: 15}, {startDate: 25, number: 10}, {startDate: 26, number: 10}];
const secondArray= [{startDay: 2, endDay:10, number: 15}, {startDay: 20, endDay:30, number: 20}];
for(let i = 0, first, second, result = 0; i < firstArray.length; i++)
{
first = firstArray[i];
second = secondArray[i];
if (second && first.startDate >= second.startDay && first.startDate <= second.endDay)
result = first.number - second.number;
else
result += first.number;
first.result = result;
}
console.log(firstArray);
Using map() would work too and producing slightly shorter code, but it's slower and very unnecessary:
const firstArray = [{startDate: 5, number: 15}, {startDate: 25, number: 10}, {startDate: 26, number: 10}];
const secondArray= [{startDay: 2, endDay:10, number: 15}, {startDay: 20, endDay:30, number: 20}];
firstArray.map((first, i) =>
{
const second = secondArray[i];
if (second && first.startDate >= second.startDay && first.startDate <= second.endDay)
first.result = first.number - second.number;
else
first.result = ((firstArray[i-1]||{}).result || 0) + first.number;
});
console.log(firstArray);

nodejs/JS: Objects not being updated in array [duplicate]

This question already has answers here:
Push object in Javascript
(3 answers)
Closed 2 years ago.
Firstly, I am a complete noob to Javascript and Node.js.
I tried to write a simple program that creates an array of planes that all have the same values except in index, which should take on the value of each index in the for loop from 0 - 2.
Here is my code
var plane = {
index: -1,
name: "A380",
seats: {
first: 40,
buisness: 90,
economy: 300
},
wheels: 8,
}
var planeArray = []
for (var i = 0; i < 3; i++) {
plane.index = i
planeArray.push(plane)
}
console.log(planeArray)
But when I print the output, the indices of all the planes are 2. Here is my output.
[
{
index: 2,
name: 'A380',
seats: { first: 40, buisness: 90, economy: 300 },
wheels: 8
},
{
index: 2,
name: 'A380',
seats: { first: 40, buisness: 90, economy: 300 },
wheels: 8
},
{
index: 2,
name: 'A380',
seats: { first: 40, buisness: 90, economy: 300 },
wheels: 8
}
]
Here is my expected output.
[
{
index: 0,
name: 'A380',
seats: { first: 40, buisness: 90, economy: 300 },
wheels: 8
},
{
index: 1,
name: 'A380',
seats: { first: 40, buisness: 90, economy: 300 },
wheels: 8
},
{
index: 2,
name: 'A380',
seats: { first: 40, buisness: 90, economy: 300 },
wheels: 8
}
]
I don't understand why.
Could someone help me out. Also, any additional explanations/resources helpful for learning node.js would be greatly appreciated
The problem here is that the object being added in the array goes as a reference and not as the value of the object
you can either try either of the below two solutions.
This way you pass the object value instead of its reference.
for (var i = 0; i < 3; i++) {
plane.index = i
planeArray.push({...plane})
}
for (var i = 0; i < 3; i++) {
plane.index = i
planeArray.push(JSON.parse(JSON.stringify(plane)))
}
You are always changing the same object with plane.index = i. If you want to have three different entries in your array you need three different objects.

Convert an Array of Array to Array of Object [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
I have an array of arrays (2d array), and I want to convert it into an array of objects. In the resulting array, I would like each object to have a key-name (see expected output below)
data = [
['Apple', 'Orange', 'Banana', 'Pears', 'Peach'],
[40, 35, 25, 58, 84],
[2, 4, 4, 3, 1, 2],
['Red', 'Yellow', 'Green', 'Violet', 'Blue']
];
My expected result:
expected_result = [
{name: 'Apple', price: 40, quantity: 2, color: 'Red'},
{name: 'Orange', price: 35, quantity: 4, color: 'Yellow'},
{name: 'Banana', price: 25, quantity: 4, color: 'Green'},
{name: 'Pears', price: 58, quantity: 1, color: 'Violet'},
{name: 'Peach', price: 84, quantity: 2, color: 'Blue'}
];
Note The iteration of each array (in data) should be consecutive so that it gives the expected result
Seems to be there is one item extra in the quantity values. I have updated
[2, 4, 4, 3, 1, 2] to [2, 4, 4, 1, 2] removed 3 to match the result, hoping its a typo.
let data = [
["Apple", "Orange", "Banana", "Pears", "Peach"],
[40, 35, 25, 58, 84],
[2, 4, 4, 1, 2],
["Red", "Yellow", "Green", "Violet", "Blue"]
];
let output = [];
let props = ["name", "price", "quantity", "color"];
function updateInfo(row, prop){
row.filter((value, index) => {
if (output[index]) {
output[index][prop] = value;
} else {
output.push({
[prop]: value
});
}
});
};
data.filter((row, index) => {
updateInfo(row, props[index]);
});
console.log(output);
One solution consist of creating first a Map between the outter-array indexes and the property (or key) name you want to assign to them (however this could be replaced by an array like this ["name","price","quantity","color"]). Also, you can obtain the minimun length of the inner arrays to later check for non-creation of objects that won't have all the properties. After you do this pre-initialization, you can use Array.reduce() to generate your expected result:
const data = [
['Apple', 'Orange', 'Banana', 'Pears', 'Peach'],
[40, 35, 25, 58, 84],
[2, 4, 4, 3, 1, 2],
['Red', 'Yellow', 'Green', 'Violet', 'Blue']
];
let mapIdxToProp = new Map([[0, "name"],[1, "price"],[2, "quantity"],[3, "color"]]);
let minLen = Math.min(...data.map(x => x.length));
let res = data.reduce((acc, arr, idx) =>
{
arr.forEach((x, j) =>
{
(j < minLen) && (acc[j] = acc[j] || {}, acc[j][mapIdxToProp.get(idx)] = x);
});
return acc;
}, []);
console.log(res);
The simplest (not most efficient) solution in my opinion is to simply loop through the array, adding to another array as you go.
let arr = [
['Apple', 'Orange', 'Banana', 'Pears', 'Peach'],
[40, 35, 25, 58, 84],
[2, 4, 4, 1, 2],
['Red', 'Yellow', 'Green', 'Violet', 'Blue']
];
let keys = ["name", "price", "quantity", "color"];
let output = [];
//Add's blank objects too the output array
for (let i = 0; i < arr[0].length; i++) {
output.push({});
}
//Loops through the array
for (let i = 0; i < arr.length; i++) {
//Loops through the sub array
for (let x = 0; x < arr[i].length; x++) {
//Adds the sub array to the key that corresponds to it
output[x][keys[i]] = arr[i][x];
}
}
console.log(output);

Need help in getting as much unique beer as possible

Let's say I have a bar and cars stop by to pick up beer(s) before heading to the beach. Each car has a trunk size (remainingSum) and each beer has a size (beer.size)
I would like to provide customers with the beer combination choices (AllCombinations) that their car trunk can fit, but unique combinations.
For example, Input:
let Beers = [
{id: 1, size: 4},
{id: 5, size: 1},
{id: 10, size: 0.5},
{id: 11, size: 1},
{id: 12, size: 2},
{id: 13, size: 1},
];
let TrunkSize = 2;
Expected Output
AllCombinations = [ // no duplicates
[{id: 5, size: 1}, {id: 10, size: 0.5}],
[{id: 5, size: 1}, {id: 11, size: 1}],
[{id: 5, size: 1}, {id: 13, size: 1}],
[{id: 10, size: 0.5}, {id: 11, size: 1}],
[{id: 10, size: 0.5}, {id: 13, size: 1}],
[{id: 11, size: 1}, {id: 13, size: 1}],
[{id: 5, size: 1}],
[{id: 11, size: 1}],
[{id: 12, size: 2}],
[{id: 13, size: 1}],
[{id: 10, size: 0.5}],
]
Current Output
AllCombinations = [
[{id: 5, size: 1}, {id: 10, size: 0.5}], // dup a
[{id: 5, size: 1}, {id: 11, size: 1}], // dup c
[{id: 5, size: 1}, {id: 13, size: 1}], // dup d
[{id: 10, size: 0.5}, {id: 5, size: 1}], // dup a
[{id: 10, size: 0.5}, {id: 11, size: 1}], // dup b
[{id: 10, size: 0.5}, {id: 13, size: 1}], // dup e
[{id: 11, size: 1}, {id: 13, size: 1}], // dup f
[{id: 11, size: 1}, {id: 10, size: 0.5}], // dup b
[{id: 11, size: 1}, {id: 5, size: 1}], // dup c
[{id: 13, size: 1}, {id: 5, size: 1}], // dup d
[{id: 13, size: 1}, {id: 10, size: 0.5}], // dup e
[{id: 13, size: 1}, {id: 11, size: 1}], // dup f
[{id: 5, size: 1}],
[{id: 11, size: 1}],
[{id: 12, size: 2}],
[{id: 13, size: 1}],
[{id: 10, size: 0.5}]
]
Current function:
AllCombinations = [];
GetCombinations(currentCombination, beers, remainingSum)
{
if (remainingSum < 0)
return;// Sum is too large; terminate recursion
else {
if (currentCombination.length > 0)
{
currentCombination.sort();
var uniquePermutation = true;
for (var i = 0; i < this.AllCombinations.length; i++)
{
if (currentCombination.length == this.AllCombinations[i].length)
{
for (var j = 0; currentCombination[j] == this.AllCombinations[i][j] && j < this.AllCombinations[i].length; j++); // Pass
if (j == currentCombination.length) {
uniquePermutation = false;
break;
}
}
}
if (uniquePermutation)
this.AllCombinations.push(currentCombination);
}
}
for (var i = 0; i < beers.length; i++) {
var newChoices = beers.slice();
var newCombination = currentCombination.concat(newChoices.splice(i, 1));
var newRemainingSum = remainingSum - beers[i].size;
this.GetCombinations(newCombination, newChoices, newRemainingSum);
}
}
I've edited your code, fixing sort & checking with additional array & stringify:
let Beers = [
{id: 1, size: 4},
{id: 5, size: 1},
{id: 10, size: 0.5},
{id: 11, size: 1},
{id: 12, size: 2},
{id: 13, size: 1},
];
let TrunkSize = 2;
AllCombinations = [];
var combStrings = []
function GetCombinations(currentCombination, beers, remainingSum)
{
if (remainingSum < 0)
return;// Sum is too large; terminate recursion
else {
if (currentCombination.length > 0)
{
currentCombination.sort((a,b)=>{
return a.id > b.id
});
//var uniquePermutation = true;
var tmp = currentCombination.map((cc)=>{
return cc.id;
})
if (combStrings.indexOf(JSON.stringify(tmp)) == -1) {
this.AllCombinations.push(currentCombination);
var tmp = currentCombination.map((cc)=>{
return cc.id;
})
combStrings.push(JSON.stringify(tmp))
}
}
}
for (var i = 0; i < beers.length; i++) {
var newChoices = beers.slice();
var newCombination = currentCombination.concat(newChoices.splice(i, 1));
var newRemainingSum = remainingSum - beers[i].size;
this.GetCombinations(newCombination, newChoices, newRemainingSum);
}
}
GetCombinations([],Beers,TrunkSize)
console.log(AllCombinations,combStrings)
Here's another approach:
let Beers = [
{id: 1, size: 4},
{id: 5, size: 1},
{id: 10, size: 0.5},
{id: 11, size: 1},
{id: 12, size: 2},
{id: 13, size: 1},
];
let TrunkSize = 2;
// get all combinations (stolen from http://stackoverflow.com/questions/5752002/find-all-possible-subset-combos-in-an-array )
function combinations(array) {
return new Array(1 << array.length).fill().map(
(e1,i) => array.filter((e2, j) => i & 1 << j));
}
// filter them out if the summed sizes are > trunksize
var valids = combinations(Beers).filter(function(el) {
return el.reduce(function(a,b){return a+b.size;}, 0) <= TrunkSize;
});
console.log(valids);
To get all possible combinations without duplicates, you can represent your combinations with a set of N bits, where N = # of 🍺.
So you should get a table that looks like this:
000000
000001
000010
000011
000100
000101
000110
000111
...
111111
The 1 tell you which beers are part of that possible combination. Then you just sum their sizes. If you get a sum greater than trunkCapacity, abort that loop.
After the loop, check that the total size of that combination is within the limits and add it to the list of combinations.
function getCombination(beers, trunkSize) {
const beersCount = beers.length;
const combinationsCount = Math.pow(2, beersCount);
const combinations = [];
let i = 0; // Change this to 1 to remove the empty combination that will always be there.
while(i < combinationsCount) {
const binary = i.toString(2);
const bits = Array.prototype.concat.apply(Array(beersCount - binary.length).fill(0), binary.split('').map(parseInt));
const combination = [];
let bit = 0;
let total = 0;
while(bit < beersCount && total <= trunkSize) {
if (bits[bit]) {
const beer = beers[bit];
total += beer.size;
combination.push(beer);
}
++bit;
}
if (total <= trunkSize) {
combinations.push(combination)
}
++i;
}
return combinations;
}
const combinations = getCombination([
{id: 1, size: 4},
{id: 5, size: 1},
{id: 10, size: 0.5},
{id: 11, size: 1},
{id: 12, size: 2},
{id: 13, size: 1},
], 2);
console.log(JSON.stringify(combinations, null, 2));
You could get all combinations and decide which sets match the conditions.
function getCombinations(array, sum, length) {
function fork(i, t) {
var s = t.reduce((a, b) => a + b.size, 0);
if (i === array.length) {
return s <= sum && t.length <= length && result.push(t);
}
fork(i + 1, t.concat([array[i]]));
fork(i + 1, t);
}
var result = [];
fork(0, []);
return result;
}
var beers = [{ id: 1, size: 4 }, { id: 5, size: 1 }, { id: 10, size: 0.5 }, { id: 11, size: 1 }, { id: 12, size: 2 }, { id: 13, size: 1 }],
result = getCombinations(beers, 2, 2);
document.getElementById('out').appendChild(document.createTextNode(JSON.stringify(result, 0, 4)));
<pre id="out"></pre>

JavaScript filter array function

I've made a list of ships that it represented like so:
var fleet =
["RMS MARY", 2000, 15],
["TITANIC 2", 10000, 13],
["Boaty McBoatface", 2000, 18],
["Jutlandia", 1945, 10],
["Hjejlen", 250, 8]
];
I want to write a function that will filter the ships by a given capacity.
Example:
filterByCapacity(fleet,5000)
This should only return the ship Titanic 2 since it is the only ship with a capacity higher than 5000.
Any ideas on how to write such a function?
Easy :
function filterByCapacity(fleet, capacity) {
var filteredArray = [];
for (var i = 0; i < fleet.length; i++) {
// Supposing we know that the capacity is the second index in the array
if (fleet[i][1] >= capacity) // Or you can make this strictly greather than (>)
filteredArray.push(fleet[i]);
}
return (filteredArray);
}
But I suggest you use objects rather than arrays. Something more like this :
var fleet = [
{
name: "RMS MARY",
capacity: 2000,
age: 15, // I had no idea what the third index meant so I made up one
},
{
name: "TITANIC 2",
capacity: 10000,
age: 13,
},
{
name: "Boaty McBoatface",
capacity: 2000,
age: 18,
},
{
name: "Jutlandia",
capacity: 1945,
age: 10,
},
{
name: "Hjejlen",
capacity: 250,
age: 8,
}
];
That way you can make one generic function filterBy(fleet, paramName, paramValue) that could return you the ships that match the filter
First, I'd like to point out that your array was badly made. You were missing a left square bracket on the first entry ("RMS MARY".) The corrected array is below:
var fleet = [
["RMS MARY", 2000, 15],
["TITANIC 2", 10000, 13],
["Boaty McBoatface", 2000, 18],
["Jutlandia", 1945, 10],
["Hjejlen", 250, 8],
];
Second, I'd recommend that you represent the ships with objects instead of arrays, so you'd have something like this:
var fleet = {
{name: "RMS MARY", capacity: 2000, somethingElse: 15},
{name: "TITANIC 2", capacity: 10000, somethingElse: 13},
{name: "Boaty McBoatface", capacity: 2000, somethingElse: 18},
{name: "Jutlandia", capacity: 1945, somethingElse: 10},
{name: "Hjejlen", capacity: 250, somethingElse: 8},
};
Now, as for the function you want, I'll provide two, one for the original array representation of ships, and one for the object representation:
//array representation
function filterByCapacity(fleet, capacity){
return fleet.filter(function(ship){
return ship[1] >= capacity;
});
}
//object representation
function filterByCapacity(fleet, capacity){
return fleet.filter(function(ship){
return ship.capacity >= capacity;
});
}
Hope this helps.
Represent your ships as objects:
var fleet = [
{name: "RMS Mary", capacity: 2000, whatever: 15},
...
];
Then you can use filter in a way that is readable and makes sense.
function filterByCapacity(fleetArr, minCapacity) {
return fleetArr.filter(function(ship) {
return ship.capacity > minCapacity;
});
}
console.log(filterByCapacity(fleet, 5000)) // only Titanic

Categories

Resources