Javascript function to generate random integers with nonuniform probabilities - javascript

In javascript (or jquery) is there a simple function to have four integers with their probability values: 1|0.41, 2|0.29, 3|0.25, 4|0.05
how can I generate these four numbers taking into account their probabilities ?
This question is very similar to the one posted here: generate random integers with probabilities
HOWEVER the solution posted there:
function randomWithProbability() {
var notRandomNumbers = [1, 1, 1, 1, 2, 2, 2, 3, 3, 4];
var idx = Math.floor(Math.random() * notRandomNumbers.length);
return notRandomNumbers[idx];
}
states in the comment "create notRandomNumbers dynamically (given the numbers and their weight/probability)"
This is insufficient for my needs. That works well when the probabilities are say 10%,20%, 60%,10%.
In that case constructing notRandomNumbers with the required distribution is easy and the array size is small. But in the general case where probabilities can be something like 20.354%,30.254% etc , the array size would be huge to correctly model the situation.
Is there a clean solution to this more general problem?
EDIT: Thanks Georg, solution accepted, here is my final version, which may be useful for others. I have split the calculation of the cumulative into a separate function in order to avoid extra additions at each call to get a new random number.
function getRandomBinFromCumulative(cumulative) {
var r = Math.random();
for (var i = 0; i < cumulative.length; i++) {
if (r <= cumulative[i])
return i;
}
}
function getCummulativeDistribution(probs) {
var cumulative = [];
var sum = probs[0];
probs.forEach(function (p) {
cumulative.push(sum);
sum += p;
});
// the next 2 lines are optional
cumulative[cumulative.length - 1] = 1; //force to 1 (if input total was <>1)
cumulative.shift(); //remove the first 0
return cumulative;
}
function testRand() {
var probs = [0.1, 0.3, 0.3, 0.3];
var c = getCummulativeDistribution(probs);
console.log(c);
for (var i = 0; i < 100; i++) {
console.log(getRandomBinFromCumulative(c));
}
}

Just accumulate the probabilities and return an item for which current_sum >= random_number:
probs = [0.41, 0.29, 0.25, 0.05];
function item() {
var r = Math.random(), s = 0;
for(var i = 0; i < probs.length; i++) {
s += probs[i];
if(r <= s)
return i;
}
}
// generate 100000 randoms
a = [];
c = 0;
while(c++ < 100000) {
a.push(item());
}
// test actual distibution
c = {}
a.forEach(function(x) {
c[x] = (c[x] || 0) + 1;
});
probs.forEach(function(_, x) {
document.write(x + "=" + c[x] / a.length + "<br>")
});

Create a second parallel array with corresponding weights and use a "wheel" algorithm to get an index.
function randomWithProbability()
{
var notRandomNumbers = [1,2,3,4];
var w = [0.41, 0.29, 0.25, 0.05];
var placehldr = 0;
var maxProb = 0.41;
var index = Math.floor(Math.random() * w.length);
var i = 0;
placehldr = Math.random() * (maxProb * 2);
while(placehldr > index )
{
placehldr -= w[index];
index = (index + 1) % w.length
}
return (notRandomNumbers[index]);
}
This video has a good explanation as to why it works, it's easier to understand with the visual representation.
https://www.youtube.com/watch?v=wNQVo6uOgYA

There is an elegant solution only requiring a single comparison due to A. J. Walker (Electronics Letters 10, 8 (1974), 127-128; ACM Trans. Math Software 3 (1977), 253-256) and described in Knuth, TAOCP Vol. 2, 120-121.
You can also find a description here, generate random numbers within a range with different probabilities.

Related

JavaScript: Generate a unique 'x' numbers base on the range & set given [duplicate]

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)

How to generated N numbers in array that sum of these numbers is equal to 0

I have a code like this:
function myArr(N){
let arr = [];
function randomNumber(min,max) {
if (min > max) {
let vMin = min;
min = parseInt(max,10);
max = parseInt(vMin,10);
}
return Math.floor(Math.random()*(max-min+1)+min);
}
for(let i = 0; i < N; i++) {
arr.push(randomNumber(100,-100));
}
return arr;
}
This function generates an array with N numbers. But I want that the sum of these generated numbers will be equal to 0. How to make it? I was thinking about conditional 'if' but I don't exactly know, how to use it in this case ... Maybe some of you know, how to do this? Thanks for any tips!
There are many ways to generate an array of randomly-generated values that add up to zero, but they all have different implications for the distribution of values.
For example, one simple approach is to first generate the values and compute their average, and then subtract that average from each value. The consequence of this is that the values may end up outside the range you originally wanted; for example, if you randomly generate [100, 100, 100, -100], then the average is 50, so you'd end up with [50, 50, 50, -150]. You can compensate for that by starting out with a narrower range than you really need; but then that means that values in or near that narrower range will be much more likely to appear than values near the end of your full range.
Another simple approach is to generate only n/2 values, and for each value that you generate, to include both that value and its arithmetic inverse (e.g., if you generate 37, then your result will include both 37 and -37). You can then randomly shuffle the result; so, for example, if you randomly generate [17, -84, 12], then your final array might be [-12, 17, -84, -17, 84, 12].
. . . all of which is to say that you need to figure out your precise requirements. Randomness is complicated!
While generating numbers you have to make sure that the numbers stay close to zero, then you generate N - 1 numbers and calculate the last one:
const arr = [];
let sum = 0;
for(let i = 0; i < N - 2; i++) {
let number;
if(sum >= 100) {
number = randomNumber(100 - sum, -100);
} else if(sum <= -100) {
number = randomNumber(100, -100 - sum);
} else {
number = randomNumber(100, -100);
}
sum += number;
arr.push(number);
}
arr.push(Math.floor(-sum / 2), Math ceil(-sum / 2));
Try it
(Won't work well for N < 4)
Here's another way. Start with zero and split a random array element N-1 times:
function myArr(N){
let arr = [0];
function randomNumber(min,max) {
if (min > max) {
let vMin = min;
min = parseInt(max,10);
max = parseInt(vMin,10);
}
return Math.floor(Math.random()*(max-min+1)+min);
}
function split(n){
let low = Math.max(-100, n - 100);
let high = Math.min(100, n + 100);
let r = randomNumber(low, high);
return [r, n - r]
}
for(let i = 0; i < N-1; i++) {
let idx = ~~(Math.random() * arr.length);
let newNums = split(arr[idx]);
arr[idx] = newNums[0];
arr.push(newNums[1]);
}
return arr;
}
console.log(myArr(5));
This is my solution, basically you do a for loop and start adding elements.
Whenever you add an element, just add the same element * -1
You will end up with an array of elements with sum 0.
function arrayOfSumZero(N) {
let sum = 0;
let i = N % 2 === 0 ? 0 : 1;
let output = [];
for (i; i < N; i++) {
if (output.length < N) {
sum += i;
output.push(i);
}
if (sum > 0) {
sum += i * -1;
output.push(i * -1);
}
}
return output;
}

Unique random number generator in JavaScript [duplicate]

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)

Get a random number focused on center

Is it possible to get a random number between 1-100 and keep the results mainly within the 40-60 range? I mean, it will go out of that range rarely, but I want it to be mainly within that range... Is it possible with JavaScript/jQuery?
Right now I'm just using the basic Math.random() * 100 + 1.
The simplest way would be to generate two random numbers from 0-50 and add them together.
This gives a distribution biased towards 50, in the same way rolling two dice biases towards 7.
In fact, by using a larger number of "dice" (as #Falco suggests), you can make a closer approximation to a bell-curve:
function weightedRandom(max, numDice) {
let num = 0;
for (let i = 0; i < numDice; i++) {
num += Math.random() * (max/numDice);
}
return num;
}
JSFiddle: http://jsfiddle.net/797qhcza/1/
You have some good answers here that give specific solutions; let me describe for you the general solution. The problem is:
I have a source of more-or-less uniformly distributed random numbers between 0 and 1.
I wish to produce a sequence of random numbers that follow a different distribution.
The general solution to this problem is to work out the quantile function of your desired distribution, and then apply the quantile function to the output of your uniform source.
The quantile function is the inverse of the integral of your desired distribution function. The distribution function is the function where the area under a portion of the curve is equal to the probability that the randomly-chosen item will be in that portion.
I give an example of how to do so here:
http://ericlippert.com/2012/02/21/generating-random-non-uniform-data/
The code in there is in C#, but the principles apply to any language; it should be straightforward to adapt the solution to JavaScript.
Taking arrays of numbers, etc. isn't efficient. You should take a mapping which takes a random number between 0 to 100 and maps to the distribution you need. So in your case, you could take f(x)=-(1/25)x2+4x to get a distribution with the most values in the middle of your range.
I might do something like setup a "chance" for the number to be allowed to go "out of bounds". In this example, a 20% chance the number will be 1-100, otherwise, 40-60:
$(function () {
$('button').click(function () {
var outOfBoundsChance = .2;
var num = 0;
if (Math.random() <= outOfBoundsChance) {
num = getRandomInt(1, 100);
} else {
num = getRandomInt(40, 60);
}
$('#out').text(num);
});
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<button>Generate</button>
<div id="out"></div>
fiddle: http://jsfiddle.net/kbv39s9w/
I needed to solve this problem a few years ago and my solution was easier than any of the other answers.
I generated 3 randoms between the bounds and averaged them. This pulls the result towards the centre but leaves it completely possible to reach the extremities.
It looks stupid but you can use rand twice:
var choice = Math.random() * 3;
var result;
if (choice < 2){
result = Math.random() * 20 + 40; //you have 2/3 chance to go there
}
else {
result = Math.random() * 100 + 1;
}
Sure it is possible. Make a random 1-100. If the number is <30 then generate number in range 1-100 if not generate in range 40-60.
There is a lot of different ways to generate such random numbers. One way to do it is to compute the sum of multiple uniformly random numbers. How many random numbers you sum and what their range is will determine how the final distribution will look.
The more numbers you sum up, the more it will be biased towards the center. Using the sum of 1 random number was already proposed in your question, but as you notice is not biased towards the center of the range. Other answers have propose using the sum of 2 random numbers or the sum of 3 random numbers.
You can get even more bias towards the center of the range by taking the sum of more random numbers. At the extreme you could take the sum of 99 random numbers which each were either 0 or 1. That would be a binomial distribution. (Binomial distributions can in some sense be seen as the discrete version of normal distributions). This can still in theory cover the full range, but it has so much bias towards the center that you should never expect to see it reach the endpoints.
This approach means you can tweak just how much bias you want.
What about using something like this:
var loops = 10;
var tries = 10;
var div = $("#results").html(random());
function random() {
var values = "";
for(var i=0; i < loops; i++) {
var numTries = tries;
do {
var num = Math.floor((Math.random() * 100) + 1);
numTries--;
}
while((num < 40 || num >60) && numTries > 1)
values += num + "<br/>";
}
return values;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="results"></div>
The way I've coded it allows you to set a couple of variables:
loops = number of results
tries = number of times the function will try to get a number between 40-60 before it stops running through the while loop
Added bonus: It uses do while!!! Awesomeness at its best
You can write a function that maps random values between [0, 1) to [1, 100] according to weight. Consider this example:
Here, the value 0.95 maps to value between [61, 100].
In fact we have .05 / .1 = 0.5, which, when mapped to [61, 100], yields 81.
Here is the function:
/*
* Function that returns a function that maps random number to value according to map of probability
*/
function createDistributionFunction(data) {
// cache data + some pre-calculations
var cache = [];
var i;
for (i = 0; i < data.length; i++) {
cache[i] = {};
cache[i].valueMin = data[i].values[0];
cache[i].valueMax = data[i].values[1];
cache[i].rangeMin = i === 0 ? 0 : cache[i - 1].rangeMax;
cache[i].rangeMax = cache[i].rangeMin + data[i].weight;
}
return function(random) {
var value;
for (i = 0; i < cache.length; i++) {
// this maps random number to the bracket and the value inside that bracket
if (cache[i].rangeMin <= random && random < cache[i].rangeMax) {
value = (random - cache[i].rangeMin) / (cache[i].rangeMax - cache[i].rangeMin);
value *= cache[i].valueMax - cache[i].valueMin + 1;
value += cache[i].valueMin;
return Math.floor(value);
}
}
};
}
/*
* Example usage
*/
var distributionFunction = createDistributionFunction([
{ weight: 0.1, values: [1, 40] },
{ weight: 0.8, values: [41, 60] },
{ weight: 0.1, values: [61, 100] }
]);
/*
* Test the example and draw results using Google charts API
*/
function testAndDrawResult() {
var counts = [];
var i;
var value;
// run the function in a loop and count the number of occurrences of each value
for (i = 0; i < 10000; i++) {
value = distributionFunction(Math.random());
counts[value] = (counts[value] || 0) + 1;
}
// convert results to datatable and display
var data = new google.visualization.DataTable();
data.addColumn("number", "Value");
data.addColumn("number", "Count");
for (value = 0; value < counts.length; value++) {
if (counts[value] !== undefined) {
data.addRow([value, counts[value]]);
}
}
var chart = new google.visualization.ColumnChart(document.getElementById("chart"));
chart.draw(data);
}
google.load("visualization", "1", { packages: ["corechart"] });
google.setOnLoadCallback(testAndDrawResult);
<script src="https://www.google.com/jsapi"></script>
<div id="chart"></div>
Here's a weighted solution at 3/4 40-60 and 1/4 outside that range.
function weighted() {
var w = 4;
// number 1 to w
var r = Math.floor(Math.random() * w) + 1;
if (r === 1) { // 1/w goes to outside 40-60
var n = Math.floor(Math.random() * 80) + 1;
if (n >= 40 && n <= 60) n += 40;
return n
}
// w-1/w goes to 40-60 range.
return Math.floor(Math.random() * 21) + 40;
}
function test() {
var counts = [];
for (var i = 0; i < 2000; i++) {
var n = weighted();
if (!counts[n]) counts[n] = 0;
counts[n] ++;
}
var output = document.getElementById('output');
var o = "";
for (var i = 1; i <= 100; i++) {
o += i + " - " + (counts[i] | 0) + "\n";
}
output.innerHTML = o;
}
test();
<pre id="output"></pre>
Ok, so I decided to add another answer because I felt like my last answer, as well as most answers here, use some sort of half-statistical way of obtaining a bell-curve type result return. The code I provide below works the same way as when you roll a dice. Therefore, it is hardest to get 1 or 99, but easiest to get 50.
var loops = 10; //Number of numbers generated
var min = 1,
max = 50;
var div = $("#results").html(random());
function random() {
var values = "";
for (var i = 0; i < loops; i++) {
var one = generate();
var two = generate();
var ans = one + two - 1;
var num = values += ans + "<br/>";
}
return values;
}
function generate() {
return Math.floor((Math.random() * (max - min + 1)) + min);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="results"></div>
I'd recommend using the beta distribution to generate a number between 0-1, then scale it up. It's quite flexible and can create many different shapes of distributions.
Here's a quick and dirty sampler:
rbeta = function(alpha, beta) {
var a = 0
for(var i = 0; i < alpha; i++)
a -= Math.log(Math.random())
var b = 0
for(var i = 0; i < beta; i++)
b -= Math.log(Math.random())
return Math.ceil(100 * a / (a+b))
}
var randNum;
// generate random number from 1-5
var freq = Math.floor(Math.random() * (6 - 1) + 1);
// focus on 40-60 if the number is odd (1,3, or 5)
// this should happen %60 of the time
if (freq % 2){
randNum = Math.floor(Math.random() * (60 - 40) + 40);
}
else {
randNum = Math.floor(Math.random() * (100 - 1) + 1);
}
The best solution targeting this very problem is the one proposed by BlueRaja - Danny Pflughoeft but I think a somewhat faster and more general solution is also worth mentioning.
When I have to generate random numbers (strings, coordinate pairs, etc.) satisfying the two requirements of
The result set is quite small. (not larger than 16K numbers)
The result set is discreet. (like integer numbers only)
I usually start by creating an array of numbers (strings, coordinate pairs, etc.) fulfilling the requirement (In your case: an array of numbers containing the more probable ones multiple times.), then choose a random item of that array. This way, you only have to call the expensive random function once per item.
Distribution
5% for [ 0,39]
90% for [40,59]
5% for [60,99]
Solution
var f = Math.random();
if (f < 0.05) return random(0,39);
else if (f < 0.95) return random(40,59);
else return random(60,99);
Generic Solution
random_choose([series(0,39),series(40,59),series(60,99)],[0.05,0.90,0.05]);
function random_choose (collections,probabilities)
{
var acc = 0.00;
var r1 = Math.random();
var r2 = Math.random();
for (var i = 0; i < probabilities.length; i++)
{
acc += probabilities[i];
if (r1 < acc)
return collections[i][Math.floor(r2*collections[i].length)];
}
return (-1);
}
function series(min,max)
{
var i = min; var s = [];
while (s[s.length-1] < max) s[s.length]=i++;
return s;
}
You can use a helper random number to whether generate random numbers in 40-60 or 1-100:
// 90% of random numbers should be between 40 to 60.
var weight_percentage = 90;
var focuse_on_center = ( (Math.random() * 100) < weight_percentage );
if(focuse_on_center)
{
// generate a random number within the 40-60 range.
alert (40 + Math.random() * 20 + 1);
}
else
{
// generate a random number within the 1-100 range.
alert (Math.random() * 100 + 1);
}
If you can use the gaussian function, use it. This function returns normal number with average 0 and sigma 1.
95% of this number are within average +/- 2*sigma. Your average = 50, and sigma = 5 so
randomNumber = 50 + 5*gaussian()
The best way to do that is generating a random number that is distributed equally in a certain set of numbers, and then apply a projection function to the set between 0 and a 100 where the projection is more likely to hit the numbers you want.
Typically the mathematical way of achieving this is plotting a probability function of the numbers you want. We could use the bell curve, but let's for the sake of easier calculation just work with a flipped parabola.
Let's make a parabola such that its roots are at 0 and 100 without skewing it. We get the following equation:
f(x) = -(x-0)(x-100) = -x * (x-100) = -x^2 + 100x
Now, all the area under the curve between 0 and 100 is representative of our first set where we want the numbers generated. There, the generation is completely random. So, all we need to do is find the bounds of our first set.
The lower bound is, of course, 0. The upper bound is the integral of our function at 100, which is
F(x) = -x^3/3 + 50x^2
F(100) = 500,000/3 = 166,666.66666 (let's just use 166,666, because rounding up would make the target out of bounds)
So we know that we need to generate a number somewhere between 0 and 166,666. Then, we simply need to take that number and project it to our second set, which is between 0 and 100.
We know that the random number we generated is some integral of our parabola with an input x between 0 and 100. That means that we simply have to assume that the random number is the result of F(x), and solve for x.
In this case, F(x) is a cubic equation, and in the form F(x) = ax^3 + bx^2 + cx + d = 0, the following statements are true:
a = -1/3
b = 50
c = 0
d = -1 * (your random number)
Solving this for x yields you the actual random number your are looking for, which is guaranteed to be in the [0, 100] range and a much higher likelihood to be close to the center than the edges.
This answer is really good. But I would like to post implementation instructions (I'm not into JavaScript, so I hope you will understand) for different situation.
Assume you have ranges and weights for every range:
ranges - [1, 20], [21, 40], [41, 60], [61, 100]
weights - {1, 2, 100, 5}
Initial Static Information, could be cached:
Sum of all weights (108 in sample)
Range selection boundaries. It basically this formula: Boundary[n] = Boundary[n - 1] + weigh[n - 1] and Boundary[0] = 0. Sample has Boundary = {0, 1, 3, 103, 108}
Number generation:
Generate random number N from range [0, Sum of all weights).
for (i = 0; i < size(Boundary) && N > Boundary[i + 1]; ++i)
Take ith range and generate random number in that range.
Additional note for performance optimizations. Ranges don't have to be ordered neither ascending nor descending order, so for faster range look-up range that has highest weight should go first and one with lowest weight should go last.

Runtime Error & Time Complexity Issues: Minimize the value |(A[0] + ... + A[P-1]) - (A[P] + ... + A[N-1])|

I recently tackled a coding problem. I came up with a solution to the following problem.
A non-empty zero-indexed array A consisting of N integers is given. Array A represents numbers on a tape.
Any integer P, such that 0 < P < N, splits this tape into two non-empty parts: A[0], A[1], ..., A[P − 1] and A[P], A[P + 1], ..., A[N − 1].
The difference between the two parts is the value of: |(A[0] + A[1] + ... + A[P − 1]) − (A[P] + A[P + 1] + ... + A[N − 1])|
In other words, it is the absolute difference between the sum of the first part and the sum of the second part.
For example, consider array A such that:
A[0] = 3
A[1] = 1
A[2] = 2
A[3] = 4
A[4] = 3
We can split this tape in four places:
P = 1, difference = |3 − 10| = 7
P = 2, difference = |4 − 9| = 5
P = 3, difference = |6 − 7| = 1
P = 4, difference = |10 − 3| = 7
Write a function:
function solution(A);
that, given a non-empty zero-indexed array A of N integers, returns the minimal difference that can be achieved.
For example, given:
A[0] = 3
A[1] = 1
A[2] = 2
A[3] = 4
A[4] = 3
the function should return 1, as explained above.
Assume that:
N is an integer within the range [2..100,000];
each element of array A is an integer within the range [−1,000..1,000].
Complexity:
expected worst-case time complexity is O(N);
expected worst-case space complexity is O(N), beyond input storage (not counting the storage required for input arguments).
Elements of input arrays can be modified.
The following is the feedback I obtained from testing the solution:
CORRECTNESS:
small_range range sequence, length = ~1,000 1.900 s RUNTIME ERROR
tested program terminated unexpectedly
PERFORMANCE:
Detected time complexity: O(N * N)
So I am getting one runtime error for ranges around 1000. And most importantly, I am not getting O(n). I am getting O(n * n) as I am using nested for loops.
(1) How could I fix the runtime error?
(2) How could one construct O(n) algorithm for the same problem? Any suggestions?
This is my solution:
function solution(A){
var len = A.length;
var diff = []; // Array to store the differences
var sumLeft = 0; // Sum of array elements from index 0 to index p - 1
var sumRight = 0; // Sum of array elements from index p to index n - 1
for(var p = 1; p < len; p++){
sumLeft = 0;
sumRight = 0;
// Calculate sumLeft:
for(var i = 0; i < p; i++){
sumLeft += A[i];
}
// Calculate sumRight:
for(var j = p; j < len; j++){
sumRight += A[j];
}
// Calculate differences, compute absolute values, and push into diff array:
diff.push(Math.abs(sumLeft - sumRight));
}
// Return the minimum of diff array by sorting it and returning the first element:
return bubbleSort(diff)[0];
}
function bubbleSort(array){
var len = array.length;
for(var i = 0; i < len; i++){
for(var j = i + 1; j < len; j++){
if(array[i] > array[j]){
var temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
}
return array;
}
Let me try to explain you how you can think about improving the space and time complexity of your algorithm. You realize where clearly that you're using nested for loops and it greatly increases the iterations and might also be causing the run-time error for sufficiently large inputs.
The first step should be reducing the redundancy of your operations. Now you calculate the left and right sums repeatedly for different values of p. You don't need that at all. I'll give you an example for how the algorithm will flow:
Array indices -> A [0, 1, 2, 3, ....,p ,p+1, ....n-1] for a size n array
At any point A[p] would act as a pivot as it breaks the array into two.
For p = 1, You just take the first element i.e A[0] and the right part of the sum is
A[1] + A[2] + .... A[n-1]
Let S1 = A[0] and S2 = A[1] + A[2] + .... A[n-1] for p = 1
The pivot or the break point here is A[p] i.e A[1]
Calculate the absolute difference |S1- S2| and store it in a variable min-diff
For p = 2,
S1 will simply be S1 + A[1] i.e the previous value of S1 including the last pivot
S2 = S2 - A[1], as we have moved on to the next element.
The sum of the remaining elements would not account the element we just crossed.
Formally,
S1 = S1 + A[p-1] and S2 = S2 - A[p-1]
Calculate the new difference i.e |S1 - S2| and just check
if it is smaller than the value of our variable min-diff.
If it is, update the value of min-diff with the present difference,
otherwise move on to the next element.
At any value of p, S1 represents sum of left half,
S2 represents sum of right half and
min-diff represents the minium absolute difference till now.
Complexity for this algorithm
Time complexity
The only time we calculate the sum of the elements is the first time
when we calculate A[1]+...A[n-1]. After that we just traverse the
elements of the array one by one.
So we traverse the elements of the array at max twice. So time
complexity is clearly O(N)
Space complexity
We use three extra variables i.e S1, S2 and min-diff all through
this algorithm to accumulate the sum and store the minimum absolute
difference along with the value of p and the n elements of the
array.
So space complexity of this algorithm is again O(N)
On a side note- Although you don't require sorting for this problem at all since you're to output the minimum difference only, but whenever sorting, please don't use bubble-sort at it is clearly the least efficient sorting method. You're better off with merge sort or quick sort which have a run time of O(NlogN)
I hope I was able to explain myself. Try to code this into a simple function, shouldn't take long. It should probably fix the run-time error as well.
You don't need to calculate the sum of the vector pieces when you test a new value of P. If you calculated leftSum and rightSum for both parts for P=(p-1), when you have to calculate it for P=p you just need to:
Remove array[p] from rightSum; and
Add array[p] to leftSum.
This both are O(1). If you do it (n-1) times, you are still under O(n) complexity.
Hope that helps.
Code with java : O(N)
import java.math.*;
class Solution {
public int solution(int[] A) {
long sumright = 0;
long sumleft = 0;
long ans;
for (int i =1;i<A.length;i++)
{
sumright += A[i];
}
sumleft = A[0];
ans =Math.abs(sumright+sumleft);
for (int P=1; P<A.length; P++)
{
if (Math.abs(sumleft - sumright)<ans)
{
ans = Math.abs(sumleft - sumright);
}
sumleft += A[P];
sumright -=A[P];
}
return (int) ans;
}
}
Without debugging, this solution gets a 100% task score on Codility (with 100% for both correctness and performance):
function solution(A) {
var sum_right = 0;
for (int of A.slice(1)) {
sum_right += int;
}
var sum_left = A[0];
var diff_of_sums = sum_left - sum_right;
var lowest_diff = Math.abs(diff_of_sums);
var diff_new;
// we assume the length is at least 2
if (A.length == 2) {
return lowest_diff;
}
for (var int of A.slice(1)) {
diff_new = Math.abs(sum_left - sum_right);
if (diff_new < lowest_diff) {
lowest_diff = diff_new;
}
sum_left += int;
sum_right -= int;
}
return lowest_diff;
}
With debugging:
// you can write to stdout for debugging purposes, e.g.
// console.log('this is a debug message');
function solution(A) {
var sum_right = 0;
for (int of A.slice(1)) {
sum_right += int;
}
var sum_left = A[0];
var diff_of_sums = sum_left - sum_right;
// var total = Math.abs(sum_left + sum_right);
var lowest_diff = Math.abs(diff_of_sums);
var diff_new;
// we assume the length is at least 2
if (A.length == 2) {
return lowest_diff;
}
// console.log("lowest_diff initially:", lowest_diff)
// var diff_of_sums_new = diff_of_sums;
// console.log("diff_of_sums initially:", diff_of_sums)
// console.log("A.slice(1):", A.slice(1))
for (var int of A.slice(1)) {
// console.log("lowest_diff", lowest_diff)
diff_new = Math.abs(sum_left - sum_right);
if (diff_new < lowest_diff) {
lowest_diff = diff_new;
}
sum_left += int;
sum_right -= int;
}
// if (Math.abs(sumleft - sumright)<ans)
// {
// ans = Math.abs(sumleft - sumright);
// }
// sumleft += A[P];
// sumright -=A[P];
// // console.log("int === -1:", int === -1);
// // diff_of_sums = diff_of_sums_new;
// console.log("lowest_diff =", lowest_diff);
// // console.log("A[index + 1] =", A[parseInt(index) + 1]);
// // console.log("parseInt(index) === 1", parseInt(index) === 1)
// diff_of_sums = Math.abs(lowest_diff - 2 * Math.abs(int));
// // console.log("diff_of_sums =", diff_of_sums);
// // console.log("diff_of_sums = Math.abs(diff_of_sums - 2 * A[index + 1]) = ", diff_of_sums_new);
// if (diff_of_sums < lowest_diff) {
// lowest_diff = diff_of_sums;
// // console.log("lowest_diff = diff_of_sums =", diff_of_sums_new)
// } else {
// return lowest_diff;
// }
// }
// console.log("lowest_diff before returning", lowest_diff);
return lowest_diff;
}
// Note that it's better to use test cases in Codility for this, but I've left here to show some.
// console.log("solution([-1000, 1000])", solution([-1000, 1000]));
// console.log("solution([2, 7, 20, 30, 1])", solution([2, 7, 20, 30, 1])); // sum 60, smallest diff = |29 - 31| = 2
// console.log("solution([-2, -7, -20, -30, -1])", solution([-2, -7, -20, -30, -1])); // sum -60, smallest diff = 2
// console.log("solution([-1, -1]):", solution([-1, -1]));
// console.log("solution([-2, -1]):", solution([-2, -1]));
// console.log("solution([-2, -1, -3]):", solution([-2, -1, -3]));
// console.log("solution([]):", solution([]))
Initially I tried starting from halfway, but this made the implementation more complicated. This is what I came up with before I ditched that approach (and I can't be bothered with hacking on a solution):
function solution(A) {
// const sum = A.reduce((partial_sum, a) => partial_sum + a);
// console.log(sum);
var size = A.length;
if (size % 2 == 0) {
mid = size/2;
} else {
mid = Math.floor(size/2);
}
console.log("mid initially", mid);
var sum1 = A.slice(0, mid).reduce((partial_sum, a) => partial_sum + a);
// console.log("sum1:",sum1);
var sum2 = A.slice(mid).reduce((partial_sum, a) => partial_sum + a);
// console.log("sum2:", sum2);
var sum_diff = sum1 - sum2;
// console.log("sum_diff:", sum_diff);
if (sum_diff === 0) {
return sum_diff;
}
// sum_diff = function() {Math.abs(sum2 - sum1)};
// sum_diff = sum_diff();
var lowest_diff = Math.abs(sum_diff);
var diff_negative = (sum_diff < 0);
console.log("diff_negative initially:", diff_negative)
var crossed_over = false;
var sum_diff_new;
while (diff_negative) {
mid++;
if (mid === size) {
return lowest_diff;
}
// var sum1_new = sum1 + A[mid];
// var sum2_new = sum2 - A[mid];
// sum_diff_new = sum1_new - sum2_new = sum1 - sum2 + 2*A[mid] = sum_diff - 2*A[mid];
sum_diff_new = sum_diff - 2*A[mid];
diff_negative = (sum_diff_new < 0);
if (diff_negative = false) {
crossed_over = true;
if (lowest_diff <= sum_diff_new) {
return lowest_diff;
} else {
return sum_diff_new;
}
}
}
while(!diff_negative) {
mid--;
if (mid === -1) {
return lowest_diff;
}
// var sum1_new = sum1 - A[mid];
// var sum2_new = sum2 + A[mid];
// sum_diff_new = sum1_new - sum2_new = sum1 - sum2 - 2*A[mid] = sum_diff - 2*A[mid];
console.log("sum_diff:", sum_diff);
sum_diff_new = sum_diff + 2*A[mid];
console.log("sum_diff_new:", sum_diff_new);
diff_negative = (sum_diff_new < 0);
if (diff_negative = true) {
crossed_over = true;
var sum_diff_new_pos = Math.abs(sum_diff_new);
if (lowest_diff <= sum_diff_new_pos) {
return lowest_diff;
} else {
return sum_diff_new_pos;
}
}
}
}
// Issues: doesn't work e.g. with [-2, -1, -3] and [-2, -7, -20, -30, -1]

Categories

Resources