146

How can I generate some unique random numbers between 1 and 100 using JavaScript?

Amin Soheyli
  • 605
  • 2
  • 7
  • 16
dotty
  • 40,405
  • 66
  • 150
  • 195
  • 24
    Not really a dupe as this is focusing on javascript. – dotty Mar 04 '10 at 14:41
  • 2
    @dotty well there's no essential difference between doing this in Javascript and doing it in any other language, but I won't vote to close. – Pointy Mar 04 '10 at 14:56
  • 1
    I won't vote to close either. This is specific enough. – Josh Stodola Mar 04 '10 at 14:59
  • This is different. Picking 8 numbers at random from 100 has an answer more efficient than shuffling, which is not the case when you pick all numbers in the range. The proposed dup specifically calls for picking all numbers in the range. – sje397 Sep 21 '10 at 12:10
  • 1
    http://stackoverflow.com/q/4353830/419436 – Jan Dec 26 '10 at 21:58
  • 1
    There is another cleaner way to do this https://stackoverflow.com/questions/51898200/best-approach-to-random-10-numbers-between-1-and-100-no-dupes-in-javascript/51898356?noredirect=1#comment90748507_51898356 – Huangism Aug 17 '18 at 15:45

32 Answers32

238

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);
adam0101
  • 29,096
  • 21
  • 96
  • 174
  • 3
    O can be picked ; use var randomnumber=Math.ceil(Math.random()*100) – Alsciende Mar 04 '10 at 14:49
  • Doesn't work. Firebugs output was "[84, 10, 68, 14, 55, 85, 44, 55]" – dotty Mar 04 '10 at 14:50
  • Another test case displayed " [70, 70, 54, 60, 82, 50, 23, 33]" – dotty Mar 04 '10 at 14:51
  • Added Alsciende's contribution too – adam0101 Mar 04 '10 at 14:56
  • Permutation solutions are better because they don't become exponentially worse when the number of elements to pick increase. – Alsciende Mar 04 '10 at 15:00
  • This seems a better, and shorter, solution. – dotty Mar 04 '10 at 15:02
  • 2
    @Alsciende wouldn't it depend on the ratio of numbers being picked to the numbers to pick from? For example, if picking 8 numbers from 1 to 1,000,000 I would think this would work better. If picking 800,000 numbers from 1 to 1,000,000 the Knuth Shuffle would be better suited. – adam0101 Mar 04 '10 at 15:23
  • Yes, that's totally right. Although the permutation solution is only in O(n), so I think that's a fair price for safe reusability. – Alsciende Mar 04 '10 at 16:08
  • You don't need to shuffle the entire array of 1,000,000 numbers. You only need to swap eight numbers, so that the first eight elements are shuffled. – Frerich Raabe Mar 04 '10 at 16:14
  • 15
    -1: this algorithm is the naive approach; it's very inefficient. – Frerich Raabe Mar 04 '10 at 16:15
  • 53
    Wow. Naive seems a bit strong. It may not be the best solution, but it's simple, short, easy to see what's going on, and runs within acceptable operating parameters for what needs to be accomplished. On to the next task. Perfection is great, but 'done' is better than 'perfect'. – adam0101 Mar 04 '10 at 17:03
  • 2
    @Frerich: Although I found some inefficiencies (the `for` loop could be replaced by `i=arr.length; while (i--)`), I'd be interested in your solution. – Marcel Korpel Mar 04 '10 at 17:04
  • 1
    The main issue with adam0101's answer is that you can't build a function out of it. It only works well if the number of picks is vastly inferior to the size of the set. – Alsciende Mar 04 '10 at 17:40
  • @Marcel Korpel: The efficiency problems with this solution are on a higher level; the old (like, over 70 years old!) Fisher-Yates algorithm is both shorter and faster. A few of the other answers here use it. – Frerich Raabe Mar 05 '10 at 11:01
  • This gives me one value in the array that says undefined every 4-5 runs – user391986 Feb 19 '15 at 23:17
  • @user391986, I'm not seeing that behavior. What browser are you using? Do you get that result if you click the "Run code snippet" button in my answer? – adam0101 Feb 20 '15 at 14:25
  • In Google chrome if I run it 5-10 times in a row I get some undefined values back sometimes – user391986 Feb 20 '15 at 19:28
  • I can't reproduce it using Chrome. Perhaps it's a browser extension you're using or your implementation. – adam0101 Feb 20 '15 at 20:45
  • This is a very slow algorithm, since you are running a for loop to check if a random integer already exists, instead have a tempObject with the key and the value same as the random number to check if that key already exists (as shown in my answer). – kaizer1v Apr 13 '15 at 19:19
  • 4
    There is a chance the function returns a 0 in the array. According to this link: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random, Math.random() `Returns a random number between 0 (inclusive) and 1 (exclusive)`. If `the Math.random()` accidentally returns 0, the `Math.ceil(0)` is also 0, though the chance is low. –  Apr 22 '15 at 09:58
  • @ElgsQianChen Yes, the use of `Math.ceil()` with `Math.random()` is _always_ wrong. Besides the chance of returning 0, it also gives an uneven distribution of values. `Math.floor()` is the correct function to use for something like this. – Michael Geary Nov 20 '17 at 20:36
  • I took the liberty of editing the answer to change the incorrect use of `Math.ceil()` so it uses `Math.floor()` instead. – Michael Geary Nov 20 '17 at 20:43
  • @Alsciende Just a note for your other projects, your suggestion of using `Math.ceil()` was incorrect. See my update to the answer for a correct way to use `Math.floor()` with `Math.random()`. – Michael Geary Nov 20 '17 at 20:49
54
  1. Populate an array with the numbers 1 through 100.
  2. Shuffle it.
  3. Take the first 8 elements of the resulting array.
Community
  • 1
  • 1
ЯegDwight
  • 24,821
  • 10
  • 45
  • 52
  • 9
    surely it's more efficient to tweak the code to only do the first 8 shuffles? (and then take the last 8 elements of the semi-shuffled array) – second Mar 05 '10 at 10:06
  • 1
    This is how I always do it, too. So if I wanted ten random lines from a file with a bunch of lines in it, I do `randlines file | head -10`. – tchrist Sep 09 '12 at 19:38
  • 1
    I think is is the proper answer, because it maintains the probability distrubtion, which the accepted answer does not – roberto tomás Mar 23 '17 at 22:51
  • 2
    What if N=10^12? Not very efficient. – shinzou May 28 '18 at 11:03
  • 1
    @shinzou what N? There is no N anywhere in the question. Stay on-topic. – ЯegDwight May 28 '18 at 22:27
  • Scaled down questions are trivial, in the real world you don't get nice and constant amounts. – shinzou May 28 '18 at 22:30
  • 2
    @shinzou in the real world you wouldn't sort 10^12 numbers using JavaScript. A tirival question calls for a trivial answer. I am not here to solve world hunger. I am well-equipped to do that, but that is not what the question is about. – ЯegDwight May 28 '18 at 22:35
  • @robertotomás which distribution is being maintained? the [currently accepted answer](https://stackoverflow.com/a/2380113/1358308) correctly uses rejection sampling to implement "sampling without replacement" with uniform probability of choosing each number – Sam Mason Nov 09 '20 at 11:56
29

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]);
Alister
  • 27,049
  • 9
  • 40
  • 35
  • Why is it O(n)? Can't it loop for an arbitrary amount of time? – Anthony Wieser Oct 13 '18 at 08:13
  • @AnthonyWieser You're right, worst case. I was implying average case since Set.add is o(1) – Alister Oct 13 '18 at 10:31
  • I think this could return 0 like my answer used to before it was changed to use `Math.floor(Math.random()*100) + 1` – adam0101 Nov 13 '18 at 15:48
  • 1
    Very cool to discover `Set` in JS! However, wouldn't this solution cause unnecessary generation of numbers until one fits the requirement of uniqueness, especially in the last iterations, if 8 was closer to 100? Thus I think I prefer the also-elegant answer with `sort` below. – Gilad Barner May 06 '20 at 21:45
  • As per my benchmark test, this solution is one of the most reasonable. It is a bit slower than array.indexOf solution when you need to get a small set of unique numbers, but it become faster when you need more unique numbers from the range. Check my benchmark code: https://github.com/Kostanos/randomSpeed – Kostanos Jul 10 '22 at 23:44
25

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));
Felix Lemke
  • 6,189
  • 3
  • 40
  • 67
  • This is my favorite answer of them all. Dunno why it got only 6 votes. Elegant and with good complexity (provided `sort` is implemented well, which I'm sure it is). – Gilad Barner May 06 '20 at 21:43
  • This is the method I'm using for a while, but there's a problem with it, because you'll often get "pseudo randomized arrays" which will already have grouped and sorted items at some parts. But there's a way for improve it. – NoxFly Jan 20 '21 at 11:04
  • My favorite answer! – 1antares1 May 24 '22 at 21:23
  • This is the slowest in terms of performance. If you are making it for the high demand server side code, or, for some web based game, it may have impact. see my benchmark – Kostanos Jul 10 '22 at 23:38
16

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?

Pratik Deoghare
  • 35,497
  • 30
  • 100
  • 146
  • 5
    This approach is correct but suboptimal: you could stop shuffling after eight swaps, since you need only eight random numbers. The code above swaps the entire array (in this scenario, 100 elements). – Frerich Raabe Mar 04 '10 at 15:56
  • The code could be seriously improved. Return values, side effects and function usage are all really blurry IMO. Maybe if you write a function that answers the original problem exactly, using your fisherYates function, it would be clearer. – Alsciende Mar 04 '10 at 18:50
  • 1
    Answer updated with improved code. Also, @Frerich Raabe: problem with stopping after eight swaps is mentioned. – Pratik Deoghare Mar 05 '10 at 05:51
  • Your Fisher-Yates algorithm is wrong. r should depend on i. See my anwser: http://stackoverflow.com/questions/2380019/generate-8-unique-random-numbers-between-1-and-100/2380349#2380349 – Alsciende Mar 05 '10 at 08:56
  • Ooops sorry my horrible mistake!! Your implementation is cool. Liked it. +1 . Please let me know if anything else is wrong with it.Thanks. – Pratik Deoghare Mar 05 '10 at 09:37
  • Yes, it's still wrong :) _i_ should start at _myArray.length_ and decrease. As it is now, your first step picks a random index between 0 and 0. Otherwise it's correct, but you should rename the stop_count parameter (e.g. nb_picks) and return myArray.slice(0, nb_picks) instead of manually creating _result_. Might as well use the standard library, right? – Alsciende Mar 05 '10 at 09:46
  • Yes right. Learning a lot from you. :) Please,take a look at it again! Thanks again. – Pratik Deoghare Mar 05 '10 at 10:18
  • Well... Now _i_ is initialized with _myArray.length_, right? So you can't read _myArray[i]_ (well you can but it's undefined). Either start at _length-1_ (and change the value of _r_ and stop at 0 instead of 1) or access the element as _myArray[i-1]_. Also, declare _i_ with _var_. – Alsciende Mar 05 '10 at 10:35
  • On second thought: if you start at _length-1_, you must stop at 1. But if you start at _length_, you must stop at 2. – Alsciende Mar 05 '10 at 10:43
  • @The Machine Charmer: Performing just eight swaps is fine. It's a standard optimization and the chances of ending up with '1,2,3,76,5,6,7,8' are extremely small. Unless you have a bug in your algorithm implementation. ;-) – Frerich Raabe Mar 05 '10 at 10:55
  • @Frerich Raabe: There are total 100! and there are 92! permutations with first 8 elements untouched. So there is 92!/100! chance that I end up with [1,2,3,..8] :) Not so good i guess :D – Pratik Deoghare Mar 05 '10 at 11:10
  • i=1 must be included in your loop, and r must be Math.floor(Math.random()*(i+1)) to have a random number in [0,i]. – Alsciende Mar 05 '10 at 12:24
  • @Alsciende. Glad that you noticed. The OP has asked between **1** and 100. But this answer's code includes **0** as well - `Math.floor(Math.random()*i);` – Om Shankar Jan 02 '13 at 11:26
10

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 ;)

Victor Quinn
  • 1,107
  • 9
  • 12
  • Upvote because i never saw the run code snippet before – surfmuggle Nov 06 '14 at 21:02
  • if I want to make a code (8 random alphanumeric string) for coupons, which has to be unique, how can I do it with Chance.js? note: the coupons will be made on demand, so the number of codes will be indefinite – Oscar Yuandinata Jan 12 '15 at 07:04
  • @OscarYuandinata that's easy, just do `var codes = chance.unique(chance.string, 8)` If you need the codes pulled from a particular character pool, you can specify that like this: `chance.unique(chance.string, 8, {pool: "abcd1234"})` where abcd1234 can be any characters you want in the pool. See http://chancejs.com/#string – Victor Quinn Jan 16 '15 at 00:43
  • @VictorQuinn, sorry i didn't make myself clear. I mean the coupon code will be 8 characters random alphanumeric string, not an array of 8 random alphanumeric string. hahaha.. – Oscar Yuandinata Jan 16 '15 at 04:04
  • Oh @OscarYuandinata that's far easier heh `chance.string({ length: 8 })` and if you only want certain characters to appear in that string, `chance.string({ pool: 'abcd1234', length: 8 })` which would return a random 8 character string from the characters abcd1234, so for example "2c2c44bc" or "331141cc" – Victor Quinn Jan 16 '15 at 20:15
  • best solution ever. so simple, worked great for me :) – arslan Dec 18 '16 at 12:49
  • it works great for a small amount. when I try to generate 1000 numbers, it crashed – arslan Dec 18 '16 at 13:02
8

To avoid any long and unreliable shuffles, I'd do the following...

  1. Generate an array that contains the number between 1 and 100, in order.
  2. Generate a random number between 1 and 100
  3. Look up the number at this index in the array and store in your results
  4. Remove the elemnt from the array, making it one shorter
  5. Repeat from step 2, but use 99 as the upper limit of the random number
  6. Repeat from step 2, but use 98 as the upper limit of the random number
  7. Repeat from step 2, but use 97 as the upper limit of the random number
  8. Repeat from step 2, but use 96 as the upper limit of the random number
  9. Repeat from step 2, but use 95 as the upper limit of the random number
  10. Repeat from step 2, but use 94 as the upper limit of the random number
  11. 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>

belugabob
  • 4,302
  • 22
  • 22
  • But then your eighth number is random from 1 to 92, not 1 to 100. If you had to pick 90 numbers, your last number would only be picked from 1 to 10, wouldn't it? – adam0101 Mar 04 '10 at 16:05
  • @adam0101 No, because he removes numbers as he picks them. So in step 5, there are only 99 numbers in his array. @belugabob You are not more efficient than Knuth Shuffle. In fact, the splice is probably more expensive than the shuffle (which is perfectly reliable) – Alsciende Mar 04 '10 at 16:11
  • @adam0101: He's removing the chosen element from the array (see step 4 above), thus avoiding that any elements gets chosen twice. He then uses a lower upper bound for the next random number, simply because the array is shorter. – Frerich Raabe Mar 04 '10 at 16:12
  • @Alsciende, Yes - thought that there would be a way of doing this more efficiently using a shuffle, but wasn't completely sure. To to avoid deleting the item from the array, simply copy the last entry from the array (providing it wasn't the one that you picked) into the position which you picked from. – belugabob Mar 04 '10 at 16:24
  • See my response for an implementation in js – Alsciende Mar 04 '10 at 17:28
  • Ah! recursion is so much more elegant :P But your solution is nice, avoiding the slice. You could make it nicer by decreasing values.length instead of using maxIndex. I prefer my slick Array#pick method, though. (and don't worry about competition, nobody voted me up :( ) – Alsciende Mar 05 '10 at 09:02
  • 1
    The reason for not decrementing values.length, is that there is no guarantee that decreasing the length of an array is not done by reallocating memory. Using maxIndex has the same effect, by simply ignoring the last entries in the array, as they become irrelevant. – belugabob Mar 05 '10 at 09:12
  • For this to work with lower bounds other than 1, shouldn't the initial value of maxIndex be either values.length or max - min? – adamfsk Sep 14 '19 at 20:30
4

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;
Gumbo
  • 643,351
  • 109
  • 780
  • 844
3

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.

kaizer1v
  • 898
  • 8
  • 20
  • what will happen if i want 0 to be included? the line `min = (min) ? min : 1,` will always returns 1. (so 0 will never be selected) – TBE Jan 21 '16 at 10:11
  • A very good point. :). Thank you, I have made the appropriate change. It will now return even if you pass in a 0. – kaizer1v Jan 23 '16 at 08:44
2

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.

Randal Schwartz
  • 39,428
  • 4
  • 43
  • 70
2
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

FFF
  • 741
  • 1
  • 8
  • 19
2

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

Community
  • 1
  • 1
Mulan
  • 129,518
  • 31
  • 228
  • 259
2

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);
Deteta
  • 41
  • 4
1

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
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Pulkit Chaudhri
  • 531
  • 5
  • 6
1

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));
AndersTornkvist
  • 2,610
  • 20
  • 38
1

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);
Alsciende
  • 26,583
  • 9
  • 51
  • 67
  • Nice recursive implementation - I've posted an alternative, in my answer, that doesn't use splice, as I feel that this is an avoidable performance hit (Not that the OP had any issues with performance) – belugabob Mar 05 '10 at 08:53
  • Your solution is clever, but I won't use it in my Array#pick method because I don't want _this_ to have its elements shuffled around when I return it. – Alsciende Mar 05 '10 at 09:17
  • Which array do you not want to be shuffled, the original 1-100 array or the results? The former shouldn't matter, as it's a working array, and the latter will, by the nature of the code, come out in a random order anyway. Not quite sure that I understand your reasons. – belugabob Mar 05 '10 at 14:12
  • The original one. I implemented a generic Array#pick method, which I find useful. This function doesn't know if _this_ is a working array or not. To be generic, it doesn't alter _this_ more than necessary. – Alsciende Mar 05 '10 at 14:28
  • But it still alters it, even if only a little, which is unaviodable when using this technique. – belugabob Mar 05 '10 at 17:16
  • Hum it doesn't look like you read my answer at all before commenting... My Array#pick method "removes n random elements from array _this_ and returns them". "removes" being the key word here. Just like Array#splice, Array#pick removes some elements, *but leaves the rest of the array untouched*. – Alsciende Mar 07 '10 at 00:28
  • I read your answer just fine - if Array#splice removes elements, then the array has been changed - or am I missing something? – belugabob Mar 07 '10 at 22:34
  • I said "it doesn't alter this more than necessary". The necessary part is the goal of the function : "removes n random elements from the array and returns them". The unnecessary part is "elements get shuffled around". – Alsciende Mar 08 '10 at 08:43
1

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);
Alex Mireles
  • 121
  • 1
  • 7
0

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]

MajidTaheri
  • 3,813
  • 6
  • 28
  • 46
0

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.

0

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.

falsetru
  • 357,413
  • 63
  • 732
  • 636
0

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;
  }
}
Jonas Elfström
  • 30,834
  • 6
  • 70
  • 106
0
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.

0

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);

enter image description here

jsFiddle

Nofi
  • 2,107
  • 1
  • 15
  • 23
0

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))
RIdotCOM
  • 61
  • 1
  • 3
0

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())]

Marcin Król
  • 1,555
  • 2
  • 16
  • 31
0
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
}
Oscar López
  • 381
  • 2
  • 8
0

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))
Steven Spungin
  • 27,002
  • 5
  • 88
  • 78
0

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)));
TheMaster
  • 45,448
  • 6
  • 62
  • 85
0

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)
gildniy
  • 3,528
  • 1
  • 33
  • 23
0

This is how I'd do it in ES6 and without using while. This will never return a 0 either.

function randomUniqueIntegers(total, quantity) {
  
  const numbers = Array(total)
    .fill(null)
    .map((_, i) => i+1);
  
  return numbers
    .map((value) => ({ value, sort: Math.random() }))
    .sort((a, b) => a.sort - b.sort)
    .map(({ value }) => value)
    .slice(0, quantity);
}

randomUniqueIntegers(100, 8) //[ 79, 28, 97, 17, 23, 70, 20, 12 ]

You may also want to do some error handling if there is going to be user input in this.

Diego Fortes
  • 8,830
  • 3
  • 32
  • 42
0

So many options, but mostly they are with the similar algorithms. I decided to recompile them, and make a benchmark test. You can see a source code. Feel free to make pull request with additional functions or corrections.

I would recommend to use the Set version:

/**
 * Get set of unique numbers from the renge between 0 and {max}
 * @param {number} max The top number of the renge 0..max (will not be included in the result)
 * @param {number} qty The amount of unique numbers from the renge Must be less or equal to {max}
 * @returns {Array<number>} List of unique random numbers from the renge (0 <= random number < max) 
 */
export default function uniqueIndexes(max, qty) {
  const retVal = new Set;
  while (retVal.size < qty) {
    retVal.add(Math.floor(Math.random() * max));
  }
  return Array.from(retVal);
}

This function gives a second result when you need to get small amount of unique random numbers independently of size of the array. And it is still has a good performance when you need a bigger set of unique numbers.

And I strongly do not recommend to use a shuffle algorithm, which several people suggested in this topic. The shuffle algorithm is the worse in terms of performance, it is 25x times slower than one I recommended, and may have some impact if you working with bigger arrays in a high demand servers, or making some web based games. Also its performance become worse exponentially when you have a bigger range of numbers.

In my test example, in the range from 0 to 1000, it runs in 300! times slower than the recommended method.

Here is the result of the benchmark.

  • Testing 10000 times (80 unique numbers out of 100)

Shuffle function took 205.500ms --- The worse

Hash function took 48.448ms --- The BEST

Set function took 77.849ms --- reasonable

IndexOf function took 105.924ms


  • Testing 10000 x (10 out of 100)

Shuffle function took 203.991ms --- The worse

Hash function took 11.478ms

Set function took 8.007ms --- reasonable

IndexOf function took 4.859ms --- The BEST


  • Testing 10000 x (10 out of 1000)

Shuffle function took 2772.697ms --- wow, no words (but expected)

Hash function took 29.295ms

Set function took 8.435ms --- reasonable

IndexOf function took 4.792ms --- The BEST

Kostanos
  • 9,615
  • 4
  • 51
  • 65
0
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
CPslashM
  • 81
  • 1
  • 1