Cannot find a solid solution to odds - javascript

I'm making something where a players gains credits over the level they currently have.
Now let's say on average I want to spend the amount of credits equal to the players level, meaning if he is level one he on average wins 1 credit, and if he is level 50 he will on average win 50.
Now what I've done is create a method to calculate different outcomes using the following function,
var getDailyRewards = function(level){
var averagePayment = 0.01 * level,
paymentIncrement = 3.00
paymentDivision = 4,
paymentAmount = 8;
var allPayments = [];
for(x = 1; x < (paymentAmount + 1); x++){
var payment = (averagePayment / paymentDivision) * Math.pow(x, paymentIncrement);
console.log(payment);
}
}
Output:
0.25
2
6.75
16
...
How can I link this to a percentage? from [0, 100]? I can't just divide payment by averagePayment as it would not work with numbers under one.

Related

Why are the differences between my numbers inconsistent (sort of compund interest but not really)

My code is supposed to generate an amount of xp required for each level, but on levels 6, 8 and 13 the difference between the xp needed for the previous level and the xp needed for the current level decrease. My code is as follows
const BASE_LVL_XP = 20;
let xp_collection = new Map();
for (let i = 1; i < 101; i ++) {
// xp_collection.set(i, BASE_LVL_XP * .1);
let amount = BASE_LVL_XP * (Math.ceil(Math.pow((1.1), (2 * i))) + i);
xp_collection.set(i+1, amount);
}
I'm also not 100% sure about the math, I kinda ripped it off of the compound interest formula

Change Size of Integer Array But Retain Array Sum Javascript - Probability Distribution

I think this is more conceptual than anything and my use case is very niche.
I am trying to create an array of probabilites to pass to an RNG function. The array of probabilites can vary in size so it can't have a static length. I am creating the array of probabilities based on a single integer 0-100. I know this sounds confusing but bear with me.
For example.
const range = 5
const weight = 30
const weights = new Array(10).fill(0)
const middleIndex = Math.floor(weight / 10)
weights[middleIndex] = 100
const add = 10 - middleIndex
let counter = 2
for (let i = middleIndex + 1; i < add + middleIndex; i++) {
weights[i] = 100 / counter
counter += 1
}
counter = 2
for (let i = middleIndex - 1; i >= 0; i--) {
weights[i] = 100 / counter
counter += 1
}
console.log(weights) // [ 25, 33.336, 50, 100, 50, 33.3336, 25, 20, 16.668, 14.285 ]
See that the output array is in the form length 10, but I need the same distribution of probabilities in length 5 to pass to my RNG function such that:
const cleanWeights = compressWeights(weights, range)
// This is my required function, it should output an array of length range and share the exact same distribution of probabilties with the input weights array.
const rand = weightedRandom(weights)
// Returns an int from range 0 to weights.length. Likely to equal ~2 - as distribution of probability (30/100) leans to lower end of range 0, 5
If there is a better approach all together for this please let me know, if you also spot any optimization opportunities I'd love to hear them.

Function that doubles value by various rates

I'm trying to make a line chart like the New York Times Coronavirus Deaths by U.S. State and Country Over Time: Daily Tracker.
NYT has some clever lines in the chart showing the doubling rate, every day, every 2 days, every 3 days, every week, and so on.
I'm wondering how to write a function that returns an array of values that represent these lines given a start value of 10 and a maxX-value of 36 (total number of days as of today).
This is where I'm at right now, I'm afraid it does not calculate the values correctly but it might explain what I want to achieve.
How can I do this in a correct way? My math is too rusty for this.
var maxX = 36;
var start = 10;
function double(factor) {
var f = start;
var arr = [f];
for (var i = 1; i < maxX; i++) {
f = f + (f / factor)
arr.push(f)
}
return arr
}
var lines = [1, 2, 3, 7, 30].map(f => {
return {
days: f,
arr: double(f)
}
})
console.log(lines)
You first need to figure out what to multiply each daily value by, given a doubling rate. For example, with a doubling rate of every 2 days, you'd want each day count to be multiplied by 1.412 (square root of 2); after 1 day, it'd be 1.412x, after 2 days, it'd be 2x, after 4 days, it'd be 4x, etc.
For the general solution, the equation to solve is:
orig * (dayFactor ** daysToDouble) = 2 * orig
where orig is the original number of infections (here, 10), and dayFactor is the value you want to solve for, since that's the number the infections should be multiplied each day.
orig * (dayFactor ** daysToDouble) = 2 * orig
(dayFactor ** daysToDouble) = 2
dayFactor = 2 ** (1 / daysToDouble)
In the function, identify the dayFactor given the doubling rate, then multiply the count by that each day:
var maxX = 36;
var start = 10;
function double (daysToDouble) {
const dayFactor = 2 ** (1 / daysToDouble);
let currentInfected = start;
const arr = [currentInfected];
for (var i = 1; i < maxX; i++){
currentInfected *= dayFactor;
arr.push(currentInfected);
}
return arr;
}
var lines = [1,2,3,7,30].map(f => {
return {days: f, arr: double(f)}
});
console.log(lines[2]); // double every 3rd day

How to loop through an array of numbers to see which numbers are relevant to another number?

I'm trying to write a simple program in vanilla JavaScript for weightlifting. The user inputs a certain amount of weight and it returns the specific weight plates to put on each side of the barbell.
I then take that number into a function which subtracts 45 from it to account for the barbell weight then divides that number by 2 which is the amount of weight to put on each side of the bar.
const num = document.getElementById("weightAmount").value;
function getWeightAmount (num) {
const newNum = num - 45;
const halfNum = newNum / 2;
return getWeights(halfNum);
}
I have an array with each weight plate:
let plates = [44, 33, 22, 11, 5.5, 2.75];
I'm having trouble correctly looping through the array to get what I want. If I need, say, 60.5 lbs on each side, it should return 44, 11, 5.5. So I need to figure out which numbers in the plate array fit in the number returned from my first function.
I have an empty array called weights which I want to push the numbers from the plates array into that work with the weight which then is returned.
My question is how do I loop through the plates array to figure out which weights are needed?
A possible solution to this is iterating indefinitely until either
you have a solution
the problem becomes unsolvable given the set of weights
Every iteration step, you subtract the highest possible weight times the highest possible factor, store both in a suitable data structure (my implementation simply uses an Object) and continue.
const plates = [44, 33, 22, 11, 5.5, 2.75];
// We assume that plates is always sorted
const determineWeights = (totalWeight) => {
let factor = 0;
let weights = {};
while (totalWeight > 0) {
weight = plates.find(weight => Math.floor(totalWeight / weight) > 0);
// There is no weight we can subtract from the total weight to solve the problem
// Hence, the problem is unsolvable and we return null to indicate that no solution exists
if (!weight) { return null; }
// Determine the factor with which to multiply the weight before we subtract from the total weight an subtract the product
factor = Math.floor(totalWeight / weight);
totalWeight = totalWeight - factor * weight;
// Store weight and factor
weights[weight] = factor;
}
return weights;
}
console.log(determineWeights(104.5)); // { "11": 1, "44": 2, "5.5": 1 }
console.log(determineWeights(60.5)); // { "11": 1, "44": 1, "5.5": 1 }
console.log(determineWeights(5.0)); // null
The problem is essentially an instance of the Knapsack problem.
Note that we assume that plates is sorted. Otherwise, Array.find will not necessarily retrieve the maximum weight that can be subtracted from the total weight.
I have a simple solution below if value of the target weight will always be the sum of the available plates. Assuming the weights array are sorted in a descending order. I loop thought all available weights and will only proceed to the next weight if the total exceeds the total weight you require.
function getWeights(targeWeight) {
let plates = [44, 33, 22, 11, 5.5, 2.75];
let totalWeight = 0;
let neededPlates = [];
let i = 0;
while(i < plates.length){
var pweight = totalWeight + plates[i];
if (pweight > targeWeight) {
i++;
continue;
}
totalWeight += plates[i];
neededPlates.push(plates[i]);
}
return neededPlates;
}
console.log(getWeights(60.5)); // [44, 11, 5.5]
console.log(getWeights(104.5)); //[44, 44, 11, 5.5]
Here's a solution. In the event that the available plates don't add up to the target weight, it will return the combination of available plates that add up closest to the target. Adapted from this answer.
function createSubsets(numbers, target) {
// filter out all items larger than target
numbers = numbers.filter(function (value) {
return value <= target;
});
// sort from largest to smallest
numbers.sort(function (a, b) {
return b - a;
});
var i;
var sum = 0;
var addedIndices = [];
// go from the largest to the smallest number and
// add as many of them as long as the sum isn't above target
for (i = 0; i < numbers.length; i++) {
if (sum + numbers[i] <= target) {
sum += numbers[i];
addedIndices.push(i);
}
}
return addedIndices.map(n => numbers[n]);
}

Calculate Ratios in JS like Humble Bundle

Do you know the sliders that you have on humblebundle.com when selecting where you want the money to go? Well when you adjust any one ratio it will automatically adjust the rest.
So say you're paying $20 no matter what but you want to adjust your tip to HB from $2 to $5, the ratios that were on the other stuff should automatically lowered to match but I have no idea what I'm doing.
This is as close as I get mathematically:
var settip = 50;
var tip = 5;
var devs = 75;
var donation = 20;
tip = settip;
var newAvail = 100 - tip;
var rCalc = 100 - (devs + donation);
devs = ((devs + rCalc) * newAvail) * .01;
donation = ((donation + rCalc) * newAvail) * .01;
console.log("New Ratio Calculation: " + rCalc);
console.log("New available space: " + newAvail);
console.log(tip);
console.log(devs);
console.log(donation);
The console logs are just so I can try and put it together in my head where things are going wrong. The numbers are also whole numbers first: 50 instead of .5 because Javascript is not accurate and I don't want to do the fix code every time, I'd rather figure out how to make the code work first and then think about optimizing.
So if anyone could guide me on a method or where I am going wrong here, then that'd be great. Thanks.
Tip is tip to the bundle maker.
Devs is tip to the devs.
Donation is tip to the donation box.
Each number is the ratio. Settip is the new ratio, I should be able to change any one value and have it automatically change all others but I can't even figure out how to do the first part so I couldn't begin to try for the second part of making it actually functional.
I think this problem is not as easy as it might seem if you want to cover different edge cases. Here I assume that you distribute money so you need following properties:
Each amount must be whole integer in cents
Sum of all amounts must be equal to the total sum
The simplest way to deal with it in JS is to make all calculations using whole numbers (e.g. sum in cents instead of dollars) and format them in more human-readable way on UI. Still even with this simplification it requires some non-trivial code:
function updateRates(rates, newValue, index) {
var i, len = rates.length;
var sum = 0;
for (i = 0; i < len; i++)
sum += rates[i];
var oldValue = rates[index];
var newRest = sum - newValue;
var curRest = sum - rates[index];
rates[index] = newValue;
var remainders = new Array(len);
var fraction, value, subsum = 0;
for (i = 0; i < len; i++) {
if (i === index) continue;
// special case, all other sliders were at 0 - split value equally
if (curRest === 0) {
fraction = 1.0 / (len - 1)
}
else {
fraction = rates[i] / curRest
}
value = newRest * fraction;
rates[i] = Math.floor(value); // always round down and then distribute rest according to the Largest remainder method
subsum += rates[i];
remainders[i] = {
index: i,
value: value - rates[i]
};
}
// sort remainders and distribute rest (fractions) accordingly
remainders.sort(function (a, b) {
var av = a.value;
var bv = b.value;
if (av === bv)
return 0;
if (av < bv)
return 1;
else
return -1;
});
for (i = 0; subsum < newRest; i++) {
rates[remainders[i].index] += 1;
subsum += 1;
}
return rates;
}
Some non-trivial tests:
1. updateRates([85,10,5], 82, 0) => [82, 12, 6]
2. updateRates([85,10,5], 83, 0) => [83, 11, 6]
3. updateRates([85,10,5], 84, 0) => [84, 11, 5]
4. updateRates([100,0,0], 95, 0) => [95, 2, 3]
5. updateRates([4,3,3,1], 0, 0) => [0, 5, 5, 1]
Pay attention to the example #5. If one used some naive rounding, sum will not be preserved. Effectively you need to distribute +4 in proportion 3:3:1. It means you should add +12/7, +12/7 and +4/7. Since 12/7 = 1 5/7, according to standard mathematical rules all three should be rounded up resulting in +2, +2, +1 but we only got +4 cents to distribute. To fix this issue the largest remainder method is used to distribute fractional cents among categories. Simply speaking the idea is that we first distribute only whole number of cents (i.e. always round down), calculate how many cents are actually left and then distribute them one by one. The biggest possible drawback of this method is that some rates that started with equal values might have different values after update. On the other hand this can't be avoided as example #4 shows: you can't split 5 cents equally between two categories.
To restate what I think you want: the three variables tip, devs and donation should always sum to 100. When one variable is updated, the other two should be updated to compensate. The automatic updates should keep the same ratios to each other (for example, if donation is double devs, and tips is updated, then the updated donation value should still be double the devs value).
If I've got that right, then this should work for you:
var tips = 5;
var devs = 20;
var donation = 75;
var setTips = function(newValue) {
tips = newValue;
var sum = devs + donation;
var devShare = devs / sum; // the share devs gets between devs and donation
var donationShare = 1 - devShare; // or could calculate as donation / sum
devs = (100 - tips) * devShare; // the remaining times it's share ratio
donation = (100 - tips) * donationShare; // the remaining times it's share ratio
};
// test it out
setTips(50);
console.log(tips, devs, donation);

Categories