Distribute items evenly but add remainder to first - javascript

The snippet below pretty much says it all, but in short I need to distribute a certain amount of months equally over activities. Since there is always a chance to deal with a remainder these should be added to the first month.
const selectedMonth = 5
const project = {
duration: 2, // in months
activities: [{
number: 1,
title: 'game 1'
},
{
number: 2,
title: 'game 2'
},
{
number: 3,
title: 'game 3'
},
]
}
// 1 Add a "plannedInMonth" property to each activity
// 2 Start planning from the selected month and onwards (the month number can be > 12)
// 3 Spread the activities evenly based on the duration
function planActivitiesInMonths() {}
planActivitiesInMonths()
// So this function should, since the remainder of 3 / 2 = 1, return as follows:
activities: [{
number: 1,
title: 'game 1',
plannedInMonth: 5
},
{
number: 2,
title: 'game 2',
plannedInMonth: 5
},
{
number: 3,
title: 'game 3',
plannedInMonth: 6
},
]
// However, it should also work when e.g. 24 activities need to be distributed across 5 months

If you're just looking to copy paste an implementation of the algorithm, this should do it:
function planActivitiesInMonths(project, selectedMonth) {
const remainder = project.activities.length % project.duration
const activitesPerMonth = Math.floor(project.activities.length / project.duration)
return project.activities.map((activity, i) => {
let index = Math.floor((i - remainder) / activitesPerMonth)
if (index < 0) {
index = 0
}
activity.plannedInMonth = index + selectedMonth
return activity
})
}
Just keep in mind that my function returns a value and doesn't mutate directly the object.
I am shifting the index by the remainder to have to be able to nicely handle the fact that the remainder activities should be added to the first month, but there are a tons of ways of implementing this algorithm.
However, this algorithm has a strange behaviour if the project duration is slightly below a multiple of the activities per month. In this case, the remainder would be very big and a lot of activities would be added to the first month.
For example, if you want to distribute 9 activities across 5 months, the remainder would be 5 % 9 = 4, so the first month would have a total of 5 activities!
Maybe it's better to evenly distribute the remainder too. And this algorithm has a cleaner and simpler implementation:
function planActivitiesInMonths(project, selectedMonth) {
const activitesPerMonth = project.activities.length / project.duration
return project.activities.map((activity, i) => {
const index = Math.floor(i / project.activities.length * activitesPerMonth)
activity.plannedInMonth = index + selectedMonth
return activity
})
}

Related

Calculate best fit from number input

I'm currently trying to figure out the algorithm to calculate the best fit.
What is the problem:
I have many types of bowls (5-15 types). Each type holds a minimum amount and a maximum amount of food (per person). So as an example I have five bowls:
A: holds between 3 and 5 persons worth of food.
B: holds between 4 to 6 persons worth of food.
C: holds between 5 and 10 persons worth of food.
D: holds between 10 and 15 persons worth of food.
E: holds between 15 and 20 persons worth of food.
The rules are:
A bowl is always filled up with food until the minimum amount or the maximum amount.
Prevent giving away free food or having waste as much as possible.
What I want to do:
give in an amount of people, and that the functions calculates what is the best fit of the amount of bowls that I need.
So as an example I would say that I have 12 people. In this case Bowl D is the best since only one bowl is needed.
But if I give in 36 people. I would expect that I would get that the best fitis:
1 X E: Holds up to 20 people
1 X C: Holds up to 10 people
1 X B: Holds up to 6 people
That makes a total of 36 people. If you know a better or more efficient way let me know.
How to create such a function in Javascript?
Since I'm a junior, please try to explain as much as possible.
This question is an optimization problem. The following code walk every possible solutions and use some heuristics (or deterministic function) to calculate the solution with least cost. There may be room for more optimizations, but your problem space is relatively small.
// 1. List of bowl
const bowlTypes = [
{
name: "A",
description: "holds between 3 and 5 persons worth of food",
min: 3,
max: 5
},
{
name: "B",
description: "holds between 4 to 6 persons worth of food",
min: 4,
max: 6
},
{
name: "C",
description: "holds between 5 and 10 persons worth of food",
min: 5,
max: 10
},
{
name: "D",
description: "holds between 10 and 15 persons worth of food",
min: 10,
max: 15
},
{
name: "E",
description: "holds between 15 and 20 persons worth of food",
min: 15,
max: 20
}
];
// 2. Create a cost function for the best combination of bowls
// e.g. may use sum of the bowls' costs
function getCost(bowls, surplus) {
const total = bowls.reduce((total, { min, max }) => total + ((max - min) / 2), 0);
// penalty for more bowls, heavy penalty for surplus
// adjust function to calibrate, perhaps add actual
// bowl cost to data set
return bowls.length + total + (surplus * surplus);
}
// 3. Evaluate how many bowls we need given a number of persons
function evaluatePersons(persons) {
const bowlCount = bowlTypes.length;
let bestSolution;
// recursive function walking all possible options.
const findSolution = (bowls, servings, startIndex) => {
// while we can add more bowls...
if (servings > 0) {
// try next combination
for (let bowlIndex = startIndex; bowlIndex < bowlCount; ++bowlIndex) {
const bowl = bowlTypes[bowlIndex];
findSolution([ ...bowls, bowl ], servings - bowl.max, bowlIndex);
}
// if the current solution has enough, or too many servings
} else {
// get amount of surplus
const surprlus = Math.abs(servings);
// get the cost of this solution
const cost = getCost(bowls, surprlus);
// if the current solution is better than any previous one
if (!bestSolution || (cost < bestSolution.cost)) {
bestSolution = { bowls, cost, surprlus };
}
}
};
// init first step
for (let bowlIndex = 0; bowlIndex < bowlCount; ++bowlIndex) {
findSolution([], persons, bowlIndex);
}
// optimize solution
bestSolution.bowls = Array.from(bestSolution.bowls.reduce((map, bowl) => {
if (map.has(bowl.name)) {
map.get(bowl.name).qty = map.get(bowl.name).qty + 1;
} else {
map.set(bowl.name, { ...bowl, qty:1 });
}
return map;
}, new Map()).values());
// return our best solution
return bestSolution;
}
// UI for testing purposes
const inputPersons = document.getElementById('inputPersons');
const output = document.getElementById('output');
inputPersons.addEventListener('change', () => {
const solution = evaluatePersons(inputPersons.value);
const verbatim = solution.bowls.map(bowl => `${bowl.qty} x ${bowl.name}: ${bowl.description}`).join('\n');
const debugString = JSON.stringify(solution, null, 3);
output.innerHTML = verbatim + '\n--------\n' + debugString;
});
main {
width: 100%;
display: flex;
flex-direction: column;
}
form {
flex: 0;
}
pre {
min-height: 200px;
flex: 1;
border: 1px solid black;
background-color: #e7e7e7;
padding: 10px;
}
<main>
<form>
<input type="number" id="inputPersons" />
</form>
<pre><code id="output"></code></pre>
</main>

How to return all the divisors from a big integer?

I understand the concept of how to return all the divisors from an given integer. However, when it gets to the big integers, nothing gets return:
function divisors(n,res=[]) {
for (let i = 1; i <= n; i++) !(n%i) && res.push(i);
return res;
}
console.log(divisors(4)); // [ 1, 2, 4 ]
console.log(divisors(9)); // [ 1, 3, 9 ]
console.log(divisors(12)); // [ 1, 2, 3, 4, 6, 12 ]
console.log(divisors(975179493674)); // ?????
console.log(divisors(27550356289)); // ?????
The next logical step is to minimize the iteration amount by taking the square root of the given integer n in the for loop. This works and partially return some divisors but it didn't return all the divisors from each integers.
function divisors(n,res=[]) {
for (let i = 1; i <= Math.floor(Math.sqrt(n)); i++) {
!(n%i) && res.push(i)
}
return res
}
console.log(divisors(4)); // [ 1, 2 ]
console.log(divisors(9)); // [ 1, 3 ]
console.log(divisors(12)); // [ 1, 2, 3 ]
console.log(divisors(975179493674)); // [ 1, 2, 97, 194 ]
console.log(divisors(27550356289)); // [ 1, 165983 ]
I just can't quite wrap my head around it. Any help or pointers will be greatly appreciated.
UPDATE:
The solution is still possible with only 1 loop. It was missing a few lines of control flow as follow:
function divisors(n,res=[]) {
for (let i = 1; i <= Math.sqrt(n); i++) {
if (!(n%i)) {
i*i < n && res.push(i);
res.push(n/i);
}
}
return res;
}
console.log(divisors(4)); // [ 1, 2 ]
console.log(divisors(9)); // [ 1, 3 ]
console.log(divisors(12)); // [ 1, 2, 3 ]
console.log(divisors(975179493674)); // [ 1, 2, 97, 194, 5026698421, 10053396842, 487589746837, 975179493674 ]
console.log(divisors(27550356289)); // [ 1, 165983, 27550356289 ]
Having found all the divisors less than or equal to the square root of the input, you can then divide the input by those numbers to get the rest of the divisors:
function divisors(n,res=[]) {
for (let i = 1; i <= Math.floor(Math.sqrt(n)); i++) {
if (n%i == 0) { res.push(i) }
}
let len = res.length;
// if n is a square, don't include the square root twice
if (res[len-1] * res[len-1] == n) len--;
for (i = len - 1; i >= 0; i--) { res.push(n / res[i]) }
return res
}
console.log(divisors(4)); // [ 1, 2, 4 ]
console.log(divisors(9)); // [ 1, 3, 9 ]
console.log(divisors(12)); // [ 1, 2, 3, 4, 6, 12 ]
console.log(divisors(975179493674)); // [ 1, 2, 97, 194, 5026698421, 10053396842, 487589746837, 975179493674 ]
console.log(divisors(27550356289)); // [ 1, 165983, 27550356289 ]
It makes sense to distinguish several similar but different problems here:
(1) To check whether a number N is a prime number or not, you can stop searching for possible divisors when you've reached its square root. That's because if N == x * y, and x > sqrt(N), then y < sqrt(N), so you would have found y before finding x.
As an example with concrete numbers: to check whether 11 is prime, you can stop searching after checking that 11 % 2 != 0 and 11 % 3 != 0 (because sqrt(11) < 4). If any of 4, 5, 6, ... were divisors of 11, then there'd be a corresponding divisor 11/4 or 11/5 or 11/6 etc, all of which are smaller than sqrt(11), so you would have found them before.
(2) To find all prime factors of a number N, you can't simply stop searching at sqrt(N). In contrast with case (1): if you only want to test whether 10 is prime, you can stop searching for divisors after checking 3 == floor(sqrt(10)) (and you would have found 2 at that point, proving that 10 is not prime), whereas if your task is to find all prime factors, you need to somehow find 5 as well, and 5 > sqrt(10).
One way to accomplish that is to keep dividing N by each factor that you find, so you'd have something like:
function primeFactors(n,res=[]) {
for (let i = 2; i <= Math.floor(Math.sqrt(n)); ) {
let candidate = Math.floor(n / i);
if (candidate * i === n) {
res.push(i);
n = candidate;
} else {
i++;
}
}
res.push(n);
return res;
}
Note that this uses candidate * i === n instead of n % i === 0 because multiplications are much faster than divisions. Since we already have the n / i division (and can't avoid that in this approach), we can at least replace the second n % i division with that multiplication.
Similarly, you could improve performance further if you replaced the loop's condition i <= Math.floor(Math.sqrt(n)) with i*i <= n. Or even better, reusing the work we've already done: if (candidate < i) break;.
(3) To find all divisors (both prime factors and composite divisors), you can approach the problem from several directions:
The simplest is probably to do what #Nick's answer suggests: try all candidates i from 1 to sqrt(N), and whenever you find one, add both i and n / i to the list.
As a minor improvement to that, you could start at i = 2, and always add 1 and n to the list without checking (because every integer is divisible by 1 and by itself).
An alternative that's probably faster, but also more complicated to implement, is to find the prime factors first (see (2)), and then build the set of all divisors as the powerset of the prime factors. For example, if you find that the prime factors are [2, 3, 5], then the set of divisors is [1, 2, 3, 5, 2*3, 2*5, 3*5, 2*3*5]. (Note that this will need some deduplication when some prime factors occur more than once.)
If performance is really important, there's more you could do. For example, you could cache prime numbers you've found, and on subsequent invocations only check those as possible divisors.
A very simple step in this direction would be to special-case i=2, and then check only odd candidates (3, 5, 7, ...) afterwards. That simple trick would save about half the work!
One can even go as far as getting rid of expensive divisions entirely at the cost of spending some more memory to keep track of the next multiple of each prime that needs to be checked... but that's getting a bit far from your original question! Before getting too carried away with optimizations, I'd like to point out that for a single invocation, even for an input like 975179493674, any such tuning isn't worth the effort: you'd save a couple of milliseconds of execution time, but it would cost at least several minutes to implement. However, if this happens to be a performance-critical core part of an application, then it provides quite some room for investing implementation effort in order to save execution time.

Function to approximate long numbers

I have a question I have a value that I need to divide and make the values ​​without infinite decimals when divided by an odd number
example:
5 values ​​that add up to 200 divided by three people
this result is: 66.66666666666667
I want to avoid this, so that an approximation is made like:
2 people would stay with 65
and one person with 70
my code:
const test = [
{ price: 5, quantity: 10 },
{ price: 10, quantity: 10 },
{ price: 5, quantity: 10 },
];
const persons = ['person 1', 'person 2', 'person 3']
const total = testList
.map((test ) => test .unitPrice * test .quantity)
.reduce((sum, current) => sum + current);
const division = total/persons.length
I need a way that in the end the total is 200 divided for 3 people, without having decimal numbers
You need to figure out how many will remain. Remove them, divide it, than loop to add the remaining to the other buckets until you run out.
const total = 200
const numParts = 3
let extras = total % numParts;
const base = (total - extras) / numParts
const portions = new Array(numParts).fill(base)
for (let i = 0; i < numParts && extras > 0; i++) {
portions[i]++;
extras--;
}
console.log(portions);

JavaScript array find fitting range

I have the following array with two objects:
var myArr = [{
id: 3,
licences: 100
new_value_pr_licence: 40
}, {
id: 4,
licences: 200
new_value_pr_licence: 25
}]
A user wish to buy 150 licences. This means that they fall into the category 100 because they are above 100 licences but below 200 which means they pay $40 per licence.
Note that the array object values varies.
Order your plans by the price per licence:
myArr.sort(function (a, b) {
return a.new_value_pr_licence - b.new_value_pr_licence;
})
then starting from the start of the array, take as many of that plan as you can without going over the number the user wants to buy:
var numUserWants = 150;
var purchases = {};
var cheapestAvailableProduct = myArr.shift();
while (numUserWants > 0 && cheapestAvailableProduct) {
if (numUserWants <= cheapestAvailableProduct.licences) {
purchases[cheapestAvailableProduct.id] = Math.floor(cheapestAvailableProduct.licences / numUserWants);
numUserWants = cheapestAvailableProduct.licences % numUserWants;
}
cheapestAvailableProduct = myArr.shift();
}
At this point, purchases will now be a map of plan id to number:
purchases => {
3: 3
4: 1
}
This doesn't handle the case where over-purchasing is the cheapest option (eg: it's cheaper to buy 160 at 4x40, instead of 150 at 3x40 + 1x25 + 1x5), but it's probably a good start for you to tweaking.
Just a simple forEach here. Take the number requested, begin calculating/mutating total based on option limits, and once the number requested is less than the option limit you have your final total, which wont be mutated any longer and returned from the function.
function calculateDiscountedTotal(numberRequested, myArr){
var total;
// loop, compare, calculate
myArr.forEach(function(option) {
if(numberRequested >= option.licenses){
total = numberRequested * option.new_value_pr_licence
}
}
if(total != undefined){
return total;
} else {
// user never had enough for initial discount
return "no discount price";
}
}
Sort the array first in terms of number of licenses and then get the object in which number of licenses is less than number of licenses to be bought (just less than the next item in the array which is greater than number of licenses to be bought)
var myArr = [
{
id: 3,
licences: 100
new_value_pr_licence: 40,
},
{
id: 4,
licences: 200,
new_value_pr_licence: 25
},
];
var numOfLic = 150;
myArr.sort( function(a,b){ return a.licences - b.licences } );
var selectedObj = myArr.reduce( function(prev,current){
if ( current.licences > numOfLic )
{
return prev;
}
});
console.log ( "pricing should be " + ( selectedObj.new_value_pr_licence * numOfLic ) );

Self adjusting random list

I have a grid of 9 columns by 3 rows (so each column has 3 slots). A minimum of one slot in every column must be marked and no more than 3 can be marked. A mark is represented by a 1,2 or 3 digit.
There must always be 15 marked slots in total. To achieve this I tried the following:
var valueLeft = 15;
while (valueLeft > 0)
{
var ranValue = utils.getRandomInt(1,3);
console.log('slots remaining: ' + (valueLeft - ranValue));
if (ranValue >= valueLeft)
{
ranValue = valueLeft;
}
valueList.push(ranValue);
valueLeft -= ranValue;
}
console.log(valueList);
But this often gives me an array of numbers with less than 9 elements inside. I can see that my logic is flawed but I am pulling my hair out trying to figure out how I can get it to always give a set of 9 elements which all add up to 15.
For example what I might end up with is:
[2, 1, 1, 1, 1, 2, 3, 1, 2, 1]
When what I need for example is:
[2, 2, 1, 1, 1, 1, 3, 2, 2]
or
[2, 2, 1, 2, 1, 1, 3, 2, 1]
and so on.
Any advice appreciated. Feel free to flame me for my poor sense of logic :)
This answer shows a similar approach to many of those already posted, but I feel as though they're making it too complicated. It can be very straightforward:
function pad(list, size) {
var total = list.length;
while (total != size) {
var i = utils.getRandomInt(0, 8);
if (list[i] < 3) {
list[i]++;
total++;
}
}
return list;
}
var valueList = pad(new Array(1,1,1,1,1,1,1,1,1), 15);
You don't need a lot of cases. Just -- as many others have already said -- init the array with 1's. Then, simply add one to random elements (whose value isn't already 3) until the total is 15.
why don't you do this:
start off with an array that looks like this: 1,1,1,1,1,1,1,1,1
then make a function that picks a random number between 0 and 8 6 times.
if the same number has been picked more than twice, skip it and pick a new one
then correlate those 6 numbers to the index of the array and add 1 for each time it picks that number.
var i; var valueList = new Array(1,1,1,1,1,1,1,1,1);
for(i=0;i<6;i++)
{
var ranNum = utils.getRandomInt(0,8);
if(valueList[ranNum]<3) valueList[ranNum]+=1;
else i--;
}
just tested it, changed <=6 to <6 and it's working for me. Good luck!
Following logic should work. You should select a random value (within 1-3) such that choosing that would not lead us to not able to select a random value for further slots.
var gridLeft = 9
var valueLeft = 15
while(gridLeft>0) {
var ranValue
while(true) {
ranValue = utils.getRandomInt(1,3);
if (valueLeft-ranValue > 3*(gridLeft-1))
continue;
else if (valueLeft-ranValue < 1*(gridLeft-1))
continue;
else
break;
}
valueList.push(ranValue);
valueLeft -= ranValue;
gridLeft -= 1
}

Categories

Resources