random values with random chances [duplicate] - javascript

I have an array and returning random values.
const array = [ 1, 2 ,3 ,4 ,5, 6, 7, 8]
const rand = array[~~(Math.random() * array.length)]
I would like to return a random element of the array, but with a weighted probability that higher indexes (indices) are less likely to be returned. i.e 8 is less likely to be returned than 1.
How can I achieve this?.

You can use a trick that clones the origin array to a new array by weighted probability.
You can modify it by:
increase weight on which item you want to show more
decrease weight on which item you want to show less.
You can check the below demo:
const array = [ 1, 2 ,3 ,4 ,5, 6, 7, 8 ]
const weight = [ 8, 7, 6, 5, 4, 3, 2, 1 ];
let randomArray = [];
array.forEach((item, index) => {
var clone = Array(weight[index]).fill(item);
randomArray.push(...clone);
});
const result = randomArray[~~(Math.random() * randomArray.length)]
console.log('random value:', result);

Here is an efficient method of achieving this. This method uses binary searching (although it is modified to suit your needs).
Here is a summary of how it works:
You represent the probabilites of certain elements getting picked in an array. So if you have the probabilities 50% for "A", 20% for "B", 10% for C, 5% for D, 5% for E, 0.1% for F and 9.9% for G, this would be [.5, .2, .1, .05, .05, .001, .099] in an array. However this is no good because we cannot use it in binary searching as it is not sorted - but if we sort it the probabilities will no longer correspond to our letter-array ([A,B,C,D,E,F,G]). So instead we need to add up each of the probabilities until we end up with 1. Now the probability-array looks like so: [.5, .7, .8, .85, .9, .901, 1]. Now it is sorted, and still corresponds to the array of letters above.
Now we create a random fractional number between 0 and the highest value in the probability array. Math.random() is perfect for that.
Now we look to see which value in the array of probabilities array is closest to this fraction. But there is one catch - the "closest" value cannot be smaller than the fraction.
Once we have the index of this "closest" value, we can use the same index to pick a value from the letters-array. Here is an example in JavaScript:
function find(arr, x , start=0, end=arr.length) {
if(end < start) return -1;
else if(end == start) return end;
const mid = Math.floor((start + end) / 2);
if(arr[mid] === x) return mid+1;
else if(arr[mid] < x) return find(arr, x, mid+1, end);
else
return find(arr, x, start, mid);
};
const table_of_corresponding_probabilities = [.5,.7,.8,.85,.9,.901,1];
const values_to_pick_from = ["A", "B", "C", "D", "E", "F", "G"];
function weighted_random_pick(items, weights) {
return items[find(weights, Math.random())];
};
console.log(weighted_random_pick(values_to_pick_from, table_of_corresponding_probabilities));
So, with these probabilities we should get As 50% of the time, and other letters the rest of the time. Here is a test testing the randomness of the algorithm above:
function find(arr, x , start=0, end=arr.length) {
if(end < start) return -1;
else if(end == start) return end;
const mid = Math.floor((start + end) / 2);
if(arr[mid] === x) return mid+1;
else if(arr[mid] < x) return find(arr, x, mid+1, end);
else
return find(arr, x, start, mid);
};
const prob = [.5,.7,.8,.85,.9,.901,1];
const vals = ["A", "B", "C", "D", "E", "F", "G"];
const results = {A:0, B:0, C:0, D:0, E:0, F:0, G:0};
const times_it_ran = 160000;
for(let i = 0; i<times_it_ran; i++) {
results[vals[find(prob, Math.random())]]++
};
for(letter in results) {
console.log(letter+":",(results[letter]/(times_it_ran/100)).toFixed(3),"%");
};
When you run the above snippet you should find the percentage of times each letter was picked was close to the intended probability of that letter being picked. Of course it will never be absolutely equal because after all it is random (or at least pseudo-random).
Ok, what about speed and efficiency? Let's test that too:
function find(arr, x , start=0, end=arr.length) {
if(end < start) return -1;
else if(end == start) return end;
const mid = Math.floor((start + end) / 2);
if(arr[mid] === x) return mid+1;
else if(arr[mid] < x) return find(arr, x, mid+1, end);
else
return find(arr, x, start, mid);
};
const array_length = 330000;
const probs = Array.apply(null, {length: array_length}).map((x,i) => (i??0)/(array_length-1)); // Note: this way of creating an array means that each value has an equal chance of getting picked but the array is still very long;
const vals = Array.apply(null, {length: array_length}).map(Function.call, String);
const time = func => {
console.time("timer");
func();
console.timeEnd("timer");
};
// Now time the time it takes to search within this LONG array:
function button_click() {
var x = time(() => {
vals[find(probs, Math.random())];
});
};
<button onclick="button_click();">Run test</button>
As you can see, the tests are pretty fast. Mine averaged at about 2ms. However this only searches in an array of length 3.3e5. This is the value I have chosen because otherwise I get a range error (limitation of built-in function Array.apply). So here I have done the same test, but used a different method of generating massive arrays (a for loop... i know it's probably the worst method but it does the job).
function find(arr, x , start=0, end=arr.length) {
if(end < start) return -1;
else if(end == start) return end;
const mid = Math.floor((start + end) / 2);
if(arr[mid] === x) return mid+1;
else if(arr[mid] < x) return find(arr, x, mid+1, end);
else
return find(arr, x, start, mid);
};
const len = 75e6; // 75 million elements in this array!
let probs = [];
for(let i = 0; i < 1; i+=(1/len)) {
probs.push(i);
};
const time = func => {
console.time("timer");
func();
console.timeEnd("timer");
};
// Now time the time it takes to search within this LONG array:
function button_click() {
var x = time(() => {
find(probs, Math.random());
});
};
<button onclick="button_click();">Run test</button>
So after running this test with 75 million elements, what do we find?
The first test is marginally slower than the tests we previously ran (with 3.3e5 elements), and the rest average at about 2ms to 2.25ms . So this is (2+2.25)/2 - avg time from last tests = 2.125-2 = 0.125 0.125ms slower than searching with an array with 227 TIMES less elements. That is the extent to which binary search is efficient. Actually I'd like to suggest that a part of that 0.125ms delay could be due to the fact that the CPU cores are very hot due to that bad method of building an array. Yes, I'm talking about the 75 million iterations we had to complete just to create that array!
Hope you found the efficiency helpful! If you would like to use this algorithm just use the first snippet I gave you, everything is much more readable there than in the last few snippets.

Related

Return random words from a json array [duplicate]

I have an array like this:
var arr1 = ["a", "b", "c", "d"];
How can I randomize / shuffle it?
The de-facto unbiased shuffle algorithm is the Fisher-Yates (aka Knuth) Shuffle.
You can see a great visualization here (and the original post linked to this)
function shuffle(array) {
let currentIndex = array.length, randomIndex;
// While there remain elements to shuffle.
while (currentIndex != 0) {
// Pick a remaining element.
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex--;
// And swap it with the current element.
[array[currentIndex], array[randomIndex]] = [
array[randomIndex], array[currentIndex]];
}
return array;
}
// Used like so
var arr = [2, 11, 37, 42];
shuffle(arr);
console.log(arr);
Some more info about the algorithm used.
Here's a JavaScript implementation of the Durstenfeld shuffle, an optimized version of Fisher-Yates:
/* Randomize array in-place using Durstenfeld shuffle algorithm */
function shuffleArray(array) {
for (var i = array.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
It picks a random element for each original array element, and excludes it from the next draw, like picking randomly from a deck of cards.
This clever exclusion swaps the picked element with the current one, then picks the next random element from the remainder, looping backwards for optimal efficiency, ensuring the random pick is simplified (it can always start at 0), and thereby skipping the final element.
Algorithm runtime is O(n). Note that the shuffle is done in-place so if you don't want to modify the original array, first make a copy of it with .slice(0).
EDIT: Updating to ES6 / ECMAScript 2015
The new ES6 allows us to assign two variables at once. This is especially handy when we want to swap the values of two variables, as we can do it in one line of code. Here is a shorter form of the same function, using this feature.
function shuffleArray(array) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
}
You can do it easily with map and sort:
let unshuffled = ['hello', 'a', 't', 'q', 1, 2, 3, {cats: true}]
let shuffled = unshuffled
.map(value => ({ value, sort: Math.random() }))
.sort((a, b) => a.sort - b.sort)
.map(({ value }) => value)
console.log(shuffled)
We put each element in the array in an object, and give it a random sort key
We sort using the random key
We unmap to get the original objects
You can shuffle polymorphic arrays, and the sort is as random as Math.random, which is good enough for most purposes.
Since the elements are sorted against consistent keys that are not regenerated each iteration, and each comparison pulls from the same distribution, any non-randomness in the distribution of Math.random is canceled out.
Speed
Time complexity is O(N log N), same as quick sort. Space complexity is O(N). This is not as efficient as a Fischer Yates shuffle but, in my opinion, the code is significantly shorter and more functional. If you have a large array you should certainly use Fischer Yates. If you have a small array with a few hundred items, you might do this.
Warning!
The use of this algorithm is not recommended, because it is inefficient and strongly biased; see comments. It is being left here for future reference, because the idea is not that rare.
[1,2,3,4,5,6].sort( () => .5 - Math.random() );
This https://javascript.info/array-methods#shuffle-an-array tutorial explains the differences straightforwardly.
Use the underscore.js library. The method _.shuffle() is nice for this case.
Here is an example with the method:
var _ = require("underscore");
var arr = [1,2,3,4,5,6];
// Testing _.shuffle
var testShuffle = function () {
var indexOne = 0;
var stObj = {
'0': 0,
'1': 1,
'2': 2,
'3': 3,
'4': 4,
'5': 5
};
for (var i = 0; i < 1000; i++) {
arr = _.shuffle(arr);
indexOne = _.indexOf(arr, 1);
stObj[indexOne] ++;
}
console.log(stObj);
};
testShuffle();
One could (but should NOT) use it as a protoype from Array:
From ChristopheD:
Array.prototype.shuffle = function() {
var i = this.length, j, temp;
if ( i == 0 ) return this;
while ( --i ) {
j = Math.floor( Math.random() * ( i + 1 ) );
temp = this[i];
this[i] = this[j];
this[j] = temp;
}
return this;
}
NEW!
Shorter & probably *faster Fisher-Yates shuffle algorithm
it uses while---
bitwise to floor (numbers up to 10 decimal digits (32bit))
removed unecessary closures & other stuff
function fy(a,b,c,d){//array,placeholder,placeholder,placeholder
c=a.length;while(c)b=Math.random()*(--c+1)|0,d=a[c],a[c]=a[b],a[b]=d
}
script size (with fy as function name): 90bytes
DEMO
http://jsfiddle.net/vvpoma8w/
*faster probably on all browsers except chrome.
If you have any questions just ask.
EDIT
yes it is faster
PERFORMANCE: http://jsperf.com/fyshuffle
using the top voted functions.
EDIT
There was a calculation in excess (don't need --c+1) and noone noticed
shorter(4bytes)&faster(test it!).
function fy(a,b,c,d){//array,placeholder,placeholder,placeholder
c=a.length;while(c)b=Math.random()*c--|0,d=a[c],a[c]=a[b],a[b]=d
}
Caching somewhere else var rnd=Math.random and then use rnd() would also increase slightly the performance on big arrays.
http://jsfiddle.net/vvpoma8w/2/
Readable version (use the original version. this is slower, vars are useless, like the closures & ";", the code itself is also shorter ... maybe read this How to 'minify' Javascript code , btw you are not able to compress the following code in a javascript minifiers like the above one.)
function fisherYates( array ){
var count = array.length,
randomnumber,
temp;
while( count ){
randomnumber = Math.random() * count-- | 0;
temp = array[count];
array[count] = array[randomnumber];
array[randomnumber] = temp
}
}
Shuffle Array In place
function shuffleArr (array){
for (var i = array.length - 1; i > 0; i--) {
var rand = Math.floor(Math.random() * (i + 1));
[array[i], array[rand]] = [array[rand], array[i]]
}
}
ES6 Pure, Iterative
const getShuffledArr = arr => {
const newArr = arr.slice()
for (let i = newArr.length - 1; i > 0; i--) {
const rand = Math.floor(Math.random() * (i + 1));
[newArr[i], newArr[rand]] = [newArr[rand], newArr[i]];
}
return newArr
};
Reliability and Performance Test
Some solutions on this page aren't reliable (they only partially randomise the array). Other solutions are significantly less efficient. With testShuffleArrayFun (see below) we can test array shuffling functions for reliability and performance.
function testShuffleArrayFun(getShuffledArrayFun){
const arr = [0,1,2,3,4,5,6,7,8,9]
var countArr = arr.map(el=>{
return arr.map(
el=> 0
)
}) // For each possible position in the shuffledArr and for
// each possible value, we'll create a counter.
const t0 = performance.now()
const n = 1000000
for (var i=0 ; i<n ; i++){
// We'll call getShuffledArrayFun n times.
// And for each iteration, we'll increment the counter.
var shuffledArr = getShuffledArrayFun(arr)
shuffledArr.forEach(
(value,key)=>{countArr[key][value]++}
)
}
const t1 = performance.now()
console.log(`Count Values in position`)
console.table(countArr)
const frequencyArr = countArr.map( positionArr => (
positionArr.map(
count => count/n
)
))
console.log("Frequency of value in position")
console.table(frequencyArr)
console.log(`total time: ${t1-t0}`)
}
Other Solutions
Other solutions just for fun.
ES6 Pure, Recursive
const getShuffledArr = arr => {
if (arr.length === 1) {return arr};
const rand = Math.floor(Math.random() * arr.length);
return [arr[rand], ...getShuffledArr(arr.filter((_, i) => i != rand))];
};
ES6 Pure using array.map
function getShuffledArr (arr){
return [...arr].map( (_, i, arrCopy) => {
var rand = i + ( Math.floor( Math.random() * (arrCopy.length - i) ) );
[arrCopy[rand], arrCopy[i]] = [arrCopy[i], arrCopy[rand]]
return arrCopy[i]
})
}
ES6 Pure using array.reduce
function getShuffledArr (arr){
return arr.reduce(
(newArr, _, i) => {
var rand = i + ( Math.floor( Math.random() * (newArr.length - i) ) );
[newArr[rand], newArr[i]] = [newArr[i], newArr[rand]]
return newArr
}, [...arr]
)
}
Edit: This answer is incorrect
See comments and https://stackoverflow.com/a/18650169/28234. It is being left here for reference because the idea isn't rare.
A very simple way for small arrays is simply this:
const someArray = [1, 2, 3, 4, 5];
someArray.sort(() => Math.random() - 0.5);
It's probably not very efficient, but for small arrays this works just fine. Here's an example so you can see how random (or not) it is, and whether it fits your usecase or not.
const resultsEl = document.querySelector('#results');
const buttonEl = document.querySelector('#trigger');
const generateArrayAndRandomize = () => {
const someArray = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
someArray.sort(() => Math.random() - 0.5);
return someArray;
};
const renderResultsToDom = (results, el) => {
el.innerHTML = results.join(' ');
};
buttonEl.addEventListener('click', () => renderResultsToDom(generateArrayAndRandomize(), resultsEl));
<h1>Randomize!</h1>
<button id="trigger">Generate</button>
<p id="results">0 1 2 3 4 5 6 7 8 9</p>
Adding to #Laurens Holsts answer. This is 50% compressed.
function shuffleArray(d) {
for (var c = d.length - 1; c > 0; c--) {
var b = Math.floor(Math.random() * (c + 1));
var a = d[c];
d[c] = d[b];
d[b] = a;
}
return d
};
I found this variant hanging out in the "deleted by author" answers on a duplicate of this question. Unlike some of the other answers that have many upvotes already, this is:
Actually random
Not in-place (hence the shuffled name rather than shuffle)
Not already present here with multiple variants
Here's a jsfiddle showing it in use.
Array.prototype.shuffled = function() {
return this.map(function(n){ return [Math.random(), n] })
.sort().map(function(n){ return n[1] });
}
With ES2015 you can use this one:
Array.prototype.shuffle = function() {
let m = this.length, i;
while (m) {
i = (Math.random() * m--) >>> 0;
[this[m], this[i]] = [this[i], this[m]]
}
return this;
}
Usage:
[1, 2, 3, 4, 5, 6, 7].shuffle();
Warning!
Using this answer for randomizing large arrays, cryptography, or any other application requiring true randomness is not recommended, due to its bias and inefficiency. Elements position is only semi-randomized, and they will tend to stay closer to their original position. See https://stackoverflow.com/a/18650169/28234.
You can arbitrarily decide whether to return 1 : -1 by using Math.random:
[1, 2, 3, 4].sort(() => (Math.random() > 0.5) ? 1 : -1)
Try running the following example:
const array = [1, 2, 3, 4];
// Based on the value returned by Math.Random,
// the decision is arbitrarily made whether to return 1 : -1
const shuffeled = array.sort(() => {
const randomTrueOrFalse = Math.random() > 0.5;
return randomTrueOrFalse ? 1 : -1
});
console.log(shuffeled);
//one line solution
shuffle = (array) => array.sort(() => Math.random() - 0.5);
//Demo
let arr = [1, 2, 3];
shuffle(arr);
alert(arr);
https://javascript.info/task/shuffle
Math.random() - 0.5 is a random number that may be positive or
negative, so the sorting function reorders elements randomly.
benchmarks
Let's first see the results then we'll look at each implementation of shuffle below -
splice is slow
Any solution using splice or shift in a loop is going to be very slow. Which is especially noticeable when we increase the size of the array. In a naive algorithm we -
get a rand position, i, in the input array, t
add t[i] to the output
splice position i from array t
To exaggerate the slow effect, we'll demonstrate this on an array of one million elements. The following script almost 30 seconds -
const shuffle = t =>
Array.from(sample(t, t.length))
function* sample(t, n)
{ let r = Array.from(t)
while (n > 0 && r.length)
{ const i = rand(r.length) // 1
yield r[i] // 2
r.splice(i, 1) // 3
n = n - 1
}
}
const rand = n =>
0 | Math.random() * n
function swap (t, i, j)
{ let q = t[i]
t[i] = t[j]
t[j] = q
return t
}
const size = 1e6
const bigarray = Array.from(Array(size), (_,i) => i)
console.time("shuffle via splice")
const result = shuffle(bigarray)
console.timeEnd("shuffle via splice")
document.body.textContent = JSON.stringify(result, null, 2)
body::before {
content: "1 million elements via splice";
font-weight: bold;
display: block;
}
pop is fast
The trick is not to splice and instead use the super efficient pop. To do this, in place of the typical splice call, you -
select the position to splice, i
swap t[i] with the last element, t[t.length - 1]
add t.pop() to the result
Now we can shuffle one million elements in less than 100 milliseconds -
const shuffle = t =>
Array.from(sample(t, t.length))
function* sample(t, n)
{ let r = Array.from(t)
while (n > 0 && r.length)
{ const i = rand(r.length) // 1
swap(r, i, r.length - 1) // 2
yield r.pop() // 3
n = n - 1
}
}
const rand = n =>
0 | Math.random() * n
function swap (t, i, j)
{ let q = t[i]
t[i] = t[j]
t[j] = q
return t
}
const size = 1e6
const bigarray = Array.from(Array(size), (_,i) => i)
console.time("shuffle via pop")
const result = shuffle(bigarray)
console.timeEnd("shuffle via pop")
document.body.textContent = JSON.stringify(result, null, 2)
body::before {
content: "1 million elements via pop";
font-weight: bold;
display: block;
}
even faster
The two implementations of shuffle above produce a new output array. The input array is not modified. This is my preferred way of working however you can increase the speed even more by shuffling in place.
Below shuffle one million elements in less than 10 milliseconds -
function shuffle (t)
{ let last = t.length
let n
while (last > 0)
{ n = rand(last)
swap(t, n, --last)
}
}
const rand = n =>
0 | Math.random() * n
function swap (t, i, j)
{ let q = t[i]
t[i] = t[j]
t[j] = q
return t
}
const size = 1e6
const bigarray = Array.from(Array(size), (_,i) => i)
console.time("shuffle in place")
shuffle(bigarray)
console.timeEnd("shuffle in place")
document.body.textContent = JSON.stringify(bigarray, null, 2)
body::before {
content: "1 million elements in place";
font-weight: bold;
display: block;
}
var shuffle = function(array) {
temp = [];
originalLength = array.length;
for (var i = 0; i < originalLength; i++) {
temp.push(array.splice(Math.floor(Math.random()*array.length),1));
}
return temp;
};
A recursive solution:
function shuffle(a,b){
return a.length==0?b:function(c){
return shuffle(a,(b||[]).concat(c));
}(a.splice(Math.floor(Math.random()*a.length),1));
};
Fisher-Yates shuffle in javascript. I'm posting this here because the use of two utility functions (swap and randInt) clarifies the algorithm compared to the other answers here.
function swap(arr, i, j) {
// swaps two elements of an array in place
var temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
function randInt(max) {
// returns random integer between 0 and max-1 inclusive.
return Math.floor(Math.random()*max);
}
function shuffle(arr) {
// For each slot in the array (starting at the end),
// pick an element randomly from the unplaced elements and
// place it in the slot, exchanging places with the
// element in the slot.
for(var slot = arr.length - 1; slot > 0; slot--){
var element = randInt(slot+1);
swap(arr, element, slot);
}
}
Modern short inline solution using ES6 features:
['a','b','c','d'].map(x => [Math.random(), x]).sort(([a], [b]) => a - b).map(([_, x]) => x);
(for educational purposes)
Using Fisher-Yates shuffle algorithm and ES6:
// Original array
let array = ['a', 'b', 'c', 'd'];
// Create a copy of the original array to be randomized
let shuffle = [...array];
// Defining function returning random value from i to N
const getRandomValue = (i, N) => Math.floor(Math.random() * (N - i) + i);
// Shuffle a pair of two elements at random position j
shuffle.forEach( (elem, i, arr, j = getRandomValue(i, arr.length)) => [arr[i], arr[j]] = [arr[j], arr[i]] );
console.log(shuffle);
// ['d', 'a', 'b', 'c']
First of all, have a look here for a great visual comparison of different sorting methods in javascript.
Secondly, if you have a quick look at the link above you'll find that the random order sort seems to perform relatively well compared to the other methods, while being extremely easy and fast to implement as shown below:
function shuffle(array) {
var random = array.map(Math.random);
array.sort(function(a, b) {
return random[array.indexOf(a)] - random[array.indexOf(b)];
});
}
Edit: as pointed out by #gregers, the compare function is called with values rather than indices, which is why you need to use indexOf. Note that this change makes the code less suitable for larger arrays as indexOf runs in O(n) time.
All the other answers are based on Math.random() which is fast but not suitable for cryptgraphic level randomization.
The below code is using the well known Fisher-Yates algorithm while utilizing Web Cryptography API for cryptographic level of randomization.
var d = [1,2,3,4,5,6,7,8,9,10];
function shuffle(a) {
var x, t, r = new Uint32Array(1);
for (var i = 0, c = a.length - 1, m = a.length; i < c; i++, m--) {
crypto.getRandomValues(r);
x = Math.floor(r / 65536 / 65536 * m) + i;
t = a [i], a [i] = a [x], a [x] = t;
}
return a;
}
console.log(shuffle(d));
a shuffle function that doesn't change the source array
Update: Here I'm suggesting a relatively simple (not from complexity perspective) and short algorithm that will do just fine with small sized arrays, but it's definitely going to cost a lot more than the classic Durstenfeld algorithm when you deal with huge arrays. You can find the Durstenfeld in one of the top replies to this question.
Original answer:
If you don't wish your shuffle function to mutate the source array, you can copy it to a local variable, then do the rest with a simple shuffling logic.
function shuffle(array) {
var result = [], source = array.concat([]);
while (source.length) {
let index = Math.floor(Math.random() * source.length);
result.push(source[index]);
source.splice(index, 1);
}
return result;
}
Shuffling logic: pick up a random index, then add the corresponding element to the result array and delete it from the source array copy. Repeat this action until the source array gets empty.
And if you really want it short, here's how far I could get:
function shuffle(array) {
var result = [], source = array.concat([]);
while (source.length) {
let index = Math.floor(Math.random() * source.length);
result.push(source.splice(index, 1)[0]);
}
return result;
}
Here is the EASIEST one,
function shuffle(array) {
return array.sort(() => Math.random() - 0.5);
}
for further example, you can check it here
We're still shuffling arrays in 2019, so here goes my approach, which seems to be neat and fast to me:
const src = [...'abcdefg'];
const shuffle = arr =>
[...arr].reduceRight((res,_,__,s) =>
(res.push(s.splice(0|Math.random()*s.length,1)[0]), res),[]);
console.log(shuffle(src));
.as-console-wrapper {min-height: 100%}
You can do it easily with:
// array
var fruits = ["Banana", "Orange", "Apple", "Mango"];
// random
fruits.sort(function(a, b){return 0.5 - Math.random()});
// out
console.log(fruits);
Please reference at JavaScript Sorting Arrays
A simple modification of CoolAJ86's answer that does not modify the original array:
/**
* Returns a new array whose contents are a shuffled copy of the original array.
* #param {Array} The items to shuffle.
* https://stackoverflow.com/a/2450976/1673761
* https://stackoverflow.com/a/44071316/1673761
*/
const shuffle = (array) => {
let currentIndex = array.length;
let temporaryValue;
let randomIndex;
const newArray = array.slice();
// While there remains elements to shuffle...
while (currentIndex) {
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex -= 1;
// Swap it with the current element.
temporaryValue = newArray[currentIndex];
newArray[currentIndex] = newArray[randomIndex];
newArray[randomIndex] = temporaryValue;
}
return newArray;
};
For those of us who are not very gifted but have access to the wonders of lodash, there is such a thing as lodash.shuffle.
I found this useful:
const shuffle = (array: any[]) => {
return array.slice().sort(() => Math.random() - 0.5);
}
console.log(shuffle([1,2,3,4,5,6,7,8,9,10]));
// Output: [4, 3, 8, 10, 1, 7, 9, 2, 6, 5]
yet another implementation of Fisher-Yates, using strict mode:
function shuffleArray(a) {
"use strict";
var i, t, j;
for (i = a.length - 1; i > 0; i -= 1) {
t = a[i];
j = Math.floor(Math.random() * (i + 1));
a[i] = a[j];
a[j] = t;
}
return a;
}

How can I generate all the combos taking into account the weight of each item in Javascript? [duplicate]

I have an array and returning random values.
const array = [ 1, 2 ,3 ,4 ,5, 6, 7, 8]
const rand = array[~~(Math.random() * array.length)]
I would like to return a random element of the array, but with a weighted probability that higher indexes (indices) are less likely to be returned. i.e 8 is less likely to be returned than 1.
How can I achieve this?.
You can use a trick that clones the origin array to a new array by weighted probability.
You can modify it by:
increase weight on which item you want to show more
decrease weight on which item you want to show less.
You can check the below demo:
const array = [ 1, 2 ,3 ,4 ,5, 6, 7, 8 ]
const weight = [ 8, 7, 6, 5, 4, 3, 2, 1 ];
let randomArray = [];
array.forEach((item, index) => {
var clone = Array(weight[index]).fill(item);
randomArray.push(...clone);
});
const result = randomArray[~~(Math.random() * randomArray.length)]
console.log('random value:', result);
Here is an efficient method of achieving this. This method uses binary searching (although it is modified to suit your needs).
Here is a summary of how it works:
You represent the probabilites of certain elements getting picked in an array. So if you have the probabilities 50% for "A", 20% for "B", 10% for C, 5% for D, 5% for E, 0.1% for F and 9.9% for G, this would be [.5, .2, .1, .05, .05, .001, .099] in an array. However this is no good because we cannot use it in binary searching as it is not sorted - but if we sort it the probabilities will no longer correspond to our letter-array ([A,B,C,D,E,F,G]). So instead we need to add up each of the probabilities until we end up with 1. Now the probability-array looks like so: [.5, .7, .8, .85, .9, .901, 1]. Now it is sorted, and still corresponds to the array of letters above.
Now we create a random fractional number between 0 and the highest value in the probability array. Math.random() is perfect for that.
Now we look to see which value in the array of probabilities array is closest to this fraction. But there is one catch - the "closest" value cannot be smaller than the fraction.
Once we have the index of this "closest" value, we can use the same index to pick a value from the letters-array. Here is an example in JavaScript:
function find(arr, x , start=0, end=arr.length) {
if(end < start) return -1;
else if(end == start) return end;
const mid = Math.floor((start + end) / 2);
if(arr[mid] === x) return mid+1;
else if(arr[mid] < x) return find(arr, x, mid+1, end);
else
return find(arr, x, start, mid);
};
const table_of_corresponding_probabilities = [.5,.7,.8,.85,.9,.901,1];
const values_to_pick_from = ["A", "B", "C", "D", "E", "F", "G"];
function weighted_random_pick(items, weights) {
return items[find(weights, Math.random())];
};
console.log(weighted_random_pick(values_to_pick_from, table_of_corresponding_probabilities));
So, with these probabilities we should get As 50% of the time, and other letters the rest of the time. Here is a test testing the randomness of the algorithm above:
function find(arr, x , start=0, end=arr.length) {
if(end < start) return -1;
else if(end == start) return end;
const mid = Math.floor((start + end) / 2);
if(arr[mid] === x) return mid+1;
else if(arr[mid] < x) return find(arr, x, mid+1, end);
else
return find(arr, x, start, mid);
};
const prob = [.5,.7,.8,.85,.9,.901,1];
const vals = ["A", "B", "C", "D", "E", "F", "G"];
const results = {A:0, B:0, C:0, D:0, E:0, F:0, G:0};
const times_it_ran = 160000;
for(let i = 0; i<times_it_ran; i++) {
results[vals[find(prob, Math.random())]]++
};
for(letter in results) {
console.log(letter+":",(results[letter]/(times_it_ran/100)).toFixed(3),"%");
};
When you run the above snippet you should find the percentage of times each letter was picked was close to the intended probability of that letter being picked. Of course it will never be absolutely equal because after all it is random (or at least pseudo-random).
Ok, what about speed and efficiency? Let's test that too:
function find(arr, x , start=0, end=arr.length) {
if(end < start) return -1;
else if(end == start) return end;
const mid = Math.floor((start + end) / 2);
if(arr[mid] === x) return mid+1;
else if(arr[mid] < x) return find(arr, x, mid+1, end);
else
return find(arr, x, start, mid);
};
const array_length = 330000;
const probs = Array.apply(null, {length: array_length}).map((x,i) => (i??0)/(array_length-1)); // Note: this way of creating an array means that each value has an equal chance of getting picked but the array is still very long;
const vals = Array.apply(null, {length: array_length}).map(Function.call, String);
const time = func => {
console.time("timer");
func();
console.timeEnd("timer");
};
// Now time the time it takes to search within this LONG array:
function button_click() {
var x = time(() => {
vals[find(probs, Math.random())];
});
};
<button onclick="button_click();">Run test</button>
As you can see, the tests are pretty fast. Mine averaged at about 2ms. However this only searches in an array of length 3.3e5. This is the value I have chosen because otherwise I get a range error (limitation of built-in function Array.apply). So here I have done the same test, but used a different method of generating massive arrays (a for loop... i know it's probably the worst method but it does the job).
function find(arr, x , start=0, end=arr.length) {
if(end < start) return -1;
else if(end == start) return end;
const mid = Math.floor((start + end) / 2);
if(arr[mid] === x) return mid+1;
else if(arr[mid] < x) return find(arr, x, mid+1, end);
else
return find(arr, x, start, mid);
};
const len = 75e6; // 75 million elements in this array!
let probs = [];
for(let i = 0; i < 1; i+=(1/len)) {
probs.push(i);
};
const time = func => {
console.time("timer");
func();
console.timeEnd("timer");
};
// Now time the time it takes to search within this LONG array:
function button_click() {
var x = time(() => {
find(probs, Math.random());
});
};
<button onclick="button_click();">Run test</button>
So after running this test with 75 million elements, what do we find?
The first test is marginally slower than the tests we previously ran (with 3.3e5 elements), and the rest average at about 2ms to 2.25ms . So this is (2+2.25)/2 - avg time from last tests = 2.125-2 = 0.125 0.125ms slower than searching with an array with 227 TIMES less elements. That is the extent to which binary search is efficient. Actually I'd like to suggest that a part of that 0.125ms delay could be due to the fact that the CPU cores are very hot due to that bad method of building an array. Yes, I'm talking about the 75 million iterations we had to complete just to create that array!
Hope you found the efficiency helpful! If you would like to use this algorithm just use the first snippet I gave you, everything is much more readable there than in the last few snippets.

Weighted probability random choice array

I have an array and returning random values.
const array = [ 1, 2 ,3 ,4 ,5, 6, 7, 8]
const rand = array[~~(Math.random() * array.length)]
I would like to return a random element of the array, but with a weighted probability that higher indexes (indices) are less likely to be returned. i.e 8 is less likely to be returned than 1.
How can I achieve this?.
You can use a trick that clones the origin array to a new array by weighted probability.
You can modify it by:
increase weight on which item you want to show more
decrease weight on which item you want to show less.
You can check the below demo:
const array = [ 1, 2 ,3 ,4 ,5, 6, 7, 8 ]
const weight = [ 8, 7, 6, 5, 4, 3, 2, 1 ];
let randomArray = [];
array.forEach((item, index) => {
var clone = Array(weight[index]).fill(item);
randomArray.push(...clone);
});
const result = randomArray[~~(Math.random() * randomArray.length)]
console.log('random value:', result);
Here is an efficient method of achieving this. This method uses binary searching (although it is modified to suit your needs).
Here is a summary of how it works:
You represent the probabilites of certain elements getting picked in an array. So if you have the probabilities 50% for "A", 20% for "B", 10% for C, 5% for D, 5% for E, 0.1% for F and 9.9% for G, this would be [.5, .2, .1, .05, .05, .001, .099] in an array. However this is no good because we cannot use it in binary searching as it is not sorted - but if we sort it the probabilities will no longer correspond to our letter-array ([A,B,C,D,E,F,G]). So instead we need to add up each of the probabilities until we end up with 1. Now the probability-array looks like so: [.5, .7, .8, .85, .9, .901, 1]. Now it is sorted, and still corresponds to the array of letters above.
Now we create a random fractional number between 0 and the highest value in the probability array. Math.random() is perfect for that.
Now we look to see which value in the array of probabilities array is closest to this fraction. But there is one catch - the "closest" value cannot be smaller than the fraction.
Once we have the index of this "closest" value, we can use the same index to pick a value from the letters-array. Here is an example in JavaScript:
function find(arr, x , start=0, end=arr.length) {
if(end < start) return -1;
else if(end == start) return end;
const mid = Math.floor((start + end) / 2);
if(arr[mid] === x) return mid+1;
else if(arr[mid] < x) return find(arr, x, mid+1, end);
else
return find(arr, x, start, mid);
};
const table_of_corresponding_probabilities = [.5,.7,.8,.85,.9,.901,1];
const values_to_pick_from = ["A", "B", "C", "D", "E", "F", "G"];
function weighted_random_pick(items, weights) {
return items[find(weights, Math.random())];
};
console.log(weighted_random_pick(values_to_pick_from, table_of_corresponding_probabilities));
So, with these probabilities we should get As 50% of the time, and other letters the rest of the time. Here is a test testing the randomness of the algorithm above:
function find(arr, x , start=0, end=arr.length) {
if(end < start) return -1;
else if(end == start) return end;
const mid = Math.floor((start + end) / 2);
if(arr[mid] === x) return mid+1;
else if(arr[mid] < x) return find(arr, x, mid+1, end);
else
return find(arr, x, start, mid);
};
const prob = [.5,.7,.8,.85,.9,.901,1];
const vals = ["A", "B", "C", "D", "E", "F", "G"];
const results = {A:0, B:0, C:0, D:0, E:0, F:0, G:0};
const times_it_ran = 160000;
for(let i = 0; i<times_it_ran; i++) {
results[vals[find(prob, Math.random())]]++
};
for(letter in results) {
console.log(letter+":",(results[letter]/(times_it_ran/100)).toFixed(3),"%");
};
When you run the above snippet you should find the percentage of times each letter was picked was close to the intended probability of that letter being picked. Of course it will never be absolutely equal because after all it is random (or at least pseudo-random).
Ok, what about speed and efficiency? Let's test that too:
function find(arr, x , start=0, end=arr.length) {
if(end < start) return -1;
else if(end == start) return end;
const mid = Math.floor((start + end) / 2);
if(arr[mid] === x) return mid+1;
else if(arr[mid] < x) return find(arr, x, mid+1, end);
else
return find(arr, x, start, mid);
};
const array_length = 330000;
const probs = Array.apply(null, {length: array_length}).map((x,i) => (i??0)/(array_length-1)); // Note: this way of creating an array means that each value has an equal chance of getting picked but the array is still very long;
const vals = Array.apply(null, {length: array_length}).map(Function.call, String);
const time = func => {
console.time("timer");
func();
console.timeEnd("timer");
};
// Now time the time it takes to search within this LONG array:
function button_click() {
var x = time(() => {
vals[find(probs, Math.random())];
});
};
<button onclick="button_click();">Run test</button>
As you can see, the tests are pretty fast. Mine averaged at about 2ms. However this only searches in an array of length 3.3e5. This is the value I have chosen because otherwise I get a range error (limitation of built-in function Array.apply). So here I have done the same test, but used a different method of generating massive arrays (a for loop... i know it's probably the worst method but it does the job).
function find(arr, x , start=0, end=arr.length) {
if(end < start) return -1;
else if(end == start) return end;
const mid = Math.floor((start + end) / 2);
if(arr[mid] === x) return mid+1;
else if(arr[mid] < x) return find(arr, x, mid+1, end);
else
return find(arr, x, start, mid);
};
const len = 75e6; // 75 million elements in this array!
let probs = [];
for(let i = 0; i < 1; i+=(1/len)) {
probs.push(i);
};
const time = func => {
console.time("timer");
func();
console.timeEnd("timer");
};
// Now time the time it takes to search within this LONG array:
function button_click() {
var x = time(() => {
find(probs, Math.random());
});
};
<button onclick="button_click();">Run test</button>
So after running this test with 75 million elements, what do we find?
The first test is marginally slower than the tests we previously ran (with 3.3e5 elements), and the rest average at about 2ms to 2.25ms . So this is (2+2.25)/2 - avg time from last tests = 2.125-2 = 0.125 0.125ms slower than searching with an array with 227 TIMES less elements. That is the extent to which binary search is efficient. Actually I'd like to suggest that a part of that 0.125ms delay could be due to the fact that the CPU cores are very hot due to that bad method of building an array. Yes, I'm talking about the 75 million iterations we had to complete just to create that array!
Hope you found the efficiency helpful! If you would like to use this algorithm just use the first snippet I gave you, everything is much more readable there than in the last few snippets.

How to create a binary search with recursion

I am attempting to write a "binary search" which I've never done before. The code below does not work when the value searched for is 6 or 2 and I want to know what I am doing wrong and how to remedy it.
EDIT
To explain what it is suppose to do (based on my understanding) a binary search requires that an array is already sorted, it then looks for the mid-point index of an array. For example, if an array had nine indexes (0-8)the the mid point would be index 4.
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
The algorithm then determines if that mid point has a higher or lower value than the number you are searching for. All elements on the side of the array that does not contain the searched for number and that exist before the midpoint value simply get removed. If the search for value is 8 then the result would be:
[ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
array midpoint value: 5
[ 5, 6, 7, 8, 9 ]
array midpoint value: 7
[ 7, 8, 9 ]
array midpoint value: 8
Code
//_________________________________________________BEGIN notes
// Step 1. Get length of array
// Step 2. Find mid point
// Step 3. Compare if mid point is lower or higher than searched number
// Step 4. lop off unneeded side
// Step 5. go to step 1
//_________________________________________________END notes
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 44, 55];
function getMidPoint(arr, searchNumb) {
var length = arr.length;
var midPoint = Math.floor(length / 2);
var newArr = arr;
console.log(arr);
console.log("array midpoint value: " + arr[midPoint]);
if (arr[midPoint] > searchNumb) {
var newArr = arr.slice(0, arr[midPoint]);
return getMidPoint(newArr, searchNumb);
} else if (arr[midPoint] < searchNumb) {
var newArr = arr.slice(midPoint, arr.length);
return getMidPoint(newArr, searchNumb);
} else {
return arr
}
}
Language agnostic, here is the simplified flow of a recursive binary search implementation, assuming we have an (initially non-empty) array [ARR] and a target [T], where we refer to the middle element of ARR as M:
// 1. If M == T, return true
// 2. If length of ARR is 0, return false (note: step 1 short circuits, ensuring we only hit step 2 if step 1 evaluates to false)
// 3. If T < M, return the result of the recursion on the lower half of ARR
// 4. If T > M, return the result of the recursion on the the latter half of ARR
Following is solution that executes the control flow outlined above. This is similar to solutions already presented in this post, with a few noteworthy differences:
function binarySearch(arr, target, start=0, stop=(arr.length-1)) {
let midPoint = Math.floor(((stop-start)/2) + start)
switch (true) {
case arr[midPoint] === target:
return true
case stop - start === 0:
return false
case arr[midPoint] < target:
return binarySearch(arr, target, midPoint+1, stop)
case arr[midPoint] > target:
return binarySearch(arr, target, start, midPoint)
}
}
Let's unpack the main differences of this implementation:
Slice is no longer used:
We are eschewing the use of Array.prototype.slice because it is a relatively expensive operation (copying half of the current array with each recursive call!) and it is not required for the algorithm to function properly.
In place of slice, we are passing the start and stop indexes of the range of the array that we have narrowed the search down to. This keeps our heap happy by not cluttering it with (potentially many) partial, impermanent copies of the same (potentially massive) array.
We are passing two additional arguments, and they have defaults:
These arguments (start and stop) serve to keep track of the range of the array we are currently recurring on. They are our alternative to slice!
The default arguments enable us to call this recursive function exactly the same as we would when using slice (should the user not provide an explicit range when it is first called).
We are using a switch statement:
The speed of a switch statement vs. an if-else chain depends on several factors, most notably the programming language and the amount of conditionals in each. A switch statement was used here primarily for readability. It is a control flow that matches what we are concerned with handling in this recursive function: 4 discrete cases, each requiring different action. Additionally, a few individuals have a rare allergy to if-else statements that exceed 3 logical tests.
For more information on JavaScript's switch statement and its performance vs. if-else, please take a look at this post: Javascript switch vs. if...else if...else, which links to this more informative page http://archive.oreilly.com/pub/a/server-administration/excerpts/even-faster-websites/writing-efficient-javascript.html
You are slicing it wrong.
Use this code:
//_________________________________________________BEGIN notes
// Step 1. Get length of array
// Step 2. Find mid point
// Step 3. Compare if mid point is lower or higher than searched number
// Step 4. lop off unneeded side
// Step 5. go to step 1
//_________________________________________________END notes
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 44, 55];
function getMidPoint(arr, searchNumb) {
var length = arr.length;
var midPoint = Math.floor(length / 2);
var newArr = arr;
console.log(arr);
console.log("array midpoint value: " + arr[midPoint]);
if (arr[midPoint] > searchNumb) {
var newArr = arr.slice(0, midPoint);
return getMidPoint(newArr, searchNumb);
} else if (arr[midPoint] < searchNumb) {
var newArr = arr.slice(midPoint + 1, arr.length);
return getMidPoint(newArr, searchNumb);
} else {
return midPoint;
}
}
Also, if the search element is not in array, this will go on infinitely. Add a base case for that too.
I think that this line:
var newArr = arr.slice(0, arr[midPoint]);
should probably be:
var newArr = arr.slice(0, midPoint);
But I don't know if that's the only issue with your code. (It's not clear to me what the code is supposed to actually do. Right now "getMidPoint" appears to returns a smaller array containing the searched-for value.)
Probably You are already a master with Binary search. However I would like to indicate that is not necessary to create a sliding window for resolving a binary search.
function binarySearch(arr, value){
if(!arr.length) return -1;
let average = Math.floor(arr.length-1/2);
if (value === arr[average]) return average;
if (value > arr[average]) return binarySearch(arr.slice(average+1),value);
if (value < arr[average]) return binarySearch(arr.slice(0,average),value);
}
binarySearch([1,2,3,4,5],6) //-1
binarySearch([1,2,3,4,5],3) //2
Follow this steps to create the Binary search with recursion:
function binarySearch(arr, value){
1 ) implement a base case
if(!arr.length) return -1;
2 ) create a middle point
let average = Math.floor(arr.length-1/2);
3 ) if the middle point is equal to the searched valued, you found it! return the value
if (value === arr[average]) return average;
4) if the value is greater than the middle point run a new process with only the sub array starting from the middle + 1 till the end
if (value > arr[average]) return binarySearch(arr.slice(average+1),value);
5) if the value is lower than the middle point run a new process with only the sub array starting from 0 to the middle
if (value < arr[average]) return binarySearch(arr.slice(0,average),value);
}
I hope it helps!
Note: you can use a switch statement in order to not repeat if,if,if but I like it more this way, more readable.
For solving the question in recursion please find the answer and explanation below.
const BinarySearchRec = (arr, el) => {
// finding the middle index
const mid = Math.floor(arr.length / 2);
if (arr[mid] === el) {
// if the element is found then return the element.
return mid;
}
if (arr[mid] < el && mid < arr.length) {
/** here we are having the value returned from recursion as
the value can be -1 as well as a value which is in second half of the original array.**/
const retVal = BinarySearchRec(arr.slice(mid + 1, arr.length), el);
/** if value is greater than or equal to 0 then only add that value with mid
and also one as mid represents the index.
Since index starts from 0 we have to compensate it as we require the length here.**/
return retVal >= 0 ? mid + 1 + retVal : -1;
}
if (arr[mid] > el) {
// here we need not do any manipulation
return BinarySearchRec(arr.slice(0, mid), el);
}
return -1;
};
The above solutions which have been added and the one accepted fails in scenarios when the element to be found is in the second half.
There is solution with while loop which works correctly but since the question was to solve it recursively I have given a comprehensive recursive version.
There are 2 issues in your code :-
1) You are slicing it incorrectly
2) You have not put any base condition
This code should work hopefully :-
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 44, 55];
function getMidPoint(arr, searchNumb) {
var length = arr.length;
var midPoint = Math.floor(length / 2);
var newArr = arr;
console.log(arr);
console.log("array midpoint value: " + arr[midPoint]);
if (arr[midPoint] > searchNumb) {
var newArr = arr.slice(0, midPoint);
return getMidPoint(newArr, searchNumb);
} else if (arr[midPoint] < searchNumb) {
var newArr = arr.slice(midPoint+1, arr.length);
return getMidPoint(newArr, searchNumb);
} else {
return arr[midPoint];
}
}
This function would return undefined if element is not found in array.
This is fully rewritten code to achieve your goal (commented, linted).
This example doesn't have any checks for params.
Main error:
wrong slicing
Disadvantages of this approach:
recursion is slower and takes up more of the stack
slice() also there is no needed (because of the stack again)
/**
* Searches recursively number from the list
* #param {Array} list
* #param {number} item Search item
* #param {number} low Lower limit of search in the list
* #param {number} high Highest limit of search in the list
* #param {number} arrLength Length of the list
* #return {(number | null)} Number if the value is found or NULL otherwise
*/
const binarySearch = ( list, item, low, high, arrLength ) => {
while ( low <= high ) {
let mid = Math.floor((low + high) / 2);
let guess = list[mid];
if ( guess === item ) {
return mid;
} else if ( guess > item ) {
high = mid - 1;
list = list.slice( 0, mid );
return binarySearch( list, item, low, high );
} else {
low = mid + 1;
list = list.slice( low, arrLength );
return binarySearch( list, item, low, high );
}
}
return null;
};
/**
* Creates the array that contains numbers 1...N
* #param {number} n - number N
* #return {Array}
*/
const createArr = ( n ) => Array.from({length: n}, (v, k) => k + 1);
const myList = createArr( 100 );
const arrLength = myList.length;
let low = 0;
let high = arrLength - 1;
console.log( '3 ' + binarySearch( myList, 3, low, high, arrLength ) ); // 2
console.log( '-1 ' + binarySearch( myList, -1, low, high, arrLength ) ); // null
I think it's more elegant solution for binary search:
const binarySearch = ( list, item ) => {
let low = 0;
let high = list.length - 1;
while ( low <= high ) {
let mid = Math.floor((low + high) / 2);
let guess = list[mid];
if ( guess === item ) {
return mid;
} else if ( guess > item ) {
high = mid - 1;
} else {
low = mid + 1;
}
}
return null;
};
const myList = [1, 3, 5, 7, 9];
console.log( binarySearch( myList, 3 ) );
console.log( binarySearch( myList, -1 ) );
Here's my recursive binary search solution:
// arr = sorted array, val = search value
// left and right are the index pointers enclosing the search value
// e.g. binarySearch([1,5,7,9,14,17,24,29,33,38,49,52,61,62,70,80,90,95,104,107,109],70)
binarySearch = (arr,val,left=0,right=arr.length) => {
position = (left,right) => {
let pos = (left + right)/2
return Math.floor(pos)
}
let i = position(left,right)
if (arr[i] === val) {
return i
}
// Base Case: if left and midpoint index coincide then there are no more possible solutions
else if (i === left) {
return -1
}
// For this case we shift the left index pointer
else if (arr[i] < val) {
return binarySearch(arr,val,i,right)
}
// For this case we shift the right index pointer
else if (arr[i] > val) {
return binarySearch(arr,val,left,i)
}
}
Here is my approach for binary search recursively.
We don't slice the array because it is not needed if we can just pass down the indexes.
I think that will save some time.
Function will return index if the element is found and -1 if not.
l is standing for left, r is standing for right.
function binarySearch(arr, searchNumber) {
return _binarySearch(0, arr.length -1, arr, searchNumber);
function _binarySearch(l, r, arr, searchNumber) {
const mid = Math.floor((l + r) / 2);
const guess = arr[mid];
if (guess === searchNumber) { // base case
return mid;
} else if (l === r) { // end-case the element is not in the array
return -1;
} else if (guess < searchNumber) {
return _binarySearch(mid + 1, arr.length - 1, arr, searchNumber);
} else if (guess > searchNumber) {
return _binarySearch(l, mid - 1, arr, searchNumber);
}
}
}
const list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
console.log(binarySearch(list, 4));
Simple and Easy
let arr = [1,2,3,4,5];
function BinarySearch(arr, start, end, key) {
if(start > end) return -1;
let mid = Math.floor((start + end) / 2);
if(arr[mid] === key) return mid;
if(key > arr[mid]) {
return BinarySearch(arr, mid + 1, end, key);
} else if(key < arr[mid]) {
return BinarySearch(arr, start, mid -1, key);
}
}
BinarySearch([1,3,4,5], 0, arr.length - 1, 1); // it will return 0;
BinarySearch recursion Returning search element index.
Below code worked for me
function binerySearchRecursive(arr, num, start=0 end=arr.length-1){
let mid = Math.floor((start+end/2));
if(start> end){
return -1; // edge case if array has 1 element or 0
}
if(num === arr[mid])
return mid;
else if(num < arr[mid])
return binerySearchRecursive(arr, num, start, mid-1 );
else
return binerySearchRecursive(arr, num, mid+1 , end);
}
binerySearchRecursive([1,2,3,4,5], 5)
function binarySearch(arr, n) {
let mid = Math.floor(arr.length / 2);
// Base case
if (n === arr[mid]) {
return mid;
}
//Recursion
if (n > arr[mid]) {
return mid + binarySearch(arr.slice(mid, arr.length), n)
} else {
return binarySearch(arr.slice(0, mid), n)
} }
Simple solution to recursive binary search
For a recursive binary search you can try this :
function recursiveBinarySearch(lst, target, start=0, end=(lst.length-1)){
let midPoint = (Math.floor((start+end)/2));
if (start > end){
return false;
}
if (lst[midPoint] === target){
return true;
}
else{
if(lst[midPoint] < target){
return recursiveBinarySearch(lst, target, midPoint+1, end);
}
else{
return recursiveBinarySearch(lst, target, start, midPoint-1);
}
}
}
this too late but i hope this well be useful for some one :)
const items = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
let target = 30;
function binarySearch(L,R){
if(L == R){
return false;
}
let mid = Math.floor((L + R)/2);
if(mid == target){
return target;
}
if(mid > target){
binarySearch(L,mid);
}
if(mid < target){
binarySearch(mid+1,R);
}
}
binarySearch(1,items.length);
This is the most comprehensive version of binary recursive search for JavaScript. In my opinion, this is O(log n).
function binaryRecursion(arr, val) {
if (arr.length === 0) return -1
let middle = Math.floor(arr.length - 1 / 2)
if (arr[middle] === val) return middle;
if (val > arr[middle]) {
return binaryRecursion(arr.slice(middle + 1), val)
}
if (val < arr[middle]) {
return binaryRecursion(arr.slice(0, middle), val)
}
}
This returns the index of the element, not whether it exists or not.

Create array of all integers between two numbers, inclusive, in Javascript/jQuery

Say I have the following checkbox:
<input type="checkbox" value="1-25" />
To get the two numbers that define the boundaries of range I'm looking for, I use the following jQuery:
var value = $(this).val();
var lowEnd = Number(value.split('-')[0]);
var highEnd = Number(value.split('-')[1]);
How do I then create an array that contains all integers between lowEnd and highEnd, including lowEnd and highEnd themselves? For this specific example, obviously, the resulting array would be:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25]
var list = [];
for (var i = lowEnd; i <= highEnd; i++) {
list.push(i);
}
ES6 :
Use Array.from (docs here):
console.log(
Array.from({length:5},(v,k)=>k+1)
)
In JavaScript ES6:
function range(start, end) {
return Array(end - start + 1).fill().map((_, idx) => start + idx)
}
var result = range(9, 18); // [9, 10, 11, 12, 13, 14, 15, 16, 17, 18]
console.log(result);
For completeness, here it is with an optional step parameter.
function range(start, end, step = 1) {
const len = Math.floor((end - start) / step) + 1
return Array(len).fill().map((_, idx) => start + (idx * step))
}
var result = range(9, 18, 0.83);
console.log(result);
I would use range-inclusive from npm in an actual project. It even supports backwards steps, so that's cool.
I highly recommend underscore or lo-dash libraries:
http://underscorejs.org/#range
(Almost completely compatible, apparently lodash runs quicker but underscore has better doco IMHO)
_.range([start], stop, [step])
Both libraries have bunch of very useful utilities.
My version of the loop ;)
var lowEnd = 1;
var highEnd = 25;
var arr = [];
while(lowEnd <= highEnd){
arr.push(lowEnd++);
}
fastest way
while-- is faster on most browsers
direct setting a variable is faster than push
function:
var x=function(a,b,c,d){d=[];c=b-a+1;while(c--){d[c]=b--}return d},
theArray=x(lowEnd,highEnd);
or
var arr=[],c=highEnd-lowEnd+1;
while(c--){arr[c]=highEnd--}
EDIT
readable version
var arr = [],
c = highEnd - lowEnd + 1;
while ( c-- ) {
arr[c] = highEnd--
}
Demo
http://jsfiddle.net/W3CUn/
FOR THE DOWNVOTERS
performance
http://jsperf.com/for-push-while-set/2
faster in ie and 3x faster in firefox
only on aipad air the for loop is a little faster.
tested on win8, osx10.8, ubuntu14.04, ipad, ipad air, ipod;
with chrome,ff,ie,safari,mobile safari.
i would like to see the performance on older ie browsers where the for loop isn't that optimized!
function range(j, k) {
return Array
.apply(null, Array((k - j) + 1))
.map(function(_, n){ return n + j; });
}
this is roughly equivalent to
function range(j, k) {
var targetLength = (k - j) + 1;
var a = Array(targetLength);
var b = Array.apply(null, a);
var c = b.map(function(_, n){ return n + j; });
return c;
}
breaking it down:
var targetLength = (k - j) + 1;
var a = Array(targetLength);
this creates a sparse matrix of the correct nominal length. Now the problem with a sparse matrix is that although it has the correct nominal length, it has no actual elements, so, for
j = 7, k = 13
console.log(a);
gives us
Array [ <7 empty slots> ]
Then
var b = Array.apply(null, a);
passes the sparse matrix as an argument list to the Array constructor, which produces a dense matrix of (actual) length targetLength, where all elements have undefined value. The first argument is the 'this' value for the the array constructor function execution context, and plays no role here, and so is null.
So now,
console.log(b);
yields
Array [ undefined, undefined, undefined, undefined, undefined, undefined, undefined ]
finally
var c = b.map(function(_, n){ return n + j; });
makes use of the fact that the Array.map function passes: 1. the value of the current element and 2. the index of the current element, to the map delegate/callback. The first argument is discarded, while the second can then be used to set the correct sequence value, after adjusting for the start offset.
So then
console.log(c);
yields
Array [ 7, 8, 9, 10, 11, 12, 13 ]
My five cents:
Both direction array of integers function.
When range(0, 5) become [0, 1, 2, 3, 4, 5].
And range(5, 0) become [5, 4, 3, 2, 1, 0].
Based on this answer.
function range(start, end) {
const isReverse = (start > end);
const targetLength = isReverse ? (start - end) + 1 : (end - start ) + 1;
const arr = new Array(targetLength);
const b = Array.apply(null, arr);
const result = b.map((discard, n) => {
return (isReverse) ? n + end : n + start;
});
return (isReverse) ? result.reverse() : result;
}
P.S. For use in real life you should also check args for isFinite() and isNaN().
function createNumberArray(lowEnd, highEnd) {
var start = lowEnd;
var array = [start];
while (start < highEnd) {
array.push(start);
start++;
}
}
If the start is always less than the end, we can do:
function range(start, end) {
var myArray = [];
for (var i = start; i <= end; i += 1) {
myArray.push(i);
}
return myArray;
};
console.log(range(4, 12)); // → [4, 5, 6, 7, 8, 9, 10, 11, 12]
If we want to be able to take a third argument to be able to modify the step used to build the array, and to make it work even though the start is greater than the end:
function otherRange(start, end, step) {
otherArray = [];
if (step == undefined) {
step = 1;
};
if (step > 0) {
for (var i = start; i <= end; i += step) {
otherArray.push(i);
}
} else {
for (var i = start; i >= end; i += step) {
otherArray.push(i);
}
};
return otherArray;
};
console.log(otherRange(10, 0, -2)); // → [10, 8, 6, 4, 2, 0]
console.log(otherRange(10, 15)); // → [10, 11, 12, 13, 14, 15]
console.log(otherRange(10, 20, 2)); // → [10, 12, 14, 16, 18, 20]
This way the function accepts positive and negative steps and if no step is given, it defaults to 1.
Solution with pure ES6
Inspired by m59's answer above, but without the dependency on fill:
const range = (start, stop) => Array.from({ length: stop - start + 1 }, (_, i) => start + i)
So you can use it like:
range(3,5)
=> [3, 4, 5]
var values = $(this).val().split('-'),
i = +values[0],
l = +values[1],
range = [];
while (i < l) {
range[range.length] = i;
i += 1;
}
range[range.length] = l;
There's probably a DRYer way to do the loop, but that's the basic idea.
You can design a range method that increments a 'from' number by a desired amount until it reaches a 'to' number.
This example will 'count' up or down, depending on whether from is larger or smaller than to.
Array.range= function(from, to, step){
if(typeof from== 'number'){
var A= [from];
step= typeof step== 'number'? Math.abs(step):1;
if(from> to){
while((from -= step)>= to) A.push(from);
}
else{
while((from += step)<= to) A.push(from);
}
return A;
}
}
If you ever want to step by a decimal amount : Array.range(0,1,.01)
you will need to truncate the values of any floating point imprecision.
Otherwise you will return numbers like
0.060000000000000005 instead of .06.
This adds a little overhead to the other version, but works correctly for integer or decimal steps.
Array.range= function(from, to, step, prec){
if(typeof from== 'number'){
var A= [from];
step= typeof step== 'number'? Math.abs(step):1;
if(!prec){
prec= (from+step)%1? String((from+step)%1).length+1:0;
}
if(from> to){
while(+(from -= step).toFixed(prec)>= to) A.push(+from.toFixed(prec));
}
else{
while(+(from += step).toFixed(prec)<= to) A.push(+from.toFixed(prec));
}
return A;
}
}
Adding http://minifiedjs.com/ to the list of answers :)
Code is similar to underscore and others:
var l123 = _.range(1, 4); // same as _(1, 2, 3)
var l0123 = _.range(3); // same as _(0, 1, 2)
var neg123 = _.range(-3, 0); // same as _(-3, -2, -1)
var empty = _.range(2,1); // same as _()
Docs here:
http://minifiedjs.com/api/range.html
I use minified.js because it solves all my problems with low footprint and easy to understand syntax. For me, it is a replacement for jQuery, MustacheJS and Underscore/SugarJS in one framework.
Of course, it is not that popular as underscore. This might be a concern for some.
Minified was made available by Tim Jansen using the CC-0 (public domain) license.
const range = (start, stop, step) => Array.from({ length: (stop - start) / step + 1}, (_, i) => start + (i * step));
source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from
_Array = (length) => Object.keys(Array.from({length}))
//_Array = [0, 1, 2, 3, 4]
const range = (start: number, end: number) => {
for (var i = start, list = []; i <= end; list.push(i), i++);
return list;
};
Hope the below method will help someone.
Here count variable can be used to mention the array length.
const generateRandomArryOfNumbers = (min = 1, max = 100, count = 31) => {
return Array.from(new Array(count), () =>
Math.floor(Math.random() * (max - min + 1) + min)
);
};
Here's 3 functions that should cover everything I could think of (including fixes for problems in some other answers): rangeInt(), range(), and between(). Both ascending and descending orders are accounted for in all cases.
Examples
rangeInt()
Includes endpoints and only deals with integers
rangeInt(1, 4) // [1, 2, 3, 4] Ascending order
rangeInt(5, 2) // [5, 4, 3, 2] Descending order
rangeInt(4, 4) // [4] Singleton set (i.e. not [4, 4])
rangeInt(-1, 1) // [-1, 0, 1] Mixing positive and negative
range()
Same as rangeInt() except
Not limited to integers
Allows for a specified number of points in a third parameter
range(0, 10, 2) // [0, 3.333, 6.666, 10] Gets endpoints and 2 points between
range(0, 1.5, 1) // [0, 0.75, 1.5] Accepts fractions
between()
Same as range() except
Endpoints are excluded
There are no singleton sets (an empty array will be returned instead)
between(0, 10, 2) // [3.333, 6.666]
between(-1, -1.5) // [-1.25]
between(4, 4, 99) // []
Source
/**
* Gets a set of integers that are evenly distributed along a closed interval
* #param {int} begin - Beginning endpoint (inclusive)
* #param {int} end - Ending endpoint (inclusive)
* #return {Array} Range of integers
*/
function rangeInt( begin, end ) {
if ( !Number.isInteger(begin) || !Number.isInteger(end) ) {
throw new Error('All arguments must be integers')
}
return range(begin, end, Math.abs(end - begin) - 1)
}
/**
* Gets a set of numbers that are evenly distributed along a closed interval
* #param {Number} begin - Beginning endpoint (inclusive)
* #param {Number} end - Ending endpoint (inclusive)
* #param {int} points - How many numbers to retrieve from the open interval
* #return {Array} Range of numbers
*/
function range( begin, end, points ) {
if ( begin !== end ) {
return [ begin, ...between(begin, end, points), end ]
}
else if ( Number.isFinite(begin) ) {
return [ begin ] // singleton set
}
else throw new Error('Endpoints must be finite')
}
/**
* Gets a subset of numbers that are evenly distributed along an open interval
* #param {Number} begin - Beginning endpoint (exclusive)
* #param {Number} end - Ending endpoint (exclusive)
* #param {int} points - How many numbers to retrieve from the interval
* #return {Array} Retrieved numbers
*/
function between( begin, end, points = 1 ) {
if ( !Number.isFinite(begin) || !Number.isFinite(end) || !Number.isFinite(points) ) {
throw new Error('All arguments must be finite')
}
const set = []
// Skip if an open interval does not exist
if ( begin !== end ) {
const step = (end - begin) / (points + 1)
for ( let i = 0; i < points; i++ ) {
set[i] = begin + (i + 1) * step
}
}
return set
}
Solving in underscore
data = [];
_.times( highEnd, function( n ){ data.push( lowEnd ++ ) } );
function getRange(a,b)
{
ar = new Array();
var y = a - b > 0 ? a - b : b - a;
for (i=1;i<y;i++)
{
ar.push(i+b);
}
return ar;
}

Categories