Related
So, I have this problem I need to solve, apparently this is called Subset Sum Problem, except I need to get the subset not only when is exact to the given number, but the closest in case there is no exact sum that reaches the given number, it shouldn’t go over the reference number, only below, also if there are more than two possible subsets with the same result, I'd like to get the subset with the better distribution, from the highest to lowest number in the array as preferred, and limiting each subset to not overpass 10 times the same number, repeating is allowed, for example:
Here is the array with the predefined values:
let num = [64.20, 107, 535, 1070];
and a given number:
let investment = 806.45
One possible solution would be:
[0, 2, 1, 0] // this sums up to 749 (since there is no way to get to 806.45 with the given array)
Please notice this result is referring to how many times each value in nums is allowed to reach the sum:
But a better solution would be:
[4, 5, 0, 0] // this sums up to 791.80 (since there is no way to get to 806.45 with the given array)
And an even better solution (because takes in consideration the higher values over the lower ones first)
[4, 0, 1, 0] // this sums up to 791.80 also but you can see it's taking a higher value when possible.
Another important restriction would be that should never give a negative result.
So far i have tried like this (in VueJS):
getPackages(){
let investment = 806.45;
const num = [64.20, 107, 535, 1070]
let a, b, c, d;
let results = [];
a = investment / num[0] >= 0 ? (investment/num[0]) : 0;
b = investment / num[1] >= 0 ? (investment/num[1]) : 0;
c = investment / num[2] >= 0 ? (investment/num[2]) : 0;
d = investment / num[3] >= 0 ? (investment/num[3]) : 0;
let dResult = [], cResult = [], bResult = [], aResult = [];
for (let i = 0; i <= d; i++){
if (i>0){
dResult.push((i * num[3]))
}
}
for (let i = 0; i <= c; i++){
if (i>0){
cResult.push((i * num[2]))
}
}
for (let i = 0; i <= b; i++){
if (i>0){
bResult.push((i * num[1]))
}
}
for (let i = 0; i <= a; i++){
if (i>0){
aResult.push((i * num[0]))
}
}
let aResultCoincidences = [];
let bResultCoincidences = [];
let cResultCoincidences = [];
let dResultCoincidences = [];
bResult.forEach(value => {
aResult.findIndex(item => item === value) > 0 ? bResultCoincidences.push(aResult.findIndex(item => item === value)) : null
})
aResult.splice(0, Math.max(...bResultCoincidences) + 1)
cResult.forEach(value => {
bResult.findIndex(item => item === value) > 0 ? cResultCoincidences.push(bResult.findIndex(item => item === value)) : null
})
bResult.splice(0, Math.max(...cResultCoincidences) + 1)
dResult.forEach(value => {
cResult.findIndex(item => item === value) > 0 ? dResultCoincidences.push(cResult.findIndex(item => item === value)) : null
})
cResult.splice(0, Math.max(...dResultCoincidences) + 1)
this.package1 = aResult.length
this.package2 = bResult.length
this.package3 = cResult.length
this.package4 = dResult.length
},
What happens in my approach is that I try to get all possible results from each multiplication, and then I remove the ones that matches between the arrays I made with this combination, to finally get the result, but this is not well optimized, and I'm sure there is probably a better solution to this problem.
Anyway ignore the vuejs implementation, that's only to set the values in the DOM.
***ES6 solution would be awesome.
CodeSandbox to play around: CODESANDBOX LINK
Thanks in advance.
Here's an approach. I didn't look yours over carefully, and don't know if this offers any advantages over it.
It is a brute-force approach, simply taking the cross-product of the potential individual values for each category, summing the totals, and then reducing the list to find all the options closest to the target. I originally wrote this slightly differently, trying to capture the closest value whether over or under the target.
Here's an implementation with several helper functions:
const sum = (ns) =>
ns .reduce ((a, b) => a + b, 0)
const crossproduct = (xss) =>
xss.reduce((xs, ys) => xs.flatMap(x => ys.map(y => [...x, y])), [[]])
const range = (lo, hi) =>
[...Array(hi - lo)] .map ((_, i) => lo + i)
const call = (fn, ...args) =>
fn (...args)
const closestSums = (t, ns) =>
call (
(opts = crossproduct (ns .map (n => range (0, 1 + Math .ceil (t / n))))) =>
opts .map (xs => [xs, sum (xs .map ((x, i) => x * ns [i]))])
.reduce (
({best, opts}, [opt, tot]) =>
call (
(diff = t - tot) =>
diff >= 0 && diff < best
? {best: diff, opts: [opt]}
: diff >= 0 && diff == best
? {best, opts: [...opts, opt]}
: {best, opts}
),
{best: Infinity, opts: []}
) .opts
)
const byHigher = (as, bs) =>
as .reduceRight ((r, _, i) => r || (as[i] < bs[i] ? 1 : as[i] > bs[i] ? -1 : 0), 0)
const closestCounts = (t, ns) =>
closestSums (t * 100, ns .map (n => 100 * n))
.filter (cs => cs.every(c => c <= 10))
.sort (byHigher)
console .log (
closestCounts (806.45, [64.20, 107, 535, 1070]),
closestCounts (791.8, [64.20, 107, 535, 1070]) // exact match
)
.as-console-wrapper {max-height: 100% !important; top: 0}
Note that the wrapper multiplies everything by 100 to rid us of decimals and the potential floating point rounding errors. If that won't do, it would be easy enough to add some tolerance for matching.
The final sorting is naïve, assuming that your values are in ascending numeric order. If not, that sorter would have to get more sophisticated.
As I said, this is not likely to be efficient, and it might not gain anything on your version, but it's at least a different approach.
Lets first consider the 2-varible case. We want to find two numbers x1, x2 such that
f(x1,x2) = 64.20 * x1 + 107 * x2 - 806.45
If we allow x1, x2 to be real numbers then this is just the equation of a line. In the problem we only want positive integer solutions, i.e. the grid points. I've badly drawn the grid points with those above the line in red and those below the line in blue. The problem is then finding grid point closest to the line.
Note that there are a lot of points we never need to consider, only the red and blue grid point are possible candidates.
Generating the coloured grid points is quite simple. We could loop through the x1 values from 0 to 13, calculate the real x2 value by solving the equation
x2 = (806.45 - 64.20 * x1)/107
and finding the floor(x2) and ceil(x2). Slightly better is looping through the x2 which run from 0 to 8 solving for x1
x1 = (806.45 - 107 * x2)/64.20.
Another approach might be some kind of zero following routine. If you are at a given grid point (x1,x2) calculate f(x1,x2) if it is less than 0 we need to consider (x1+1,x2) or (x1,x2+1), if f(x1,x2) is greater than zero consider (x1-1,x2) or (x1,x2-x1). I don't think the complication of this approach really brings any benefit.
If we now move to 3D, we have an equation in three variables, x1, x2, x3
f(x1,x2,x3) = 64.20 * x1 + 107 * x2 + 535 * x3 - 806.45
This defines a plane in 3D, requiring all variables to be non-negative restricts it to a triangular part of the plane.
To find candidate points here we could loop through possible interger pairs (x1,x2), then find the exact x3 value
x3 = (806.45 - 64.20 * x1 - 107 * x2)/ 535
and its floor and ceiling. The candidate (x1,x2) only line in a candidate triangle so we can use the following procedure.
// First find max possible x1
x1_max = ceil(806.45/64.20)
for(x1 = 0; x1< x1_max; ++x1) {
// for a given x1 find the maximum x2
x2_max = ceil((806.45 - 64.20*x1)/107)
for(x2=0;x2<x2_max;++x2) {
// for a given (x1,x2) find the exact x3
x3 = (806.45 - 64.20 * x1 - 107 * x2)/ 535
// and its floor and ceiling
x3l = floor(x3); x3h = ceil(x3);
add_to_candidates(x1,x2,x3l);
add_to_candidates(x1,x2,x3h);
}
}
Once you can candidates simply select the one with the smallest absolute value.
A similar idea would extend to more variables.
Suppose I have the following factors:
(1+3x)(1+x)(1+2x)
Expanded to a polynomial, it looks like:
1 + 6x + 11x^2 + 6x^3
The coefficients of this polynomial would be
c0 = 1
c1 = 6
c2 = 11
c3 = 6
I'm trying to figure out how to calculate these rapidly (for any set of factors). The ideal output would be an array of the coefficients, like
var coeff = [c0,c1,c2,c3];
What I'm trying to do is find a way to quickly go from the factors to the array of coefficients. Any suggestions on how to rapidly handle this in javascript? And for the sake of clarity, I'm trying to figure out how to do this for any set of n factors, not just this particular scenario.
You could use the factors as vector and use a cross product for the result.
function multiply(a1, a2) {
var result = [];
a1.forEach(function (a, i) {
a2.forEach(function (b, j) {
result[i + j] = (result[i + j] || 0) + a * b;
});
});
return result;
}
var data = [[1, 3], [1, 1], [1, 2]], // (1+3x)(1+x)(1+2x)
result = data.reduce(multiply);
console.log(result); // [1, 6, 11, 6] = 1x^0 + 6x^1 + 11x^2 + 6x^3
Well I found a method to do what you want from start to finish even without the need for any treatment in the original factors. Although I had to use a Math library. I found there is at least a library that does what you want: Nerdamer
As you can see from the code below the coeficients are correctly calculated from the factors you gave.
var factors = '(1+3x)(1+x)(1+2x)';
console.log('original factors: ' + factors);
var y = nerdamer('expand(' + factors + ')');
var polynomialform = y.toString();
console.log('polynomial form: ' + polynomialform);
var coef = polynomialform.split('+').map(v=>v.trim()).map(v=>v.split('x')[0]).map(v=>v.replace(/^\*+|\*+$/g, ''));
console.log('coeficients: ' + coef);
<script src="http://nerdamer.com/js/nerdamer.core.js"></script>
Notice that coefs var is an array.
Obviously, by the way I otained the coeficients, the operation may fail in different factor cases. This has to be adapted for minus characters and edge cases. You can create some kind of loop and put failed calculations in an array to check for edge cases to adapt the code for the full dataset. I can improve the answer if you provide a larger test dataset.
Hope it helps you.
Here's my take based on the fact that when you multiply (1+ax) by (1+b_1*x+b_2*x^2+...+b_nx^n), in the resulting polynomial (of degree n+1), the first term's coefficient will be one and its last term's coefficient will be a*b_n.
I think it is a bit simpler than the accepted answer, but still quadratic in time. To make this more efficient, you will need more advanced techniques.
function multOne(a, b) {
var n = b.length;
var r = [1]; //The first term is always 1
r[n] = a * b[n - 1]; //The last term is always a*b_n-1
for (var i = 1; i < n; i++)
r[i] = b[i] + a * b[i - 1];
return r;
}
function solve(c) {
var result = [1, c[0]]; //use result as an accumulator
for (var j = 1; j < c.length; j++)
result = multOne(c[j], result);
return result;
}
console.log(solve([3, 1, 2])); //You don't need to pass 1s either. Just pass the varying coefficients
I needed something similar (to calculate permutations via exponential generating functions) so I wrote a version that works when some terms missing, by using objects as the base instead. I also needed it not not calculate anything over a certain power, so that's also an option
/**
* Calculates the result of multiplying many polynomials together
* #example
* polynomials = [{0: 1, 1: 10, 2:1}, {0: 0, 1: 5, 2: 0, 3: 0.5}]
* limit = 4;
* polynomialMultiplication(polynomials, limit);
* #param {Array.<Object.<number,number>>} polynomials an array of polynomials,
* each expressed as an array of term coefficients
* #param {number} limit the maximum term to calculate
* #returns the resultant polynomial from multiplying all polynomials together
*/
function polynomialMultiplication(polynomials, limit) {
const length = limit ?? polynomials.reduce((sum, poly) => sum += Math.max(...Object.keys(poly)), 0)
// make an object to hold the result in
// which is prepopulated by zeros
const template = { ...Array.from({
length
}).fill(0)
};
return polynomials.reduce((memo, poly, polyIndex) => {
const tempCopy = { ...template};
if (polyIndex === 0) return { ...poly };
for (let termIndex = 0; termIndex < length && termIndex <= Math.max(...Object.keys(poly)); termIndex++) {
for (let memoIndex = 0;
(memoIndex + termIndex) <= length && memoIndex <= Math.max(...Object.keys(memo)); memoIndex++) {
const addition = (memo[memoIndex] ?? 0) * (poly[termIndex] ?? 0);
const copyIndex = memoIndex + termIndex;
tempCopy[copyIndex] = (tempCopy[copyIndex] ?? 0) + addition;
}
}
return tempCopy;
}, template)
}
console.log('(1+3x)(1+x)(1+2x)');
const polynomials = [{
0: 1,
1: 3
}, {
0: 1,
1: 1
}, {
0: 1,
1: 2
}];
console.log(polynomialMultiplication(polynomials));
const morePolynomials = [{
0: 1,
1: 0,
2: 5
}, {
0: 0,
1: 6
}, {
0: 1,
1: 2,
2: 0
}];
console.log('(1+5x^2)(6x)(1+2x) up to x^2')
console.log(polynomialMultiplication(morePolynomials, 2));
How can I generate some unique random numbers between 1 and 100 using JavaScript?
For example: To generate 8 unique random numbers and store them to an array, you can simply do this:
var arr = [];
while(arr.length < 8){
var r = Math.floor(Math.random() * 100) + 1;
if(arr.indexOf(r) === -1) arr.push(r);
}
console.log(arr);
Populate an array with the numbers 1 through 100.
Shuffle it.
Take the first 8 elements of the resulting array.
Modern JS Solution using Set (and average case O(n))
const nums = new Set();
while(nums.size !== 8) {
nums.add(Math.floor(Math.random() * 100) + 1);
}
console.log([...nums]);
Another approach is to generate an 100 items array with ascending numbers and sort it randomly. This leads actually to a really short and (in my opinion) simple snippet.
const numbers = Array(100).fill().map((_, index) => index + 1);
numbers.sort(() => Math.random() - 0.5);
console.log(numbers.slice(0, 8));
Generate permutation of 100 numbers and then choose serially.
Use Knuth Shuffle(aka the Fisher-Yates shuffle) Algorithm.
JavaScript:
function fisherYates ( myArray,stop_count ) {
var i = myArray.length;
if ( i == 0 ) return false;
int c = 0;
while ( --i ) {
var j = Math.floor( Math.random() * ( i + 1 ) );
var tempi = myArray[i];
var tempj = myArray[j];
myArray[i] = tempj;
myArray[j] = tempi;
// Edited thanks to Frerich Raabe
c++;
if(c == stop_count)return;
}
}
CODE COPIED FROM LINK.
EDIT:
Improved code:
function fisherYates(myArray,nb_picks)
{
for (i = myArray.length-1; i > 1 ; i--)
{
var r = Math.floor(Math.random()*i);
var t = myArray[i];
myArray[i] = myArray[r];
myArray[r] = t;
}
return myArray.slice(0,nb_picks);
}
Potential problem:
Suppose we have array of 100 numbers {e.g. [1,2,3...100]} and we stop swapping after 8 swaps;
then most of the times array will look like {1,2,3,76,5,6,7,8,...numbers here will be shuffled ...10}.
Because every number will be swapped with probability 1/100 so
prob. of swapping first 8 numbers is 8/100 whereas prob. of swapping other 92 is 92/100.
But if we run algorithm for full array then we are sure (almost)every entry is swapped.
Otherwise we face a question : which 8 numbers to choose?
The above techniques are good if you want to avoid a library, but depending if you would be alright with a library, I would suggest checking out Chance for generating random stuff in JavaScript.
Specifically to solve your question, using Chance it's as easy as:
// One line!
var uniques = chance.unique(chance.natural, 8, {min: 1, max: 100});
// Print it out to the document for this snippet so we can see it in action
document.write(JSON.stringify(uniques));
<script src="http://chancejs.com/chance.min.js"></script>
Disclaimer, as the author of Chance, I am a bit biased ;)
To avoid any long and unreliable shuffles, I'd do the following...
Generate an array that contains the number between 1 and 100, in order.
Generate a random number between 1 and 100
Look up the number at this index in the array and store in your results
Remove the elemnt from the array, making it one shorter
Repeat from step 2, but use 99 as the upper limit of the random number
Repeat from step 2, but use 98 as the upper limit of the random number
Repeat from step 2, but use 97 as the upper limit of the random number
Repeat from step 2, but use 96 as the upper limit of the random number
Repeat from step 2, but use 95 as the upper limit of the random number
Repeat from step 2, but use 94 as the upper limit of the random number
Repeat from step 2, but use 93 as the upper limit of the random number
Voila - no repeated numbers.
I may post some actual code later, if anybody is interested.
Edit: It's probably the competitive streak in me but, having seen the post by #Alsciende, I couldn't resist posting the code that I promised.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html>
<head>
<title>8 unique random number between 1 and 100</title>
<script type="text/javascript" language="Javascript">
function pick(n, min, max){
var values = [], i = max;
while(i >= min) values.push(i--);
var results = [];
var maxIndex = max;
for(i=1; i <= n; i++){
maxIndex--;
var index = Math.floor(maxIndex * Math.random());
results.push(values[index]);
values[index] = values[maxIndex];
}
return results;
}
function go(){
var running = true;
do{
if(!confirm(pick(8, 1, 100).sort(function(a,b){return a - b;}))){
running = false;
}
}while(running)
}
</script>
</head>
<body>
<h1>8 unique random number between 1 and 100</h1>
<p><button onclick="go()">Click me</button> to start generating numbers.</p>
<p>When the numbers appear, click OK to generate another set, or Cancel to stop.</p>
</body>
I would do this:
function randomInt(min, max) {
return Math.round(min + Math.random()*(max-min));
}
var index = {}, numbers = [];
for (var i=0; i<8; ++i) {
var number;
do {
number = randomInt(1, 100);
} while (index.hasOwnProperty("_"+number));
index["_"+number] = true;
numbers.push(number);
}
delete index;
This is a very generic function I have written to generate random unique/non-unique integers for an array. Assume the last parameter to be true in this scenario for this answer.
/* Creates an array of random integers between the range specified
len = length of the array you want to generate
min = min value you require
max = max value you require
unique = whether you want unique or not (assume 'true' for this answer)
*/
function _arrayRandom(len, min, max, unique) {
var len = (len) ? len : 10,
min = (min !== undefined) ? min : 1,
max = (max !== undefined) ? max : 100,
unique = (unique) ? unique : false,
toReturn = [], tempObj = {}, i = 0;
if(unique === true) {
for(; i < len; i++) {
var randomInt = Math.floor(Math.random() * ((max - min) + min));
if(tempObj['key_'+ randomInt] === undefined) {
tempObj['key_'+ randomInt] = randomInt;
toReturn.push(randomInt);
} else {
i--;
}
}
} else {
for(; i < len; i++) {
toReturn.push(Math.floor(Math.random() * ((max - min) + min)));
}
}
return toReturn;
}
Here the 'tempObj' is a very useful obj since every random number generated will directly check in this tempObj if that key already exists, if not, then we reduce the i by one since we need 1 extra run since the current random number already exists.
In your case, run the following
_arrayRandom(8, 1, 100, true);
That's all.
Shuffling the numbers from 1 to 100 is the right basic strategy, but if you need only 8 shuffled numbers, there's no need to shuffle all 100 numbers.
I don't know Javascript very well, but I believe it's easy to create an array of 100 nulls quickly. Then, for 8 rounds, you swap the n'th element of the array (n starting at 0) with a randomly selected element from n+1 through 99. Of course, any elements not populated yet mean that the element would really have been the original index plus 1, so that's trivial to factor in. When you're done with the 8 rounds, the first 8 elements of your array will have your 8 shuffled numbers.
var arr = []
while(arr.length < 8){
var randomnumber=Math.ceil(Math.random()*100)
if(arr.indexOf(randomnumber) === -1){arr.push(randomnumber)}
}
document.write(arr);
shorter than other answers I've seen
Implementing this as a generator makes it pretty nice to work with. Note, this implementation differs from ones that require the entire input array to be shuffled first.
This sample function works lazily, giving you 1 random item per iteration up to N items you ask for. This is nice because if you just want 3 items from a list of 1000, you don't have to touch all 1000 items first.
// sample :: Integer -> [a] -> [a]
const sample = n => function* (xs) {
let ys = xs.slice(0);
let len = xs.length;
while (n > 0 && len > 0) {
let i = (Math.random() * len) >> 0;
yield ys.splice(i,1)[0];
n--; len--;
}
}
// example inputs
let items = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
let numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
// get 3 random items
for (let i of sample(3) (items))
console.log(i); // f g c
// partial application
const lotto = sample(3);
for (let i of lotto(numbers))
console.log(i); // 3 8 7
// shuffle an array
const shuffle = xs => Array.from(sample (Infinity) (xs))
console.log(shuffle(items)) // [b c g f d e a]
I chose to implement sample in a way that does not mutate the input array, but you could easily argue that a mutating implementation is favourable.
For example, the shuffle function might wish to mutate the original input array. Or you might wish to sample from the same input at various times, updating the input each time.
// sample :: Integer -> [a] -> [a]
const sample = n => function* (xs) {
let len = xs.length;
while (n > 0 && len > 0) {
let i = (Math.random() * len) >> 0;
yield xs.splice(i,1)[0];
n--; len--;
}
}
// deal :: [Card] -> [Card]
const deal = xs => Array.from(sample (2) (xs));
// setup a deck of cards (13 in this case)
// cards :: [Card]
let cards = 'A234567890JQK'.split('');
// deal 6 players 2 cards each
// players :: [[Card]]
let players = Array.from(Array(6), $=> deal(cards))
console.log(players);
// [K, J], [6, 0], [2, 8], [Q, 7], [5, 4], [9, A]
// `cards` has been mutated. only 1 card remains in the deck
console.log(cards);
// [3]
sample is no longer a pure function because of the array input mutation, but in certain circumstances (demonstrated above) it might make more sense.
Another reason I chose a generator instead of a function that just returns an array is because you may want to continue sampling until some specific condition.
Perhaps I want the first prime number from a list of 1,000,000 random numbers.
"How many should I sample?" – you don't have to specify
"Do I have to find all the primes first and then select a random prime?" – Nope.
Because we're working with a generator, this task is trivial
const randomPrimeNumber = listOfNumbers => {
for (let x of sample(Infinity) (listOfNumbers)) {
if (isPrime(x))
return x;
}
return NaN;
}
This will continuously sample 1 random number at a time, x, check if it's prime, then return x if it is. If the list of numbers is exhausted before a prime is found, NaN is returned.
Note:
This answer was originally shared on another question that was closed as a duplicate of this one. Because it's very different from the other solutions provided here, I've decided to share it here as well
var numbers = [];
for (let i = 0; i < 8; i++) {
let a = true,
n;
while(a) {
n = Math.floor(Math.random() * 100) + 1;
a = numbers.includes(n);
}
numbers.push(n);
}
console.log(numbers);
Same permutation algorithm as The Machine Charmer, but with a prototyped implementation. Better suited to large number of picks. Uses js 1.7 destructuring assignment if available.
// swaps elements at index i and j in array this
// swapping is easy on js 1.7 (feature detection)
Array.prototype.swap = (function () {
var i=0, j=1;
try { [i,j]=[j,i]; }
catch (e) {}
if(i) {
return function(i,j) {
[this[i],this[j]] = [this[j],this[i]];
return this;
}
} else {
return function(i,j) {
var temp = this[i];
this[i] = this[j];
this[j] = temp;
return this;
}
}
})();
// shuffles array this
Array.prototype.shuffle = function() {
for(var i=this.length; i>1; i--) {
this.swap(i-1, Math.floor(i*Math.random()));
}
return this;
}
// returns n unique random numbers between min and max
function pick(n, min, max) {
var a = [], i = max;
while(i >= min) a.push(i--);
return a.shuffle().slice(0,n);
}
pick(8,1,100);
Edit:
An other proposition, better suited to small number of picks, based on belugabob's answer. To guarantee uniqueness, we remove the picked numbers from the array.
// removes n random elements from array this
// and returns them
Array.prototype.pick = function(n) {
if(!n || !this.length) return [];
var i = Math.floor(this.length*Math.random());
return this.splice(i,1).concat(this.pick(n-1));
}
// returns n unique random numbers between min and max
function pick(n, min, max) {
var a = [], i = max;
while(i >= min) a.push(i--);
return a.pick(n);
}
pick(8,1,100);
for arrays with holes like this [,2,,4,,6,7,,]
because my problem was to fill these holes. So I modified it as per my need :)
the following modified solution worked for me :)
var arr = [,2,,4,,6,7,,]; //example
while(arr.length < 9){
var randomnumber=Math.floor(Math.random()*9+1);
var found=false;
for(var i=0;i<arr.length;i++){
if(arr[i]==randomnumber){found=true;break;}
}
if(!found)
for(k=0;k<9;k++)
{if(!arr[k]) //if it's empty !!MODIFICATION
{arr[k]=randomnumber; break;}}
}
alert(arr); //outputs on the screen
The best earlier answer is the answer by sje397. You will get as good random numbers as you can get, as quick as possible.
My solution is very similar to his solution. However, sometimes you want the random numbers in random order, and that is why I decided to post an answer. In addition, I provide a general function.
function selectKOutOfN(k, n) {
if (k>n) throw "k>n";
var selection = [];
var sorted = [];
for (var i = 0; i < k; i++) {
var rand = Math.floor(Math.random()*(n - i));
for (var j = 0; j < i; j++) {
if (sorted[j]<=rand)
rand++;
else
break;
}
selection.push(rand);
sorted.splice(j, 0, rand);
}
return selection;
}
alert(selectKOutOfN(8, 100));
Here is my ES6 version I cobbled together. I'm sure it can be a little more consolidated.
function randomArray(i, min, max) {
min = Math.ceil(min);
max = Math.floor(max);
let arr = Array.from({length: i}, () => Math.floor(Math.random()* (max - min)) + min);
return arr.sort();
}
let uniqueItems = [...new Set(randomArray(8, 0, 100))]
console.log(uniqueItems);
How about using object properties as a hash table? This way your best scenario is to only randomize 8 times. It would only be effective if you want a small part of the range of numbers. It's also much less memory intensive than Fisher-Yates because you don't have to allocate space for an array.
var ht={}, i=rands=8;
while ( i>0 || keys(ht).length<rands) ht[Math.ceil(Math.random()*100)]=i--;
alert(keys(ht));
I then found out that Object.keys(obj) is an ECMAScript 5 feature so the above is pretty much useless on the internets right now. Fear not, because I made it ECMAScript 3 compatible by adding a keys function like this.
if (typeof keys == "undefined")
{
var keys = function(obj)
{
props=[];
for (k in ht) if (ht.hasOwnProperty(k)) props.push(k);
return props;
}
}
var bombout=0;
var checkArr=[];
var arr=[];
while(arr.length < 8 && bombout<100){
bombout++;
var randomNumber=Math.ceil(Math.random()*100);
if(typeof checkArr[randomNumber] == "undefined"){
checkArr[randomNumber]=1;
arr.push(randomNumber);
}
}
// untested - hence bombout
if you need more unique you must generate a array(1..100).
var arr=[];
function generateRandoms(){
for(var i=1;i<=100;i++) arr.push(i);
}
function extractUniqueRandom()
{
if (arr.length==0) generateRandoms();
var randIndex=Math.floor(arr.length*Math.random());
var result=arr[randIndex];
arr.splice(randIndex,1);
return result;
}
function extractUniqueRandomArray(n)
{
var resultArr=[];
for(var i=0;i<n;i++) resultArr.push(extractUniqueRandom());
return resultArr;
}
above code is faster:
extractUniqueRandomArray(50)=>
[2, 79, 38, 59, 63, 42, 52, 22, 78, 50, 39, 77, 1, 88, 40, 23, 48, 84, 91, 49, 4, 54, 93, 36, 100, 82, 62, 41, 89, 12, 24, 31, 86, 92, 64, 75, 70, 61, 67, 98, 76, 80, 56, 90, 83, 44, 43, 47, 7, 53]
Adding another better version of same code (accepted answer) with JavaScript 1.6 indexOf function. Do not need to loop thru whole array every time you are checking the duplicate.
var arr = []
while(arr.length < 8){
var randomnumber=Math.ceil(Math.random()*100)
var found=false;
if(arr.indexOf(randomnumber) > -1){found=true;}
if(!found)arr[arr.length]=randomnumber;
}
Older version of Javascript can still use the version at top
PS: Tried suggesting an update to the wiki but it was rejected. I still think it may be useful for others.
This is my personal solution :
<script>
var i, k;
var numbers = new Array();
k = Math.floor((Math.random()*8));
numbers[0]=k;
for (var j=1;j<8;j++){
k = Math.floor((Math.random()*8));
i=0;
while (i < numbers.length){
if (numbers[i] == k){
k = Math.floor((Math.random()*8));
i=0;
}else {i++;}
}
numbers[j]=k;
}
for (var j=0;j<8;j++){
alert (numbers[j]);
}
</script>
It randomly generates 8 unique array values (between 0 and 7), then displays them using an alert box.
function getUniqueRandomNos() {
var indexedArrayOfRandomNo = [];
for (var i = 0; i < 100; i++) {
var randNo = Math.random();
indexedArrayOfRandomNo.push([i, randNo]);
}
indexedArrayOfRandomNo.sort(function (arr1, arr2) {
return arr1[1] - arr2[1]
});
var uniqueRandNoArray = [];
for (i = 0; i < 8; i++) {
uniqueRandNoArray.push(indexedArrayOfRandomNo[i][0]);
}
return uniqueRandNoArray;
}
I think this method is different from methods given in most of the answers, so I thought I might add an answer here (though the question was asked 4 years ago).
We generate 100 random numbers, and tag each of them with numbers from 1 to 100. Then we sort these tagged random numbers, and the tags get shuffled randomly. Alternatively, as needed in this question, one could do away with just finding top 8 of the tagged random numbers. Finding top 8 items is cheaper than sorting the whole array.
One must note here, that the sorting algorithm influences this algorithm. If the sorting algorithm used is stable, there is slight bias in favor of smaller numbers. Ideally, we would want the sorting algorithm to be unstable and not even biased towards stability (or instability) to produce an answer with perfectly uniform probability distribution.
This can handle generating upto 20 digit UNIQUE random number
JS
var generatedNumbers = [];
function generateRandomNumber(precision) { // input --> number precision in integer
if (precision <= 20) {
var randomNum = Math.round(Math.random().toFixed(precision) * Math.pow(10, precision));
if (generatedNumbers.indexOf(randomNum) > -1) {
if (generatedNumbers.length == Math.pow(10, precision))
return "Generated all values with this precision";
return generateRandomNumber(precision);
} else {
generatedNumbers.push(randomNum);
return randomNum;
}
} else
return "Number Precision shoould not exceed 20";
}
generateRandomNumber(1);
jsFiddle
This solution uses the hash which is much more performant O(1) than checking if the resides in the array. It has extra safe checks too. Hope it helps.
function uniqueArray(minRange, maxRange, arrayLength) {
var arrayLength = (arrayLength) ? arrayLength : 10
var minRange = (minRange !== undefined) ? minRange : 1
var maxRange = (maxRange !== undefined) ? maxRange : 100
var numberOfItemsInArray = 0
var hash = {}
var array = []
if ( arrayLength > (maxRange - minRange) ) throw new Error('Cannot generate unique array: Array length too high')
while(numberOfItemsInArray < arrayLength){
// var randomNumber = Math.floor(Math.random() * (maxRange - minRange + 1) + minRange)
// following line used for performance benefits
var randomNumber = (Math.random() * (maxRange - minRange + 1) + minRange) << 0
if (!hash[randomNumber]) {
hash[randomNumber] = true
array.push(randomNumber)
numberOfItemsInArray++
}
}
return array
}
document.write(uniqueArray(1, 100, 8))
You can also do it with a one liner like this:
[...((add, set) => add(set, add))((set, add) => set.size < 8 ? add(set.add(Math.floor(Math.random()*100) + 1), add) : set, new Set())]
getRandom (min, max) {
return Math.floor(Math.random() * (max - min)) + min
}
getNRandom (min, max, n) {
const numbers = []
if (min > max) {
return new Error('Max is gt min')
}
if (min === max) {
return [min]
}
if ((max - min) >= n) {
while (numbers.length < n) {
let rand = this.getRandom(min, max + 1)
if (numbers.indexOf(rand) === -1) {
numbers.push(rand)
}
}
}
if ((max - min) < n) {
for (let i = min; i <= max; i++) {
numbers.push(i)
}
}
return numbers
}
Using a Set is your fastest option. Here is a generic function for getting a unique random that uses a callback generator. Now it's fast and reusable.
// Get a unique 'anything'
let unique = new Set()
function getUnique(generator) {
let number = generator()
while (!unique.add(number)) {
number = generator()
}
return number;
}
// The generator. Return anything, not just numbers.
const between_1_100 = () => 1 + Math.floor(Math.random() * 100)
// Test it
for (var i = 0; i < 8; i++) {
const aNumber = getUnique(between_1_100)
}
// Dump the 'stored numbers'
console.log(Array.from(unique))
This is a implementation of Fisher Yates/Durstenfeld Shuffle, but without actual creation of a array thus reducing space complexity or memory needed, when the pick size is small compared to the number of elements available.
To pick 8 numbers from 100, it is not necessary to create a array of 100 elements.
Assuming a array is created,
From the end of array(100), get random number(rnd) from 1 to 100
Swap 100 and the random number rnd
Repeat step 1 with array(99)
If a array is not created, A hashMap may be used to remember the actual swapped positions. When the second random number generated is equal to the one of the previously generated numbers, the map provides the current value in that position rather than the actual value.
const getRandom_ = (start, end) => {
return Math.floor(Math.random() * (end - start + 1)) + start;
};
const getRealValue_ = (map, rnd) => {
if (map.has(rnd)) {
return getRealValue_(map, map.get(rnd));
} else {
return rnd;
}
};
const getRandomNumbers = (n, start, end) => {
const out = new Map();
while (n--) {
const rnd = getRandom_(start, end--);
out.set(getRealValue_(out, rnd), end + 1);
}
return [...out.keys()];
};
console.info(getRandomNumbers(8, 1, 100));
console.info(getRandomNumbers(8, 1, Math.pow(10, 12)));
console.info(getRandomNumbers(800000, 1, Math.pow(10, 15)));
Here is an example of random 5 numbers taken from a range of 0 to 100 (both 0 and 100 included) with no duplication.
let finals = [];
const count = 5; // Considering 5 numbers
const max = 100;
for(let i = 0; i < max; i++){
const rand = Math.round(Math.random() * max);
!finals.includes(rand) && finals.push(rand)
}
finals = finals.slice(0, count)
I have to write the following weighted average formula in JavaScript:
Average = (p1*p2*x1 + p3*p4*x2 + ... +p(n-2)*p(n-1)*xn) / (p1*p2 + p3*p4 + ... + p(n-2)p(n-1) )
The formula gives the average of x values.
I also have an array populated with n elements in JavaScript:
Array = (p1,p2,x1,p3,p4,x2....)
...where pi are the weights and xi the values I want to find the average for.
How can I write the formula using this array?
I would probably use the following strategy:
Create two new arrays (probably weights and values).
Iterate over the original array in steps of 3; multiplying the pn's and pushing the result into weights and pushing the xn into values.
Iterate over the new arrays, creating the weighted total (the left hand of the division) and the total weight (right hand of the division).
Divide one by the other. Done.
In other words, something like this:
function weighted_average(input) {
var weights = [];
var values = [];
var weighted_total = 0;
var total_weight = 0;;
if (input.length % 3 !== 0) {
throw new Error("Input array length is not a multiple of 3.");
}
for (var i = 0; i < input.length; i += 3) {
weights.push(input[i] * input[i + 1]);
values.push(input[i + 2]);
}
for (var i = 0; i < weights.length; i += 1) {
weighted_total += weights[i] * values[i];
total_weight += weights[i];
}
return weighted_total / total_weight;
}
You'll have to verify whether this does exactly what you're after, though. No guarantees. ;)
JSFiddle demo: jsfiddle.net/Z8seZ
Of course, you could skip the intermediary arrays to make it a bit faster. But the above is more explicit and more readable, and therefore more maintainable (e.g. you could easily split out the actual algorithm and create different "wrapper" functions for different forms of input). I would only optimize it if working with (really) large data sets.
Here's a functional approach, requiring ES5:
var w = a.unzip(3).map(function(v, i, a) {
var weight = v[0] * v[1];
var sum = weight * v[2];
return [sum, weight];
}).reduce(function(p, c, i, a) {
return [p[0] + c[0], p[1] + c[1]];
}, [0, 0]);
var aw = w[0] / w[1];
which in pseudo-code is:
split the array into chunks of three
convert each three [p1, p2, x ] into a pair [ p1 * p2 * x , p1 * p2 ]
sum the pairs (along the array, not within each pair)
divide one by the other
and where the (non-standard) unzip function which chunks the array is:
Object.defineProperty(Array.prototype, 'unzip', {
value: function(n) {
n = n || 2;
return this.reduce(function(p, c, i, a) {
if (i % n === 0) {
p.push(a.slice(i, i + n));
}
return p;
}, []);
}
});
ES6 one-liner for an array of objects xs containing the keys w as weight and v as value:
((_w, _v) => _v / _w)(...xs.reduce((r, o) => [r[0] + o[w], r[1] + o[w] * o[v]], [0, 0]))
Want to improve this post? Provide detailed answers to this question, including citations and an explanation of why your answer is correct. Answers without enough detail may be edited or deleted.
This question already has answers here:
How to find the sum of an array of numbers
(59 answers)
Closed 3 months ago.
I am having problems adding all the elements of an array as well as averaging them out. How would I do this and implement it with the code I currently have? The elements are supposed to be defined as I have it below.
<script type="text/javascript">
//<![CDATA[
var i;
var elmt = new Array();
elmt[0] = "0";
elmt[1] = "1";
elmt[2] = "2";
elmt[3] = "3";
elmt[4] = "4";
elmt[5] = "7";
elmt[6] = "8";
elmt[7] = "9";
elmt[8] = "10";
elmt[9] = "11";
// Problem here
for (i = 9; i < 10; i++){
document.write("The sum of all the elements is: " + /* Problem here */ + " The average of all the elements is: " + /* Problem here */ + "<br/>");
}
//]]>
</script>
A solution I consider more elegant:
const sum = times.reduce((a, b) => a + b, 0);
const avg = (sum / times.length) || 0;
console.log(`The sum is: ${sum}. The average is: ${avg}.`);
ES6
const average = arr => arr.reduce( ( p, c ) => p + c, 0 ) / arr.length;
const result = average( [ 4, 4, 5, 6, 6 ] ); // 5
console.log(result);
var sum = 0;
for( var i = 0; i < elmt.length; i++ ){
sum += parseInt( elmt[i], 10 ); //don't forget to add the base
}
var avg = sum/elmt.length;
document.write( "The sum of all the elements is: " + sum + " The average is: " + avg );
Just iterate through the array, since your values are strings, they have to be converted to an integer first. And average is just the sum of values divided by the number of values.
Calculating average (mean) using reduce and ES6:
const average = list => list.reduce((prev, curr) => prev + curr) / list.length;
const list = [0, 10, 20, 30]
average(list) // 15
Shortest one liner for Average
const avg = arr => arr.reduce((acc,v,i,a)=>(acc+v/a.length),0);
Shortest one liner for Sum
const sum = arr => arr.reduce((a,b)=>a+b);
Let's imagine we have an array of integers like this:
var values = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
The average is obtained with the following formula
A= (1/n)Σxi ( with i = 1 to n ) ... So: x1/n + x2/n + ... + xn/n
We divide the current value by the number of values and add the previous result to the returned value.
The reduce method signature is
reduce(callback[,default_previous_value])
The reduce callback function takes the following parameters:
p : Result
of the previous calculation
c : Current value (from the current index)
i : Current array element's index value
a : The current reduced Array
The second reduce's parameter is the default value ... (Used in case the array is empty ).
So the average reduce method will be:
var avg = values.reduce(function(p,c,i,a){return p + (c/a.length)},0);
If you prefer you can create a separate function
function average(p,c,i,a){return p + (c/a.length)};
function sum(p,c){return p + c)};
And then simply refer to the callback method signature
var avg = values.reduce(average,0);
var sum= values.reduce(sum,0);
Or Augment the Array prototype directly..
Array.prototype.sum = Array.prototype.sum || function (){
return this.reduce(function(p,c){return p+c},0);
};
It's possible to divide the value each time the reduce method is called..
Array.prototype.avg = Array.prototype.avg || function () {
return this.reduce(function(p,c,i,a){return p+(c/a.length)},0);
};
Or even better , using the previously defined Array.protoype.sum()
method, optimize the process my calling the division only once :)
Array.prototype.avg = Array.prototype.avg || function () {
return this.sum()/this.length;
};
Then on any Array object of the scope:
[2, 6].avg();// -> 4
[2, 6].sum();// -> 8
NB: an empty array with return a NaN wish is more correct than 0 in my point of view and can be useful in specific use cases.
generally average using one-liner reduce is like this
elements.reduce(function(sum, a,i,ar) { sum += a; return i==ar.length-1?(ar.length==0?0:sum/ar.length):sum},0);
specifically to question asked
elements.reduce(function(sum, a,i,ar) { sum += parseFloat(a); return i==ar.length-1?(ar.length==0?0:sum/ar.length):sum},0);
an efficient version is like
elements.reduce(function(sum, a) { return sum + a },0)/(elements.length||1);
Understand Javascript Array Reduce in 1 Minute
http://www.airpair.com/javascript/javascript-array-reduce
as gotofritz pointed out seems Array.reduce skips undefined values.
so here is a fix:
(function average(arr){var finalstate=arr.reduce(function(state,a) { state.sum+=a;state.count+=1; return state },{sum:0,count:0}); return finalstate.sum/finalstate.count})([2,,,6])
You can also use lodash, _.sum(array) and _.mean(array) in Math part (also have other convenient stuff).
_.sum([4, 2, 8, 6]);
// => 20
_.mean([4, 2, 8, 6]);
// => 5
Not the fastest, but the shortest and in one line is using map() & reduce():
var average = [7,14,21].map(function(x,i,arr){return x/arr.length}).reduce(function(a,b){return a + b})
I use these methods in my personal library:
Array.prototype.sum = Array.prototype.sum || function() {
return this.reduce(function(sum, a) { return sum + Number(a) }, 0);
}
Array.prototype.average = Array.prototype.average || function() {
return this.sum() / (this.length || 1);
}
EDIT:
To use them, simply ask the array for its sum or average, like:
[1,2,3].sum() // = 6
[1,2,3].average() // = 2
In ES6-ready browsers this polyfill may be helpful.
Math.sum = (...a) => Array.prototype.reduce.call(a,(a,b) => a+b)
Math.avg = (...a) => Math.sum(...a)/a.length;
You can share same call method between Math.sum,Math.avg and Math.max,such as
var maxOne = Math.max(1,2,3,4) // 4;
you can use Math.sum as
var sumNum = Math.sum(1,2,3,4) // 10
or if you have an array to sum up,you can use
var sumNum = Math.sum.apply(null,[1,2,3,4]) // 10
just like
var maxOne = Math.max.apply(null,[1,2,3,4]) // 4
One sneaky way you could do it although it does require the use of (the much hated) eval().
var sum = eval(elmt.join('+')), avg = sum / elmt.length;
document.write("The sum of all the elements is: " + sum + " The average of all the elements is: " + avg + "<br/>");
Just thought I'd post this as one of those 'outside the box' options. You never know, the slyness might grant you (or taketh away) a point.
Here is a quick addition to the “Math” object in javascript to add a “average” command to it!!
Math.average = function(input) {
this.output = 0;
for (this.i = 0; this.i < input.length; this.i++) {
this.output+=Number(input[this.i]);
}
return this.output/input.length;
}
Then i have this addition to the “Math” object for getting the sum!
Math.sum = function(input) {
this.output = 0;
for (this.i = 0; this.i < input.length; this.i++) {
this.output+=Number(input[this.i]);
}
return this.output;
}
So then all you do is
alert(Math.sum([5,5,5])); //alerts “15”
alert(Math.average([10,0,5])); //alerts “5”
And where i put the placeholder array just pass in your variable (The input if they are numbers can be a string because of it parsing to a number!)
I found Mansilla's answer to work fine with the extension of making sure that I am doing summation of floats and not concatonation of strings using parseFloat():
let sum = ourarray.reduce((a, b) => parseFloat(a) + parseFloat(b), 0);
let avg = (sum / ourarray.length) || 0;
console.log(sum); // print out sum
console.log(avg); // print out avg
set your for loop counter to 0.... you're getting element 9 and then you're done as you have it now. The other answers are basic math. Use a variable to store your sum (need to cast the strings to ints), and divide by your array length.
Start by defining all of the variables we plan on using. You'll note that for the numbers array, I'm using the literal notation of [] as opposed to the constructor method array(). Additionally, I'm using a shorter method to set multiple variables to 0.
var numbers = [], count = sum = avg = 0;
Next I'm populating my empty numbers array with the values 0 through 11. This is to get me to your original starting point. Note how I'm pushing onto the array count++. This pushing the current value of count, and then increments it for the next time around.
while ( count < 12 )
numbers.push( count++ );
Lastly, I'm performing a function "for each" of the numbers in the numbers array. This function will handle one number at a time, which I'm identifying as "n" within the function body.
numbers.forEach(function(n){
sum += n;
avg = sum / numbers.length;
});
In the end, we can output both the sum value, and the avg value to our console in order to see the result:
// Sum: 66, Avg: 5.5
console.log( 'Sum: ' + sum + ', Avg: ' + avg );
See it in action online at http://jsbin.com/unukoj/3/edit
I am just building on Abdennour TOUMI's answer. here are the reasons why:
1.) I agree with Brad, I do not think it is a good idea to extend object that we did not create.
2.) array.length is exactly reliable in javascript, I prefer Array.reduce beacuse a=[1,3];a[1000]=5; , now a.length would return 1001.
function getAverage(arry){
// check if array
if(!(Object.prototype.toString.call(arry) === '[object Array]')){
return 0;
}
var sum = 0, count = 0;
sum = arry.reduce(function(previousValue, currentValue, index, array) {
if(isFinite(currentValue)){
count++;
return previousValue+ parseFloat(currentValue);
}
return previousValue;
}, sum);
return count ? sum / count : 0;
};
Array.prototype.avg=function(fn){
fn =fn || function(e,i){return e};
return (this.map(fn).reduce(function(a,b){return parseFloat(a)+parseFloat(b)},0) / this.length ) ;
};
Then :
[ 1 , 2 , 3].avg() ; //-> OUT : 2
[{age:25},{age:26},{age:27}].avg(function(e){return e.age}); // OUT : 26
On evergreen browsers you can use arrow functions
avg = [1,2,3].reduce((a,b) => (a+b);
Running it 100,000 times, the time difference between the for loop approach and reduce is negligible.
s=Date.now();for(i=0;i<100000;i++){ n=[1,2,3]; a=n.reduce((a,b) => (a+b)) / n.length };
console.log("100k reduce took " + (Date.now()-s) + "ms.");
s=Date.now();for(i=0;i<100000;i++){n=[1,2,3]; nl=n.length; a=0; for(j=nl-1;j>0;j--){a=a+n[j];} a/nl };
console.log("100k for loop took " + (Date.now()-s) + "ms.");
s=Date.now();for(i=0;i<1000000;i++){n=[1,2,3]; nl=n.length; a=0; for(j=nl-1;j>0;j--){a=a+n[j];} a/nl };
console.log("1M for loop took " + (Date.now()-s) + "ms.");
s=Date.now();for(i=0;i<1000000;i++){ n=[1,2,3]; a=n.reduce((a,b) => (a+b)) / n.length };
console.log("1M reduce took " + (Date.now()-s) + "ms.");
/*
* RESULT on Chrome 51
* 100k reduce took 26ms.
* 100k for loop took 35ms.
* 10M for loop took 126ms.
* 10M reduce took 209ms.
*/
If you are in need of the average and can skip the requirement of calculating the sum, you can compute the average with a single call of reduce:
// Assumes an array with only values that can be parsed to a Float
var reducer = function(cumulativeAverage, currentValue, currentIndex) {
// 1. multiply average by currentIndex to find cumulative sum of previous elements
// 2. add currentValue to get cumulative sum, including current element
// 3. divide by total number of elements, including current element (zero-based index + 1)
return (cumulativeAverage * currentIndex + parseFloat(currentValue))/(currentIndex + 1)
}
console.log([1, 2, 3, 4, 5, 6, 7, 8, 9, 10].reduce(reducer, 0)); // => 5.5
console.log([].reduce(reducer, 0)); // => 0
console.log([0].reduce(reducer, 0)); // => 0
console.log([].reduce(reducer, 0)); // => 0
console.log([,,,].reduce(reducer, 0)); // => 0
console.log([].reduce(reducer, 0)); // => 0
If anyone ever needs it - Here is a recursive average.
In the context of the original question, you may want to use the recursive average if you allowed the user to insert additional values and, without incurring the cost of visiting each element again, wanted to "update" the existing average.
/**
* Computes the recursive average of an indefinite set
* #param {Iterable<number>} set iterable sequence to average
* #param {number} initAvg initial average value
* #param {number} initCount initial average count
*/
function average(set, initAvg, initCount) {
if (!set || !set[Symbol.iterator])
throw Error("must pass an iterable sequence");
let avg = initAvg || 0;
let avgCnt = initCount || 0;
for (let x of set) {
avgCnt += 1;
avg = avg * ((avgCnt - 1) / avgCnt) + x / avgCnt;
}
return avg; // or {avg: avg, count: avgCnt};
}
average([2, 4, 6]); //returns 4
average([4, 6], 2, 1); //returns 4
average([6], 3, 2); //returns 4
average({
*[Symbol.iterator]() {
yield 2; yield 4; yield 6;
}
}); //returns 4
How:
this works by maintaining the current average and element count. When a new value is to be included you increment count by 1, scale the existing average by (count-1) / count, and add newValue / count to the average.
Benefits:
you don't sum all the elements, which may result in large number that cannot be stored in a 64-bit float.
you can "update" an existing average if additional values become available.
you can perform a rolling average without knowing the sequence length.
Downsides:
incurs lots more divisions
not infinite - limited to Number.MAX_SAFE_INTEGER items unless you employ BigNumber
Having read the other choices, I will try to make a simpler version for the future viewers, elaborating on the existing code and not creating a more elegant one. First of all, you declared the numbers as strings. Apart from the .parseInt we can also do:
const numberConverter = elmt.map(Number);
So what map does is that it "returns a copy of the original array". But I convert its values to numbers. Then we can use the reduce method (It can also be simpler, but I am writing easy to read versions and I also have 2 average methods) What the reduce method does is it has an accumulator that gets bigger and bigger if you add values to it, as it iterates through the array and adds (in this case) the currentValue to it.:
var i;
const elmt = new Array();
elmt[0] = '0';
elmt[1] = '1';
elmt[2] = '2';
elmt[3] = '3';
elmt[4] = '4';
elmt[5] = '7';
elmt[6] = '8';
elmt[7] = '9';
elmt[8] = '10';
elmt[9] = '11';
console.log(elmt);
const numberConverter = elmt.map(Number);
const sum = numberConverter.reduce((accumulator, currentValue) => {
return accumulator + currentValue;
}, 0);
const average = numberConverter.reduce(
(accumulator, currentvalue, index, numArray) => {
return accumulator + currentvalue / numArray.length;
},
0
);
const average2 =
numberConverter.reduce(
(accumulator, currentValue) => accumulator + currentValue,
0
) / numberConverter.length;
for (i = 9; i < 10; i++) {
console.log(
`The sum of all the elements is: ${sum}. <br> The average of all the elements is: ${average2}`
);}
Unless I missed something, every solution up to this point uses the length of the list to calculate the average after summing the values.
There is a downside to this approach that a slightly modified, yet still simple algorithm will address without the downsides.
The downside is that you assuming that there won't be an overflow by summing all the numbers. If you have a lot of numbers that are very big, and you add them all up, they may exceed the maximum size that can fit into the data type.
A better approach is to simply calculate the average as you go, rather than summing it and then dividing with the length at the end:
function getAvg(values) {
return values.reduce((m, x, i) => m + (x - m) / (i + 1), 0)
}
Props to Knuth's "Art of Computer Programming" vol. 2.
just for fun
let avg = [81, 77, -88, 195, 6.8].reduce((a,e,i) => (a*i+e)/(i+1));
console.log(avg)
Just for kicks:
var elmt = [0, 1, 2,3, 4, 7, 8, 9, 10, 11], l = elmt.length, i = -1, sum = 0;
for (; ++i < l; sum += elmt[i])
;
document.body.appendChild(document.createTextNode('The sum of all the elements is: ' + sum + ' The average of all the elements is: ' + (sum / l)));
I think we can do like
var k=elmt.reduce(function(a,b){return parseFloat(a+parseFloat(b));})
var avg=k/elmt.length;
console.log(avg);
I am using parseFloat twice because
when
1) you add (a)9+b("1") number then result will be "91" but we want addition. so i used parseFloat
2)When addition of (a)9+parseFloat("1") happen though result will be "10" but it will be in string which we don't want so again i used parseFloat.
I hope i am clear. Suggestions are welcome
Here is my rookie way of simply finding the avg. Hope this helps somebody.
function numAvg(num){
var total = 0;
for(var i = 0;i < num.length; i++) {
total+=num[i];
}
return total/num.length;
}
here's your one liner:
var average = arr.reduce((sum,item,index,arr)=>index !== arr.length-1?sum+item:sum+item/arr.length,0)
I think this may be a direct solution to calculate the average with a for loop and function.
var elmts = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
function average(arr) {
var total = 0;
for (var i = 0; i < arr.length; i++) {
total += arr[i];
}
console.log(Math.round(total/arr.length));
}
average(elmts);
There seem to be an endless number of solutions for this but I found this to be concise and elegant.
const numbers = [1,2,3,4];
const count = numbers.length;
const reducer = (adder, value) => (adder + value);
const average = numbers.map(x => x/count).reduce(reducer);
console.log(average); // 2.5
Or more consisely:
const numbers = [1,2,3,4];
const average = numbers.map(x => x/numbers.length).reduce((adder, value) => (adder + value));
console.log(average); // 2.5
Depending on your browser you may need to do explicit function calls because arrow functions are not supported:
const r = function (adder, value) {
return adder + value;
};
const m = function (x) {
return x/count;
};
const average = numbers.map(m).reduce(r);
console.log(average); // 2.5
Or:
const average1 = numbers
.map(function (x) {
return x/count;
})
.reduce(function (adder, value) {
return adder + value;
});
console.log(average1);