58

I need to generate a set of unique (no duplicate) integers, and between 0 and a given number.

That is:

var limit = 10;
var amount = 3;

How can I use Javascript to generate 3 unique numbers between 1 and 10?

Rob W
  • 341,306
  • 83
  • 791
  • 678
benhowdle89
  • 36,900
  • 69
  • 202
  • 331
  • 1
    possible duplicate of [Generating random numbers in Javascript](http://stackoverflow.com/questions/1527803/generating-random-numbers-in-javascript) – pimvdb Dec 04 '11 at 21:54
  • 1
    Do you mean 3 integers in the range 1 ≤ x ≤ 10, or three floats in the range 1 ≤ x < 10? – Jeremy Dec 04 '11 at 21:55
  • 4
    The "duplicate question" is not the right one. This question contains an **additional** requirement: The random number should be **unique** – Rob W Dec 04 '11 at 22:09
  • Does this answer your question? [Generating random whole numbers in JavaScript in a specific range?](https://stackoverflow.com/questions/1527803/generating-random-whole-numbers-in-javascript-in-a-specific-range) – Michael Freidgeim Oct 02 '20 at 23:30

16 Answers16

78

Use the basic Math methods:

  • Math.random() returns a random number between 0 and 1 (including 0, excluding 1).
  • Multiply this number by the highest desired number (e.g. 10)
  • Round this number downward to its nearest integer

    Math.floor(Math.random()*10) + 1
    

Example:

//Example, including customisable intervals [lower_bound, upper_bound)
var limit = 10,
    amount = 3,
    lower_bound = 1,
    upper_bound = 10,
    unique_random_numbers = [];

if (amount > limit) limit = amount; //Infinite loop if you want more unique
                                    //Natural numbers than exist in a
                                    // given range
while (unique_random_numbers.length < limit) {
    var random_number = Math.floor(Math.random()*(upper_bound - lower_bound) + lower_bound);
    if (unique_random_numbers.indexOf(random_number) == -1) { 
        // Yay! new random number
        unique_random_numbers.push( random_number );
    }
}
// unique_random_numbers is an array containing 3 unique numbers in the given range
Rob W
  • 341,306
  • 83
  • 791
  • 678
  • @benhowdle89 At the loop, in variable `random_numbers`. I've first put the logic here, then the example. – Rob W Dec 04 '11 at 22:00
  • @benhowdle89 Someone tagged your question as Homework. Is this true? If not, remove the tag. – Rob W Dec 04 '11 at 22:10
  • 1
    No, it's not, also it's not a duplicate : ( – benhowdle89 Dec 04 '11 at 22:11
  • change "Math.random()*(upper_bound - lower_bound + 1)" remove the + 1, I tested on lower_bound = 0, upper_bound = 5, with the + 1 part I got 6 sometimes – Jacek Pietal Mar 14 '13 at 21:04
  • 18
    The answer is wrong. Don't use round() - use floor() or ceil() because otherwise, your results will not be evenly distributed. For example, if you want a random integer between 0 and 3, and you use round(), you will end up with 1's and 2's more often than 0's and 3's because there are more possible random values that will round to 1 and 2 because round() can round up or down. Using floor or ceil forces floats to always round in the same direction, creating more distributed results. – Eric Rowell Aug 15 '14 at 22:44
  • Do not use Math.round. Use Math.floor, otherwise you will find yourself with an upper limit equal to your 1+top. Math.floor(Math.random()*10) + 1 http://www.w3schools.com/jsref/jsref_random.asp – phillihp Oct 10 '14 at 21:55
  • This creates a number between 1 and 10 not 0 and 10 because you always add 1 at the end. Assuming Math.random returns 0 we end up with: Math.floor(0 * 10) which equals 0, same with Math.round. Then we have 0 + 1 which equals 1 as the lower boundary. – efarley Oct 05 '16 at 19:24
  • This answer occasionally results in 11, which is not a number between 0 and 10. – Benny Schmidt Aug 01 '17 at 17:32
21
Math.floor(Math.random() * (limit+1))

Math.random() generates a floating point number between 0 and 1, Math.floor() rounds it down to an integer.

By multiplying it by a number, you effectively make the range 0..number-1. If you wish to generate it in range from num1 to num2, do:

Math.floor(Math.random() * (num2-num1 + 1) + num1)

To generate more numbers, just use a for loop and put results into an array or write them into the document directly.

taras
  • 6,566
  • 10
  • 39
  • 50
Llamageddon
  • 3,306
  • 4
  • 25
  • 44
  • If I want a random number between 0 and 5 this did not generate any results = 0. I'm thinking because the + 1 makes the minimum result = 1 – paulzag Aug 16 '16 at 05:18
  • @paulzag You're right, thanks for pointing it out! An easy fix is to move the + 1 inside the Math.floor, but then it might very rarely generate a number above the upper bound as well... hmm – Llamageddon Sep 20 '16 at 17:29
  • I tried this with num1 = 0 and num2 = 10 and it generated both 0 and 10 if I ran it enough times. Great answer - thanks! – Daniel Nov 05 '16 at 07:00
  • @Daniel Yeah, but the problem is that it can go 1 over the limit, very rarely, and there's no way to fix that other than subtract the smallest floating point unit that can be subtracted from limit+1, and I have zero clue how to do that. – Llamageddon Nov 05 '16 at 07:09
5
function generateRange(pCount, pMin, pMax) {
    min = pMin < pMax ? pMin : pMax;
    max = pMax > pMin ? pMax : pMin;
    var resultArr = [], randNumber;
    while ( pCount > 0) {
        randNumber = Math.round(min + Math.random() * (max - min));
        if (resultArr.indexOf(randNumber) == -1) {
            resultArr.push(randNumber);
            pCount--;
        }
    }
    return resultArr;
}

Depending on range needed the method of returning the integer can be changed to: ceil (a,b], round [a,b], floor [a,b), for (a,b) is matter of adding 1 to min with floor.

Bakudan
  • 19,134
  • 9
  • 53
  • 73
  • 1
    Keep in mind, this is not evenly distributed. I just found this while trying to generate a (non-unique) random number in [0,100] and tried your `Math.round(min + Math.random() * (max - min))` solution, and in a test of a million generated numbers I got about half as many instances of `0` and `100` compared to any other result. – PrincessRTFM May 18 '19 at 18:23
3
Math.floor(Math.random()*limit)+1
Pål Brattberg
  • 4,568
  • 29
  • 40
2

Here’s another algorithm for ensuring the numbers are unique:

  1. generate an array of all the numbers from 0 to x
  2. shuffle the array so the elements are in random order
  3. pick the first n

Compared to the method of generating random numbers until you get a unique one, this method uses more memory, but it has a more stable running time – the results are guaranteed to be found in finite time. This method works better if the upper limit is relatively low or if the amount to take is relatively high.

My answer uses the Lodash library for simplicity, but you could also implement the algorithm described above without that library.

// assuming _ is the Lodash library

// generates `amount` numbers from 0 to `upperLimit` inclusive
function uniqueRandomInts(upperLimit, amount) {
    var possibleNumbers = _.range(upperLimit + 1);
    var shuffled = _.shuffle(possibleNumbers);
    return shuffled.slice(0, amount);
}
Rory O'Kane
  • 29,210
  • 11
  • 96
  • 131
2
for(i = 0;i <amount; i++)
{
    var randomnumber=Math.floor(Math.random()*limit)+1
    document.write(randomnumber)
}
litterbugkid
  • 3,534
  • 7
  • 36
  • 54
1
var randomNums = function(amount, limit) {
var result = [],
    memo = {};

while(result.length < amount) {
    var num = Math.floor((Math.random() * limit) + 1);
    if(!memo[num]) { memo[num] = num; result.push(num); };
}
return result; }

This seems to work, and its constant lookup for duplicates.

  • 1
    To avoid doing an infinite loop, you should change the condition in `while` cycle to something like `while(result.length < amount && result.length <= max - 1)` – ViRPo Aug 14 '20 at 12:52
1

These answers either don't give unique values, or are so long (one even adding an external library to do such a simple task).

1. generate a random number.
2. if we have this random already then goto 1, else keep it.
3. if we don't have desired quantity of randoms, then goto 1.

function uniqueRandoms(qty, min, max){
  var rnd, arr=[];
  do { do { rnd=Math.floor(Math.random()*max)+min }
      while(arr.includes(rnd))
      arr.push(rnd);
  } while(arr.length<qty)
  return arr;
}

//generate 5 unique numbers between 1 and 10
console.log( uniqueRandoms(5, 1, 10) );

...and a compressed version of the same function:

function uniqueRandoms(qty,min,max){var a=[];do{do{r=Math.floor(Math.random()*max)+min}while(a.includes(r));a.push(r)}while(a.length<qty);return a}

ashleedawg
  • 20,365
  • 9
  • 72
  • 105
1

Something like this

var limit = 10;
var amount = 3;
var nums = new Array();

for(int i = 0; i < amount; i++)
{
    var add = true;
    var n = Math.round(Math.random()*limit + 1;
    for(int j = 0; j < limit.length; j++)
    {
        if(nums[j] == n)
        {
            add = false;
        }
    }
    if(add)
    {
        nums.push(n)
    }
    else
    {
        i--;
    }
}
Ash Burlaczenko
  • 24,778
  • 15
  • 68
  • 99
0
/**
 * Generates an array with numbers between
 * min and max randomly positioned.
 */
function genArr(min, max, numOfSwaps){
  var size = (max-min) + 1;
  numOfSwaps = numOfSwaps || size;
  var arr = Array.apply(null, Array(size));

  for(var i = 0, j = min; i < size & j <= max; i++, j++) {
    arr[i] = j;
  }

  for(var i = 0; i < numOfSwaps; i++) {
    var idx1 = Math.round(Math.random() * (size - 1));
    var idx2 = Math.round(Math.random() * (size - 1));

    var temp = arr[idx1];
    arr[idx1] = arr[idx2];
    arr[idx2] = temp;
  }

  return arr;
}

/* generating the array and using it to get 3 uniques numbers */
var arr = genArr(1, 10);
for(var i = 0; i < 3; i++) {
  console.log(arr.pop());
}
Rayron Victor
  • 2,398
  • 1
  • 25
  • 25
0

I think, this is the most human approach (with using break from while loop), I explained it's mechanism in comments.

function generateRandomUniqueNumbersArray (limit) {

    //we need to store these numbers somewhere
    const array = new Array();
    //how many times we added a valid number (for if statement later)
    let counter = 0;

    //we will be generating random numbers until we are satisfied
    while (true) {

        //create that number
        const newRandomNumber = Math.floor(Math.random() * limit);

        //if we do not have this number in our array, we will add it
        if (!array.includes(newRandomNumber)) {
            array.push(newRandomNumber);
            counter++;
        }

        //if we have enought of numbers, we do not need to generate them anymore
        if (counter >= limit) {
            break;
        }
    }

    //now hand over this stuff
    return array;
}

You can of course add different limit (your amount) to the last 'if' statement, if you need less numbers, but be sure, that it is less or equal to the limit of numbers itself - otherwise it will be infinite loop.

Martin Melichar
  • 1,036
  • 2
  • 10
  • 14
0

Just as another possible solution based on ES6 Set ("arr. that can contain unique values only").

Examples of usage:

// Get 4 unique rnd. numbers: from 0 until 4 (inclusive):
getUniqueNumbersInRange(4, 0, 5) //-> [5, 0, 4, 1];

// Get 2 unique rnd. numbers: from -1 until 2 (inclusive):
getUniqueNumbersInRange(2, -1, 2) //-> [1, -1];

// Get 0 unique rnd. numbers (empty result): from -1 until 2 (inclusive):
getUniqueNumbersInRange(0, -1, 2) //-> [];

// Get 7 unique rnd. numbers: from 1 until 7 (inclusive):
getUniqueNumbersInRange(7, 1, 7) //-> [ 3, 1, 6, 2, 7, 5, 4];

The implementation:

function getUniqueNumbersInRange(uniqueNumbersCount, fromInclusive, untilInclusive) {

    // 0/3. Check inputs.
    if (0 > uniqueNumbersCount) throw new Error('The number of unique numbers cannot be negative.');
    if (fromInclusive > untilInclusive) throw new Error('"From" bound "' + fromInclusive
        + '" cannot be greater than "until" bound "' + untilInclusive + '".');
    const rangeLength = untilInclusive - fromInclusive + 1;
    if (uniqueNumbersCount > rangeLength) throw new Error('The length of the range is ' + rangeLength + '=['
        + fromInclusive + '…' + untilInclusive + '] that is smaller than '
        + uniqueNumbersCount + ' (specified count of result numbers).');
    if (uniqueNumbersCount === 0) return [];


    // 1/3. Create a new "Set" – object that stores unique values of any type, whether primitive values or object references.
    // MDN - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
    // Support: Google Chrome 38+(2014.10), Firefox 13+, IE 11+
    const uniqueDigits = new Set();


    // 2/3. Fill with random numbers.        
    while (uniqueNumbersCount > uniqueDigits.size) {
        // Generate and add an random integer in specified range.
        const nextRngNmb = Math.floor(Math.random() * rangeLength) + fromInclusive;
        uniqueDigits.add(nextRngNmb);
    }


    // 3/3. Convert "Set" with unique numbers into an array with "Array.from()".
    // MDN – https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from
    // Support: Google Chrome 45+ (2015.09+), Firefox 32+, not IE
    const resArray = Array.from(uniqueDigits);
    return resArray;

}

The benefits of the current implementation:

  1. Have a basic check of input arguments – you will not get an unexpected output when the range is too small, etc.
  2. Support the negative range (not only from 0), e. g. randoms from -1000 to 500, etc.
  3. Expected behavior: the current most popular answer will extend the range (upper bound) on its own if input bounds are too small. An example: get 10000 unique numbers with a specified range from 0 until 10 need to throw an error due to too small range (10-0+1=11 possible unique numbers only). But the current top answer will hiddenly extend the range until 10000.
Oleg Zarevennyi
  • 2,753
  • 1
  • 21
  • 21
0

I wrote this C# code a few years back, derived from a Wikipedia-documented algorithm, which I forget now (feel free to comment...). Uniqueness is guaranteed for the lifetime of the HashSet. Obviously, if you will be using a database, you could store the generated numbers there. Randomness was ok for my needs, but probably can be improved using a different RNG. Note: count must be <= max - min (duh!) and you can easily modify to generate ulongs.

private static readonly Random RndGen = new Random();

public static IEnumerable<int> UniqueRandomIntegers(int count, int min, int max)
{
    var rv = new HashSet<int>();
    for (var i = max - min - count + 1; i <= max - min; i++)
    {
       var r = (int)(RndGen.NextDouble() * i);
       var v = rv.Contains(r) ? i : r;
       rv.Add(v);
       yield return v;
    }
}
Costas
  • 171
  • 2
  • 4
0

Randomized Array, Sliced

Similar to @rory-okane's answer, but without lodash.

  • Both Time Complexity and Space Complexity = O(n) where n=limit

  • Has a consistent runtime

  • Supports a positive or negative range of numbers

  • Theoretically, this should support a range from 0 to ±2^32 - 1

    This limit is due to Javascript arrays only supporting 2^32 - 1 indexes as per the ECMAScript specification

    I stopped testing it at 10^8 because my browser got weird around here and strangely only negative numbers to -10^7 - I got an Uncaught RangeError: Invalid array length error (shrug)

  • Bonus feature: Generate a randomized array of n length 0 to limit if you pass only one argument

let uniqueRandomNumbers = (limit, amount = limit) => {
    let array = Array(Math.abs(limit));
    for (let i = 0; i < array.length; i++) array[i] = i * Math.sign(limit);
    let currentIndex = array.length;
    let randomIndex;
    while(currentIndex > 0) {
        randomIndex = Math.floor(Math.random() * currentIndex--);
        [array[currentIndex], array[randomIndex]] = [array[randomIndex], array[currentIndex]];
    }
    return array.slice(0, Math.abs(amount));
}

console.log(uniqueRandomNumbers(10, 3));
console.log(uniqueRandomNumbers(-10, 3));

//bonus feature:
console.log(uniqueRandomNumbers(10));

Credit:

I personally got here because I was trying to generate random arrays of n length. Other SO questions that helped me arrive at this answer for my own use case are below. Thank you everyone for your contributions, you made my life better today.

Jeremy Iglehart
  • 4,281
  • 5
  • 25
  • 38
-1
const getRandomNo = (min, max) => {
   min = Math.ceil(min);
   max = Math.floor(max);
   return Math.floor(Math.random() * (max - min + 1)) + min; 
}

This function returns a random integer between the specified values. The value is no lower than min (or the next integer greater than min if min isn't an integer) and is less than (but not equal to) max. Example

console.log(`Random no between 0 and 10 ${getRandomNo(0,10)}`)
akash kumar
  • 121
  • 1
  • 6
-2

Here's a simple, one-line solution:

var limit = 10;
var amount = 3;

randoSequence(1, limit).slice(0, amount);

It uses randojs.com to generate a randomly shuffled array of integers from 1 through 10 and then cuts off everything after the third integer. If you want to use this answer, toss this within the head tag of your HTML document:

<script src="https://randojs.com/1.0.0.js"></script>
Aaron Plocharczyk
  • 2,776
  • 2
  • 7
  • 15
  • Umm, I don't think it counts as a *"1-line solution"* if it requires a **2KB+**, 118-line (prettified) external JS file in order to work... Otherwise, by that logic, any possible task could have a *"1-line solution"* if we simply dump all our JS into a separate file. (Also, your answer is 4 lines plus the file, not 1.) – ashleedawg Apr 22 '20 at 20:08