Is there any function or method for finding the answer - javascript

find total number of elements in between the numbers 0 t0 10, 10 to 20, 20 to 30 and so on in an array.
say the array [23,14,67,17,87]
so there are 0 numbers from 0 to 10, 2 numbers from 10 to 20, and so on.

This crux of the logic:
const data = [23,14,67,17,87];
console.log(
data.reduce((results, entry) => {
// We only are interested in the 10s unit; the 1s don't matter.
// This discards the 1s unit, and homologates numbers of the same multiple
// of 10, e.g.
// 14 → 1
// 17 → 1
const factor = Math.floor(entry / 10);
// Create a human readable key, i.e 10 to 20
const key = `${factor * 10} to ${(factor * 10) + 10}`
return {
...results,
// If the group has not already been added to the results,
// this will set the value to 1, otherwise increment by 1.
[key]: (results[key] ?? 0) + 1,
};
}, {})
);
You can always sort the groups if needed.

Related

Javascript smart and good way to make array of size N where each object in the array has a certain probability? [duplicate]

I'm trying to devise a (good) way to choose a random number from a range of possible numbers where each number in the range is given a weight. To put it simply: given the range of numbers (0,1,2) choose a number where 0 has an 80% probability of being selected, 1 has a 10% chance and 2 has a 10% chance.
It's been about 8 years since my college stats class, so you can imagine the proper formula for this escapes me at the moment.
Here's the 'cheap and dirty' method that I came up with. This solution uses ColdFusion. Yours may use whatever language you'd like. I'm a programmer, I think I can handle porting it. Ultimately my solution needs to be in Groovy - I wrote this one in ColdFusion because it's easy to quickly write/test in CF.
public function weightedRandom( Struct options ) {
var tempArr = [];
for( var o in arguments.options )
{
var weight = arguments.options[ o ] * 10;
for ( var i = 1; i<= weight; i++ )
{
arrayAppend( tempArr, o );
}
}
return tempArr[ randRange( 1, arrayLen( tempArr ) ) ];
}
// test it
opts = { 0=.8, 1=.1, 2=.1 };
for( x = 1; x<=10; x++ )
{
writeDump( weightedRandom( opts ) );
}
I'm looking for better solutions, please suggest improvements or alternatives.
Rejection sampling (such as in your solution) is the first thing that comes to mind, whereby you build a lookup table with elements populated by their weight distribution, then pick a random location in the table and return it. As an implementation choice, I would make a higher order function which takes a spec and returns a function which returns values based on the distribution in the spec, this way you avoid having to build the table for each call. The downsides are that the algorithmic performance of building the table is linear by the number of items and there could potentially be a lot of memory usage for large specs (or those with members with very small or precise weights, e.g. {0:0.99999, 1:0.00001}). The upside is that picking a value has constant time, which might be desirable if performance is critical. In JavaScript:
function weightedRand(spec) {
var i, j, table=[];
for (i in spec) {
// The constant 10 below should be computed based on the
// weights in the spec for a correct and optimal table size.
// E.g. the spec {0:0.999, 1:0.001} will break this impl.
for (j=0; j<spec[i]*10; j++) {
table.push(i);
}
}
return function() {
return table[Math.floor(Math.random() * table.length)];
}
}
var rand012 = weightedRand({0:0.8, 1:0.1, 2:0.1});
rand012(); // random in distribution...
Another strategy is to pick a random number in [0,1) and iterate over the weight specification summing the weights, if the random number is less than the sum then return the associated value. Of course, this assumes that the weights sum to one. This solution has no up-front costs but has average algorithmic performance linear by the number of entries in the spec. For example, in JavaScript:
function weightedRand2(spec) {
var i, sum=0, r=Math.random();
for (i in spec) {
sum += spec[i];
if (r <= sum) return i;
}
}
weightedRand2({0:0.8, 1:0.1, 2:0.1}); // random in distribution...
Generate a random number R between 0 and 1.
If R in [0, 0.1) -> 1
If R in [0.1, 0.2) -> 2
If R in [0.2, 1] -> 3
If you can't directly get a number between 0 and 1, generate a number in a range that will produce as much precision as you want. For example, if you have the weights for
(1, 83.7%) and (2, 16.3%), roll a number from 1 to 1000. 1-837 is a 1. 838-1000 is 2.
I use the following
function weightedRandom(min, max) {
return Math.round(max / (Math.random() * max + min));
}
This is my go-to "weighted" random, where I use an inverse function of "x" (where x is a random between min and max) to generate a weighted result, where the minimum is the most heavy element, and the maximum the lightest (least chances of getting the result)
So basically, using weightedRandom(1, 5) means the chances of getting a 1 are higher than a 2 which are higher than a 3, which are higher than a 4, which are higher than a 5.
Might not be useful for your use case but probably useful for people googling this same question.
After a 100 iterations try, it gave me:
==================
| Result | Times |
==================
| 1 | 55 |
| 2 | 28 |
| 3 | 8 |
| 4 | 7 |
| 5 | 2 |
==================
Here are 3 solutions in javascript since I'm not sure which language you want it in. Depending on your needs one of the first two might work, but the the third one is probably the easiest to implement with large sets of numbers.
function randomSimple(){
return [0,0,0,0,0,0,0,0,1,2][Math.floor(Math.random()*10)];
}
function randomCase(){
var n=Math.floor(Math.random()*100)
switch(n){
case n<80:
return 0;
case n<90:
return 1;
case n<100:
return 2;
}
}
function randomLoop(weight,num){
var n=Math.floor(Math.random()*100),amt=0;
for(var i=0;i<weight.length;i++){
//amt+=weight[i]; *alternative method
//if(n<amt){
if(n<weight[i]){
return num[i];
}
}
}
weight=[80,90,100];
//weight=[80,10,10]; *alternative method
num=[0,1,2]
8 years late but here's my solution in 4 lines.
Prepare an array of probability mass function such that
pmf[array_index] = P(X=array_index):
var pmf = [0.8, 0.1, 0.1]
Prepare an array for the corresponding cumulative distribution function such that
cdf[array_index] = F(X=array_index):
var cdf = pmf.map((sum => value => sum += value)(0))
// [0.8, 0.9, 1]
3a) Generate a random number.
3b) Get an array of elements that are more than or equal to this number.
3c) Return its length.
var r = Math.random()
cdf.filter(el => r >= el).length
This is more or less a generic-ized version of what #trinithis wrote, in Java: I did it with ints rather than floats to avoid messy rounding errors.
static class Weighting {
int value;
int weighting;
public Weighting(int v, int w) {
this.value = v;
this.weighting = w;
}
}
public static int weightedRandom(List<Weighting> weightingOptions) {
//determine sum of all weightings
int total = 0;
for (Weighting w : weightingOptions) {
total += w.weighting;
}
//select a random value between 0 and our total
int random = new Random().nextInt(total);
//loop thru our weightings until we arrive at the correct one
int current = 0;
for (Weighting w : weightingOptions) {
current += w.weighting;
if (random < current)
return w.value;
}
//shouldn't happen.
return -1;
}
public static void main(String[] args) {
List<Weighting> weightings = new ArrayList<Weighting>();
weightings.add(new Weighting(0, 8));
weightings.add(new Weighting(1, 1));
weightings.add(new Weighting(2, 1));
for (int i = 0; i < 100; i++) {
System.out.println(weightedRandom(weightings));
}
}
How about
int [ ] numbers = { 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 2 } ;
then you can randomly select from numbers and 0 will have an 80% chance, 1 10%, and 2 10%
This one is in Mathematica, but it's easy to copy to another language, I use it in my games and it can handle decimal weights:
weights = {0.5,1,2}; // The weights
weights = N#weights/Total#weights // Normalize weights so that the list's sum is always 1.
min = 0; // First min value should be 0
max = weights[[1]]; // First max value should be the first element of the newly created weights list. Note that in Mathematica the first element has index of 1, not 0.
random = RandomReal[]; // Generate a random float from 0 to 1;
For[i = 1, i <= Length#weights, i++,
If[random >= min && random < max,
Print["Chosen index number: " <> ToString#i]
];
min += weights[[i]];
If[i == Length#weights,
max = 1,
max += weights[[i + 1]]
]
]
(Now I'm talking with a lists first element's index equals 0) The idea behind this is that having a normalized list weights there is a chance of weights[n] to return the index n, so the distances between the min and max at step n should be weights[n]. The total distance from the minimum min (which we put it to be 0) and the maximum max is the sum of the list weights.
The good thing behind this is that you don't append to any array or nest for loops, and that increases heavily the execution time.
Here is the code in C# without needing to normalize the weights list and deleting some code:
int WeightedRandom(List<float> weights) {
float total = 0f;
foreach (float weight in weights) {
total += weight;
}
float max = weights [0],
random = Random.Range(0f, total);
for (int index = 0; index < weights.Count; index++) {
if (random < max) {
return index;
} else if (index == weights.Count - 1) {
return weights.Count-1;
}
max += weights[index+1];
}
return -1;
}
I suggest to use a continuous check of the probability and the rest of the random number.
This function sets first the return value to the last possible index and iterates until the rest of the random value is smaller than the actual probability.
The probabilities have to sum to one.
function getRandomIndexByProbability(probabilities) {
var r = Math.random(),
index = probabilities.length - 1;
probabilities.some(function (probability, i) {
if (r < probability) {
index = i;
return true;
}
r -= probability;
});
return index;
}
var i,
probabilities = [0.8, 0.1, 0.1],
count = probabilities.map(function () { return 0; });
for (i = 0; i < 1e6; i++) {
count[getRandomIndexByProbability(probabilities)]++;
}
console.log(count);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Thanks all, this was a helpful thread. I encapsulated it into a convenience function (Typescript). Tests below (sinon, jest). Could definitely be a bit tighter, but hopefully it's readable.
export type WeightedOptions = {
[option: string]: number;
};
// Pass in an object like { a: 10, b: 4, c: 400 } and it'll return either "a", "b", or "c", factoring in their respective
// weight. So in this example, "c" is likely to be returned 400 times out of 414
export const getRandomWeightedValue = (options: WeightedOptions) => {
const keys = Object.keys(options);
const totalSum = keys.reduce((acc, item) => acc + options[item], 0);
let runningTotal = 0;
const cumulativeValues = keys.map((key) => {
const relativeValue = options[key]/totalSum;
const cv = {
key,
value: relativeValue + runningTotal
};
runningTotal += relativeValue;
return cv;
});
const r = Math.random();
return cumulativeValues.find(({ key, value }) => r <= value)!.key;
};
Tests:
describe('getRandomWeightedValue', () => {
// Out of 1, the relative and cumulative values for these are:
// a: 0.1666 -> 0.16666
// b: 0.3333 -> 0.5
// c: 0.5 -> 1
const values = { a: 10, b: 20, c: 30 };
it('returns appropriate values for particular random value', () => {
// any random number under 0.166666 should return "a"
const stub1 = sinon.stub(Math, 'random').returns(0);
const result1 = randomUtils.getRandomWeightedValue(values);
expect(result1).toEqual('a');
stub1.restore();
const stub2 = sinon.stub(Math, 'random').returns(0.1666);
const result2 = randomUtils.getRandomWeightedValue(values);
expect(result2).toEqual('a');
stub2.restore();
// any random number between 0.166666 and 0.5 should return "b"
const stub3 = sinon.stub(Math, 'random').returns(0.17);
const result3 = randomUtils.getRandomWeightedValue(values);
expect(result3).toEqual('b');
stub3.restore();
const stub4 = sinon.stub(Math, 'random').returns(0.3333);
const result4 = randomUtils.getRandomWeightedValue(values);
expect(result4).toEqual('b');
stub4.restore();
const stub5 = sinon.stub(Math, 'random').returns(0.5);
const result5 = randomUtils.getRandomWeightedValue(values);
expect(result5).toEqual('b');
stub5.restore();
// any random number above 0.5 should return "c"
const stub6 = sinon.stub(Math, 'random').returns(0.500001);
const result6 = randomUtils.getRandomWeightedValue(values);
expect(result6).toEqual('c');
stub6.restore();
const stub7 = sinon.stub(Math, 'random').returns(1);
const result7 = randomUtils.getRandomWeightedValue(values);
expect(result7).toEqual('c');
stub7.restore();
});
});
Shortest solution in modern JavaScript
Note: all weights need to be integers
function weightedRandom(items){
let table = Object.entries(items)
.flatMap(([item, weight]) => Array(item).fill(weight))
return table[Math.floor(Math.random() * table.length)]
}
const key = weightedRandom({
"key1": 1,
"key2": 4,
"key3": 8
}) // returns e.g. "key1"
here is the input and ratios : 0 (80%), 1(10%) , 2 (10%)
lets draw them out so its easy to visualize.
0 1 2
-------------------------------------________+++++++++
lets add up the total weight and call it TR for total ratio. so in this case 100.
lets randomly get a number from (0-TR) or (0 to 100 in this case) . 100 being your weights total. Call it RN for random number.
so now we have TR as the total weight and RN as the random number between 0 and TR.
so lets imagine we picked a random # from 0 to 100. Say 21. so thats actually 21%.
WE MUST CONVERT/MATCH THIS TO OUR INPUT NUMBERS BUT HOW ?
lets loop over each weight (80, 10, 10) and keep the sum of the weights we already visit.
the moment the sum of the weights we are looping over is greater then the random number RN (21 in this case), we stop the loop & return that element position.
double sum = 0;
int position = -1;
for(double weight : weight){
position ++;
sum = sum + weight;
if(sum > 21) //(80 > 21) so break on first pass
break;
}
//position will be 0 so we return array[0]--> 0
lets say the random number (between 0 and 100) is 83. Lets do it again:
double sum = 0;
int position = -1;
for(double weight : weight){
position ++;
sum = sum + weight;
if(sum > 83) //(90 > 83) so break
break;
}
//we did two passes in the loop so position is 1 so we return array[1]---> 1
I have a slotmachine and I used the code below to generate random numbers. In probabilitiesSlotMachine the keys are the output in the slotmachine, and the values represent the weight.
const probabilitiesSlotMachine = [{0 : 1000}, {1 : 100}, {2 : 50}, {3 : 30}, {4 : 20}, {5 : 10}, {6 : 5}, {7 : 4}, {8 : 2}, {9 : 1}]
var allSlotMachineResults = []
probabilitiesSlotMachine.forEach(function(obj, index){
for (var key in obj){
for (var loop = 0; loop < obj[key]; loop ++){
allSlotMachineResults.push(key)
}
}
});
Now to generate a random output, I use this code:
const random = allSlotMachineResults[Math.floor(Math.random() * allSlotMachineResults.length)]
Enjoy the O(1) (constant time) solution for your problem.
If the input array is small, it can be easily implemented.
const number = Math.floor(Math.random() * 99); // Generate a random number from 0 to 99
let element;
if (number >= 0 && number <= 79) {
/*
In the range of 0 to 99, every number has equal probability
of occurring. Therefore, if you gather 80 numbers (0 to 79) and
make a "sub-group" of them, then their probabilities will get added.
Hence, what you get is an 80% chance that the number will fall in this
range.
So, quite naturally, there is 80% probability that this code will run.
Now, manually choose / assign element of your array to this variable.
*/
element = 0;
}
else if (number >= 80 && number <= 89) {
// 10% chance that this code runs.
element = 1;
}
else if (number >= 90 && number <= 99) {
// 10% chance that this code runs.
element = 2;
}

Find the maximum product that can be formed by taking any one element from each sub-array

I am trying to write an efficient algorithm in JavaScript to solve this task. Please see the next examples of input data and correct results:
Array: [ [-3,-4], [1,2,-3] ] Result: (-4)*(-3) = 12
Array: [ [1,-1], [2,3], [10,-100,20] ] Result: (-1)*3*(-100) = 300
Array: [ [-3,-15], [-3,-7], [-5,1,-2,-7] ] Result: (-15)*(-7)*1 = 105
It can be any number of sub-arrays and any number of elements in each sub-array. What I already found is that I probably should leave only min and max values in the each sub-array, I did it using .map(a => [Math.min(...a), Math.max(...a)]) and sort them using .sort((a, b) => a[0] - b[0]).
And now I am stuck. Probably there is a way to calculate all possible products but I am sure that it's not an effective way to solve this task.
Please help!
The problem you post can be solved with a simple algorithm. We just need to keep tracking the maximum/minimum when iterating over each sub-array. We can keep finding the next maximum/minimum by multiplying the current maximum/minimum with the max/min value in each sub-array. We pick the maximum when the iterating is over. Its time complexity is O(n) where n is total number of elements in an array (i.e. sum of number of elements in each sub-array).
Here's the complete code. find_maximum_product function keeps tracking the minimum/maximum and returns the maximum eventually, and it also keeps tracking the multipliers and return it:
/**
* arr: array of any number of sub-arrays and
* any number of elements in each sub-array.
* e.g. [[1, -1], [2, 3], [10, -100, 20]]
*/
function find_maximum_product(arr) {
let max = 1;
let min = 1;
let max_multipliers = [];
let min_multipliers = [];
for (let i = 0; i < arr.length; i++) {
const a = Math.max(...arr[i]);
const b = Math.min(...arr[i]);
const candidates = [max * a, max * b, min * a, min * b];
max = Math.max(...candidates);
min = Math.min(...candidates);
let new_max_multipliers;
let new_min_multipliers;
switch (max) {
case candidates[0]:
new_max_multipliers = max_multipliers.concat(a);
break;
case candidates[1]:
new_max_multipliers = max_multipliers.concat(b);
break;
case candidates[2]:
new_max_multipliers = min_multipliers.concat(a);
break;
case candidates[3]:
new_max_multipliers = min_multipliers.concat(b);
break;
}
switch (min) {
case candidates[0]:
new_min_multipliers = max_multipliers.concat(a);
break;
case candidates[1]:
new_min_multipliers = max_multipliers.concat(b);
break;
case candidates[2]:
new_min_multipliers = min_multipliers.concat(a);
break;
case candidates[3]:
new_min_multipliers = min_multipliers.concat(b);
break;
}
max_multipliers = new_max_multipliers;
min_multipliers = new_min_multipliers;
}
if (max >= min) {
return [max, max_multipliers];
}
return [min, min_multipliers];
}
const arrays = [
[
[-3, -4],
[1, 2, -3],
],
[
[1, -1],
[2, 3],
[10, -100, 20],
],
[
[-3, -15],
[-3, -7],
[-5, 1, -2, -7],
],
[
[14, 2],
[0, -16],
[-12, -16],
],
[
[-20, -4, -19, -18],
[0, -15, -10],
[-13, 4],
],
[
[-2, -15, -12, -8, -16],
[-4, -15, -7],
[-10, -5],
],
];
for (let i = 0; i < arrays.length; i++) {
const [max, max_multipliers] = find_maximum_product(arrays[i]);
console.log('Array:', JSON.stringify(arrays[i]));
console.log('Result:', `${max_multipliers.join(' * ')} = ${max}`);
console.log('');
}
UPDATE
Simpler version for just getting the maximum, not getting the multipliers:
/**
* arr: array of any number of sub-arrays and
* any number of elements in each sub-array.
* e.g. [[1, -1], [2, 3], [10, -100, 20]]
*/
function get_maximum_product(arr) {
return arr
.map((a) => [Math.min(...a), Math.max(...a)])
.reduce(
(acc, current) => {
const candidates = [
acc[0] * current[0],
acc[0] * current[1],
acc[1] * current[0],
acc[1] * current[1],
];
return [Math.min(...candidates), Math.max(...candidates)];
},
[1, 1]
)[1];
}
Here is a top-down recurrence that could be adapted to bottom-up (a loop) and utilises O(n) search space.
Until I can complete it, the reader is encouraged to add a third return value in the tuple, largest_non_positive for that special case.
// Returns [highest positive, lowest negative]
// Does not address highest non-positive
function f(A, i){
const high = Math.max(...A[i]);
const low = Math.min(...A[i]);
if (i == 0){
if (low < 0 && high >= 0)
return [high, low];
if (low <= 0 && high <= 0)
return [-Infinity, low];
if (low >= 0 && high >= 0)
return [high, Infinity];
}
const [pos, neg] = f(A, i - 1);
function maybeZero(prod){
return isNaN(prod) ? 0 : prod;
}
let hp = maybeZero(high * pos);
let hn = maybeZero(high * neg);
let ln = maybeZero(low * neg);
let lp = maybeZero(low * pos);
if (low < 0 && high >= 0)
return [Math.max(hp, ln), Math.min(hn, lp)];
if (low <= 0 && high <= 0)
return [ln, lp];
if (low >= 0 && high >= 0)
return [hp, hn];
}
var As = [
[[-3,-4], [1,2,-3]],
[[1,-1], [2,3], [10,-100,20]],
[[-3,-15], [-3,-7], [-5,1,-2,-7]],
[[-11,-6], [-20,-20], [18,-4], [-20,1]],
[[-1000,1], [-1,1], [-1,1], [-1,1]],
[[14,2], [0,-16], [-12,-16]],
[[-20, -4, -19, -18], [0, -15, -10],[-13, 4]]
];
for (let A of As){
console.log(JSON.stringify(A));
console.log(f(A, A.length - 1)[0]);
console.log('');
}
Sort values in arrays by their absolute value in descending order
Check the product of first array elements if its positive its the answer
Otherwise lets call product p and we know p < 0, so if we change some positive element to some negative element or vice verse we will improve answer
we can simply check all possible elements to change, for each array a element x we can check if p / a[0] * x is better than current result if it is we update our answer
*Special case: all elements in arrays are negative and we have odd number of arrays, then we simply sort in increasing order
Complexity: O(n log n) where n is total amount of elements across all arrays
Take the product of the highest number of all the arrays that have at least one positive number.
If there's an odd number of remaining arrays (with only negatives), find the one with the highest (closest to zero) negative number, and set its absolute aside.
Take the arrays that remain after step 2, take the product of their lowest number (furthest from zero), and multiply it by the products from step 1 and (if any) step 2.
(also, avoid 0 if it would be the chosen number)
The first thing to notice is that there are only two specific cases where it's not possible to get a positive product. So I think an algorithm should first check if those specific cases are happening, then call a different subalgorithm for each of the three possible situations:
it is possible to get a positive product, so we want to find the highest positive product;
one of the arrays is full of zeroes, so all products are zero;
it is impossible to get a positive product, because no array has both positive and negative numbers, and there is an odd number of arrays with only negative numbers, so we want to find the closest to zero negative product.
The second and third cases lead to trivial algorithms.
Let's consider the first case.
For every array, the only numbers that can be useful in the highest product are the highest positive number, and the lowest negative number. If an array only has positive numbers or only has negative numbers, then there is only one useful number in that array, which can be chosen immediately.
For all the remaining arrays, you have to choose whether to use the positive or the negative number. Ideally, you want to use the one with the highest absolute value; but if you do that for every array, then the result might be negative.
This leads to a linear algorithm:
For all of the remaining arrays, initially select the number with the highest absolute value
If the resulting product is positive, you're done.
If the resulting product is negative, then a compromise has to be done in one of the arrays. For every array, compute the "cost" of this compromise (equal to the difference between the absolute values of the two interesting numbers in that array, multiplied by the product of all the other selected numbers).
Finally, choose the array whose cost is the lowest, and change the chosen number in that array.
Here is an example execution of the algorithm on the list of arrays [[18,19,20,-23], [12,-10,9,8],[-10,-3],[5,3],[-10,-5]].
Here we notice that it is possible to find a positive solution, because at least one of the arrays contains both negative and positive numbers.
For the last three arrays we have no choice between positive and negative: so we can already choose -10, 5 and -10 as the three numbers for these three arrays. For the first array, we'll have to choose between 20 and -23; and for the second array we'll have to choose between 12 and -10.
So the final product will be: (20 or -23) * (12 and -10) * (-10) * 5 * (-10).
Ideally, we would prefer 23 to 20, and 12 to 10. That would result in:
(-23) * 12 * (-10) * 5 * (-10)
Unfortunately, this is negative. So the question is: do we replace -23 with 20, or 12 with -10?
The cost of replacing -23 with 20 would be (23-20) * 11 * (10*5*10) = 33 * (10*5*10).
The cost of replacing 12 with -10 would be (12-10) * 21 * (10*5*10) = 42 * (10*5*10).
Finally, we choose to replace -23 with 20, because that is the less costly compromise.
The final product is 20 * 12 * (-10) * 5 * (-10).

generating intervals multiples of 10 between two numbers

I have two numbers like ESP1 and ESP2.
In my DB there are many data to the ESP1 & ESP2, ESP1 is minimum values and ESP2 is maximum value.
i need to generate intervals between those two number in multiplies of 10, for example.
ESP1 = 0 and ESP2 = 83
my result should be [0,10,20,30,40,50,60,70,80].
how to write code for this in js
Just use a for loop:
function generateIntervalsOf(interval, start, end) {
const result = [];
let current = start;
while (current < end) {
result.push(current);
current += interval;
}
return result;
}
generateIntervalsOf(10, 0, 83) // [0, 10, 20, 30, 40, 50, 60, 70, 80]
Another solution
const a = 0;
const b = 83;
const result = _.range(a, b, 1).filter(item => item%10 == 0);
console.log(result)
Use either a for or while loop. There are two approaches you could take.
A: Brute force. Count up from the start number to the end number and store every multiple of ten.
const results = [];
for (let i = ESP1; i <= ESP2; i++) {
if (i %10 === 0) {
results.push(i);
}
}
B: Cleverness. Brute force might be slow if you were doing this over a very large range. Instead of going through every single number, we only need the multiples of ten. So, in short, we just need the next multiple of ten after ESP1, and we add ten until we exceed ESP2.
e.g.
const results = [];
const floor = ESP1 + 10 - ESP1 % 10;
// i.e. if ESP1 is 13, add 10 to get 23, and then subtract 3 to get 20
for(let i = floor; i < ESP2; i+= 10) {
results.push(i);
}
If we ran this with ESP1 = 3 and ESP2 = 56, we would get the result [10, 20, 30, 40, 50]. This approach will go through 1/10 as many iterations as approach A, since we're counting by tens.

Finding all possible combined (plus and minus) sums of n arguments?

I'm trying to build a function that takes a variable number of arguments.
The function takes n inputs and calculates all possible sums of addition and subtraction e.g. if the args are 1,2,3
1 + 2 + 3
1 - 2 - 3
1 + 2 - 3
1 - 2 + 3
Finally, the function outputs the sum that is closest to zero. In this case, that answer would just be 0.
I'm having a lot of problems figuring out how to loop n arguments to use all possible combinations of the + and - operators.
I've managed to build a function that either adds all or subtracts all variables, but I'm stuck on how to approach the various +'s and -'s, especially when considering multiple possible variables.
var sub = 0;
var add = 0;
function sumAll() {
var i;
for (i = 0; i < arguments.length; i++) {
sub -= arguments[i];
}
for (i = 0; i < arguments.length; i++) {
add += arguments[i];
}
return add;
return sub;
};
console.log(add, sub); // just to test the outputs
I'd like to calculate all possible arrangements of + and - for any given number of inputs (always integers, both positive and negative). Suggestions on comparing sums to zero are welcome, though I haven't attempted it yet and would rather try before asking on that part. Thanks.
I'd iterate through the possible bits of a number. Eg, if there are 3 arguments, then there are 3 bits, and the highest number representable by those bits is 2 ** 3 - 1, or 7 (when all 3 bits are set, 111, or 1+2+4). Then, iterate from 0 to 7 and check whether each bit index is set or not.
Eg, on the first iteration, when the number is 0, the bits are 000, which corresponds to +++ - add all 3 arguments up.
On the second iteration, when the number is 1, the bits are 001, which corresponds to -++, so subtract the first argument, and add the other two arguments.
The third iteration would have 2, or 010, or +-+.
The third iteration would have 3, or 011, or +--.
The third iteration would have 4, or 100, or -++.
Continue the pattern until the end, while keeping track of the total closest to zero so far.
You can also return immediately if a subtotal of 0 is found, if you want.
const sumAll = (...args) => {
const limit = 2 ** args.length - 1; // eg, 2 ** 3 - 1 = 7
let totalClosestToZeroSoFar = Infinity;
for (let i = 0; i < limit; i++) {
// eg '000', or '001', or '010', or '011', or '100', etc
const bitStr = i.toString(2).padStart(args.length, '0');
let subtotal = 0;
console.log('i:', i, 'bitStr:', bitStr);
args.forEach((arg, bitPos) => {
if (bitStr[args.length - 1 - bitPos] === '0') {
console.log('+', arg);
subtotal += arg;
} else {
console.log('-', arg);
subtotal -= arg;
}
});
console.log('subtotal', subtotal);
if (Math.abs(subtotal) < Math.abs(totalClosestToZeroSoFar)) {
totalClosestToZeroSoFar = subtotal;
}
}
return totalClosestToZeroSoFar;
};
console.log('final', sumAll(1, 2, 3));
You can "simplify" by replacing the [args.length - 1 - bitPos] with [bitPos] for the same result, but it'll look a bit more confusing - eg 3 (011, or +--), would become 110 (--+).
It's a lot shorter without all the logs that demonstrate that the code is working as desired:
const sumAll = (...args) => {
const limit = 2 ** args.length - 1;
let totalClosestToZeroSoFar = Infinity;
for (let i = 0; i < limit; i++) {
const bitStr = i.toString(2).padStart(args.length, '0');
let subtotal = 0;
args.forEach((arg, bitPos) => {
subtotal += (bitStr[bitPos] === '0' ? -1 : 1) * arg;
});
if (Math.abs(subtotal) < Math.abs(totalClosestToZeroSoFar)) {
totalClosestToZeroSoFar = subtotal;
}
}
return totalClosestToZeroSoFar;
};
console.log('final', sumAll(1, 2, 3));
You can cut the number of operations in half by arbitrarily choosing a sign for the first digit. Eg. currently, with sumAll(9, 1), both an answer of 8 (9 - 1) and -8 (1 - 9) would be valid, because they're both equally close to 0. No matter the input, if +- produces a number closest to 0, then -+ does as well, only with the opposite sign. Similarly, if ++--- produces a number closest to 0, then --+++ does as well, with the opposite sign. By choosing a sign for the first digit, you might be forcing the calculated result to have just one sign, but that won't affect the algorithm's result's distance from 0.
It's not much of an improvement (eg, 10 arguments, 2 ** 10 - 1 -> 1023 iterations improves to 2 ** 9 - 1 -> 511 iterations), but it's something.
const sumAll = (...args) => {
let initialDigit = args.shift();
const limit = 2 ** args.length - 1;
let totalClosestToZeroSoFar = Infinity;
for (let i = 0; i < limit; i++) {
const bitStr = i.toString(2).padStart(args.length, '0');
let subtotal = initialDigit;
args.forEach((arg, bitPos) => {
subtotal += (bitStr[bitPos] === '0' ? -1 : 1) * arg;
});
if (Math.abs(subtotal) < Math.abs(totalClosestToZeroSoFar)) {
totalClosestToZeroSoFar = subtotal;
}
}
return totalClosestToZeroSoFar;
};
console.log('final', sumAll(1, 2, 3));
The variable argument requirement is unrelated to the algorithm, which seems to be the meat of the question. You can use the spread syntax instead of arguments if you wish.
As for the algorithm, if the parameter numbers can be positive or negative, a good place to start is a naive brute force O(2n) algorithm. For each possible operation location, we recurse on adding a plus sign at that location and recurse separately on adding a minus sign. On the way back up the call tree, pick whichever choice ultimately led to an equation that was closest to zero.
Here's the code:
const closeToZero = (...nums) =>
(function addExpr(nums, total, i=1) {
if (i < nums.length) {
const add = addExpr(nums, total + nums[i], i + 1);
const sub = addExpr(nums, total - nums[i], i + 1);
return Math.abs(add) < Math.abs(sub) ? add : sub;
}
return total;
})(nums, nums[0])
;
console.log(closeToZero(1, 17, 6, 10, 15)); // 1 - 17 - 6 + 10 + 15
Now, the question is whether this is performing extra work. Can we find overlapping subproblems? If so, we can memoize previous answers and look them up in a table. The problem is, in part, the negative numbers: it's not obvious how to determine if we're getting closer or further from the target based on a subproblem we've already solved for a given chunk of the array.
I'll leave this as an exercise for the reader and ponder it myself, but it seems likely that there's room for optimization. Here's a related question that might offer some insight in the meantime.
This is also known as a variation of the partition problem, whereby we are looking for a minimal difference between the two parts we have divided the arguments into (e.g., the difference between [1,2] and [3] is zero). Here's one way to enumerate all the differences we can create and pick the smallest:
function f(){
let diffs = new Set([Math.abs(arguments[0])])
for (let i=1; i<arguments.length; i++){
const diffs2 = new Set
for (let d of Array.from(diffs)){
diffs2.add(Math.abs(d + arguments[i]))
diffs2.add(Math.abs(d - arguments[i]))
}
diffs = diffs2
}
return Math.min(...Array.from(diffs))
}
console.log(f(5,3))
console.log(f(1,2,3))
console.log(f(1,2,3,5))
I like to join in on this riddle :)
the issue can be described as fn = fn - 1 + an * xn , where x is of X and a0,...,an is of {-1, 1}
For a single case: X * A = y
For all cases X (*) TA = Y , TA = [An!,...,A0]
Now we have n! different A
//consider n < 32
// name mapping TA: SIGN_STATE_GENERATOR, Y: RESULT_VECTOR, X: INPUT
const INPUT = [1,2,3,3,3,1]
const SIGN_STATE_GENERATOR = (function*(n){
if(n >= 32) throw Error("Its working on UInt32 - max length is 32 in this implementation")
let uint32State = -1 >>> 32-n;
while(uint32State){
yield uint32State--;
}
})(INPUT.length)
const RESULT_VECTOR = []
let SIGN_STATE = SIGN_STATE_GENERATOR.next().value
while (SIGN_STATE){
RESULT_VECTOR.push(
INPUT.reduce(
(a,b, index) =>
a + ((SIGN_STATE >> index) & 1 ? 1 : -1) * b,
0
)
)
SIGN_STATE = SIGN_STATE_GENERATOR.next().value
}
console.log(RESULT_VECTOR)
I spent time working on the ability so apply signs between each item in an array. This feels like the most natural approach to me.
const input1 = [1, 2, 3]
const input2 = [1, 2, 3, -4]
const input3 = [-3, 6, 0, -5, 9]
const input4 = [1, 17, 6, 10, 15]
const makeMatrix = (input, row = [{ sign: 1, number: input[0] }]) => {
if(row.length === input.length) return [ row ]
const number = input[row.length]
return [
...makeMatrix(input, row.concat({ sign: 1, number })),
...makeMatrix(input, row.concat({ sign: -1, number }))
]
}
const checkMatrix = matrix => matrix.reduce((best, row) => {
const current = {
calculation: row.map((item, i) => `${i > 0 ? item.sign === -1 ? "-" : "+" : ""}(${item.number})`).join(""),
value: row.reduce((sum, item) => sum += (item.number * item.sign), 0)
}
return best.value === undefined || Math.abs(best.value) > Math.abs(current.value) ? current : best
})
const processNumbers = input => {
console.log("Generating matrix for:", JSON.stringify(input))
const matrix = makeMatrix(input)
console.log("Testing the following matrix:", JSON.stringify(matrix))
const winner = checkMatrix(matrix)
console.log("Closest to zero was:", winner)
}
processNumbers(input1)
processNumbers(input2)
processNumbers(input3)
processNumbers(input4)

How to round up number to nearest 100/1000 depending on number, in JavaScript?

I have a number that can be in the 2 digits, like 67, 24, 82, or in the 3 digits, like 556, 955, 865, or 4 digits and so on. How can I round up the number to the nearest n+1 digits depending on the number?
Example:
roundup(87) => 100,
roundup(776) => 1000,
roudnup(2333) => 10000
and so on.
You could take the logarithm of ten and round up the value for getting the value.
function roundup(v) {
return Math.pow(10, Math.ceil(Math.log10(v)));
}
console.log(roundup(87)); // 100
console.log(roundup(776)); // 1000
console.log(roundup(2333)); // 10000
For negative numbers, you might save the sign by taking the result of the check as factor or take a negative one. Then an absolute value is necessary, because logarithm works only with positive numbers.
function roundup(v) {
return (v >= 0 || -1) * Math.pow(10, 1 + Math.floor(Math.log10(Math.abs(v))));
}
console.log(roundup(87)); // 100
console.log(roundup(-87)); // -100
console.log(roundup(776)); // 1000
console.log(roundup(-776)); // -1000
console.log(roundup(2333)); // 10000
console.log(roundup(-2333)); // -10000
const roundup = n => 10 ** ("" + n).length
Just use the number of characters.
You can check how many digits are in the number and use exponentiation:
const roundup = num => 10 ** String(num).length;
console.log(roundup(87));
console.log(roundup(776));
console.log(roundup(2333));
You can use String#repeat combined with Number#toString in order to achieve that :
const roundUp = number => +('1'+'0'.repeat(number.toString().length));
console.log(roundUp(30));
console.log(roundUp(300));
console.log(roundUp(3000));
//Math.pow(10,(value+"").length)
console.log(Math.pow(10,(525+"").length))
console.log(Math.pow(10,(5255+"").length))
I came up with another solution that does'nt require a new function to be created

Categories

Resources