Related
I have been trying to wrap my head around this codility question for 1H30,and how to solve with binary search. I found the answer but I cant understand the logic behind it. Can someone who gets it kindly walk me through this answer.
This is the question
Task description
You are given integers K, M and a non-empty zero-indexed array A
consisting of N integers. Every element of the array is not greater
than M.
You should divide this array into K blocks of consecutive elements.
The size of the block is any integer between 0 and N. Every element of
the array should belong to some block.
The sum of the block from X to Y equals A[X] + A[X + 1] + ... + A[Y].
The sum of empty block equals 0.
The large sum is the maximal sum of any block.
For example, you are given integers K = 3, M = 5 and array A such
that:
A[0] = 2 A[1] = 1 A[2] = 5 A[3] = 1 A[4] = 2 A[5] = 2
A[6] = 2
The array can be divided, for example, into the following blocks:
[2, 1, 5, 1, 2, 2, 2], [], [] with a large sum of 15; [2], [1, 5, 1,
2], [2, 2] with a large sum of 9; [2, 1, 5], [], [1, 2, 2, 2] with a
large sum of 8; [2, 1], [5, 1], [2, 2, 2] with a large sum of 6.
The goal is to minimize the large sum. In the above example, 6 is the
minimal large sum.
Write a function:
function solution(K, M, A);
that, given integers K, M and a non-empty zero-indexed array A
consisting of N integers, returns the minimal large sum.
For example, given K = 3, M = 5 and array A such that:
A[0] = 2 A[1] = 1 A[2] = 5 A[3] = 1 A[4] = 2 A[5] = 2
A[6] = 2
the function should return 6, as explained above.
Assume that:
N and K are integers within the range [1..100,000];
M is an integer within the range [0..10,000];
each element of array A is an integer within the range [0..M].
This is the answer I could get my hands on
function solution(K, M, A) {
var begin = A.reduce((a, v) => (a + v), 0)
begin = parseInt((begin+K-1)/K, 10);
var maxA = Math.max(A);
if (maxA > begin) begin = maxA;
var end = begin + M + 1;
var res = 0;
while(begin <= end) {
var mid = (begin + end) / 2;
var sum = 0;
var block = 1;
for (var ind in A) {
var a = A[ind];
sum += a;
if (sum > mid) {
++block;
if (block > K) break;
sum = a;
}
}
if (block > K) {
begin = mid + 1;
} else {
res = mid;
end = mid - 1;
}
}
return res;
}
I would like to give the more detailed explanation of the algorithm that I have implemented and then one correct implementation in C++.
Find the maximum element in the input array. We could also use M, but M does not necessarily occur. A smaller number could be the maximum, so it is slight optimisation.
Calculate the sum of the input array. This would be the maximum largest sum.
Apply binary search, where the start is the maximum element and the end is the sum. The minimum largest sum would be in this range.
For each trial, check whether we can squeeze the elements into fewer blocks than the block number requested. If it is fewer, it is okay because we can use empty blocks. If it is equal, that is also acceptable. However, it is greater, then we can conclude that the tried minimum largest sum needs to be higher to allow individual blocks to be larger to reduce the block count.
One general principle can be observed above that the more fairly we distribute the sums of the blocks, the largest will become the minimum possible. For this, we need to squeeze as many elements into an individual block as possible.
If the number of blocks for a tried minimum largest sum is smaller than the expected block count, then we can try a slightly smaller minimum largest sum, otherwise we have to try a bit greater until we eventually find the best number.
As far as runtime complexity goes, the solution is O(n * log(N * M)) because the binary search is logarithmic. The sum can be N number of times the maximum element M at worst, which results in an N * M range to bisect with binary search. The inner iteration will go through all the elements, so that is N times. Therefore, it is O(N * log(N * M)) which is equivalent to O(N * log(N + M).
int check(vector<int>& A, int largest_sum)
{
int sum = 0;
int count = 0;
for (size_t i = 0; i < A.size(); ++i) {
const int e = A[i];
if ((sum + e) > largest_sum) { sum = 0; ++count; }
sum += e;
}
return count;
}
int solution(int K, int /*M*/, vector<int> &A)
{
int start = *max_element(A.begin(), A.end());
int end = accumulate(A.begin(), A.end(), 0);
while (start <= end) {
int mid = (start + end) / 2;
if (check(A, mid) < K) end = mid - 1;
else start = mid + 1;
}
return start;
}
This is a binary search on the solution. For each candidate solution, we iterate over the whole array once, filling array blocks to the maximum sum the block can be before exceeding the candidate. If the sum is not achievable, there is no point in trying a smaller sum so we search the space of higher possible candidates. And if the sum is achievable, we try the space of lower candidates, while we can.
I have changed a little bit the code so it's more clear, but here is my explanation:
/*
K = numberOfBlocks
M = maxNumber
A = array
*/
function solution(numberOfBlocks, maxNumber, array) {
let begin = array.reduce((a, b) => (a + b), 0); // Calculate total sum of A
begin = Math.ceil(begin / numberOfBlocks); // Calculate the mean of each theorethical block
begin = Math.max(begin, Math.max(...array)); // Set begin to the highest number in array if > than the mean
// In short: begin is now the smallest possible block sum
// Calculate largest possible block sum
let end = begin + maxNumber + 1;
var result = 0;
while (begin <= end) {
// Calculate the midpoint, which is our result guess
const midpoint = (begin + end) / 2;
let currentBlockSum = 0;
let block = 1;
for (let number of array) {
currentBlockSum += number;
// If currentBlockSum > midpoint means that we are
// in a different block...
if (currentBlockSum > midpoint) {
++block;
// ...so we reset sum with the current number
currentBlockSum = number;
// but if we are out of blocks, our guess (midpoint) is incorrect
// and we will have to adjust it
if (block > numberOfBlocks)
break;
}
}
// If we are out of blocks
// it means that our guess (midpoint) is bigger than we thought
if (block > numberOfBlocks) {
begin = midpoint + 1;
// else, it's smaller
} else {
result = midpoint;
end = midpoint - 1;
}
}
return result;
}
Looking and testing at the solutions, none of them actually work.
I decided to spend some time on it, here is my solution (working for any use case with maximum performance).
using System;
using System.Linq;
class Solution
{
public int solution(int K, int M, int[] A)
{
int start = Math.Max((int)Math.Ceiling((decimal)A.Sum()/(decimal)K), A.Max());
int end = A.Sum();
int result = 0;
while(start <= end)
{
int dicotomie = (end + start) / 2;
if(calculateNbBlocks(dicotomie, A) <= K)
{
result = dicotomie;
end = dicotomie - 1;
}
else
start = dicotomie + 1;
}
return result;
}
public int calculateNbBlocks(int dicotomie, int[] A)
{
int nbBlocks = 1;
int sum = 0;
for(int i=0; i<A.Length; i++)
{
sum += A[i];
if(sum > dicotomie)
{
sum = A[i];
nbBlocks++;
}
}
return nbBlocks;
}
}
Scored 100% on Codility (https://app.codility.com/demo/results/trainingQYJ68K-KJR/) using const midpoint = Math.floor((begin + end) / 2);
instead of
const midpoint = (begin + end) / 2;
after copying Javascript code in answer by pytness (Nov 5, 2020 at 8:40).
/*
K = numberOfBlocks
M = maxNumber
A = array
*/
function solution(numberOfBlocks, maxNumber, array) {
let begin = array.reduce((a, b) => (a + b), 0); // Calculate total sum of A
// console.log("total sum of A: ", begin);
begin = Math.ceil(begin / numberOfBlocks); // Calculate the mean of each theoretical block
// console.log('Math.ceil(begin / numberOfBlocks): ', begin);
begin = Math.max(begin, Math.max(...array)); // Set begin to the highest number in array if > than the mean
// console.log('Math.max(begin, Math.max(...array)): ', begin);
// In short: begin is now the smallest possible block sum
// Calculate largest possible block sum
let end = begin + maxNumber + 1;
// console.log("end: ", end);
var result = 0;
while (begin <= end) {
// Calculate the midpoint, which is our result guess
const midpoint = Math.floor((begin + end) / 2);
// console.log("midpoint: ", midpoint);
let currentBlockSum = 0;
let block = 1;
for (let number of array) {
currentBlockSum += number;
// console.log("currentBlockSum: ", currentBlockSum);
// If currentBlockSum > midpoint means that we are
// in a different block...
if (currentBlockSum > midpoint) {
// console.log("currentBlockSum > midpoint");
++block;
// console.log("block: ", block);
// ...so we reset sum with the current number
currentBlockSum = number;
// console.log("currentBlockSum: ", currentBlockSum);
// but if we are out of blocks, our guess (midpoint) is incorrect
// and we will have to adjust it
if (block > numberOfBlocks) {
// console.log("block > numberOfBlocks before break");
// console.log("block: ", block);
// console.log("break");
break;
}
}
}
// If we are out of blocks, it means that our guess for midpoint is too small.
if (block > numberOfBlocks) {
// console.log("block > numberOfBlocks before begin");
begin = midpoint + 1;
// console.log("begin: ", begin);
}
// Else, it's too big.
else {
// console.log("block <= numberOfBlocks");
result = midpoint;
// console.log("result: ", result);
end = midpoint - 1;
// console.log("end: ", end);
}
}
// console.log("result: ", result);
return result;
}
I'm having a trouble to get an array with numbers from 1 to 16 randomly without repetition.
I made num array to put numbers from createNum function.
The createNum function has a for loop which gets numbers from 1 to 16 until if statement push 16 numbers into num array.
At the end, I run createNum() and display numbers on the web.
While I'm making this code it sometimes worked but now it's not working.
can somebody point out where I made mistakes?
let num = [];
function createNum () {
for (i = 0; i <= 15;) {
let numGen = Math.floor(Math.random() * 15) + 1;
if (!num.includes(numGen)) {
num.push(numGen);
i++;
};
};
}
console.log(createNum());
document.getElementById("selectedNumbersShownHere").innerHTML = num;
console.log(num);
This is because Math.random() never returns 1, so at the end Math.floor(Math.random() * 15) will returns maximum 14 and adding it to 1 will get you maximum 15.
Use Math.ceil instead of Math.floor i.e
let num = [];
function createNum () {
for (i = 0; i <=15;) {
let numGen = Math.ceil(Math.random() * 16);
console.log(numGen)
if (!num.includes(numGen)) {
num.push(numGen);
i++;
};
};
}
console.log(createNum());
document.getElementById("selectedNumbersShownHere").innerHTML = num;
console.log(num);
Hope it helps!
for (i = 0; i <= 15;) generates 16 numbers, but Math.floor(Math.random() * 15) + 1 only have 15 possible values (1~15).
A shuffle function is recommended. Your function would be slow if you generate a large size shuffled array.
How can I shuffle an array?
Your loop seems never finish because the probability to get the last value is very low, and never can happen in a short time.
Plus :
your formula is wrong : let numGen = Math.floor(Math.random() * 15) + 1;
and should be................: let numGen = Math.floor(Math.random() * 16) + 1; value 16
see => Generating random whole numbers in JavaScript in a specific range?
do this:
function createNum()
{
let num =[], lastOne =136; // 136 = 1 +2 +3 + ... +16
for (;;)
{
let numGen = Math.floor(Math.random() *16) +1;
if (!num.includes(numGen))
{
lastOne -= numGen;
if (num.push(numGen)>14) break;
}
}
num.push(lastOne); // add the missing one (optimizing)
return num;
}
let unOrdered_16_vals = createNum();
/*
document.getElementById("selectedNumbersShownHere").textContent = unOrdered_16_vals.join('');
*/
console.log( JSON.stringify( unOrdered_16_vals ), 'length=', unOrdered_16_vals.length );
console.log( 'in order = ', JSON.stringify( unOrdered_16_vals.sort((a,b)=>a-b) ) );
remark : The push() method adds one or more elements to the end of an array and returns the new length of the array.
The problem in your code is that your looking for 16 distinct numbers out of 15 possible values.
The reason for this is that Math.floor(Math.random() * 15) + 1; will only return values between 1 and 15, but your loop will run until you have 16 unique values, so you enter into an infinite loop.
What you're basically trying to achieve is randomly shuffle an array with values from 1 to 16.
One common solution with good performance (O(n)) is the so-called Fisher-Yates shuffle. Here is code that addresses your requirements based on an implementation by Mike Bostock:
function shuffle(array) {
let m = array.length, t, i;
while (m) {
i = Math.floor(Math.random() * m--);
t = array[m];
array[m] = array[i];
array[i] = t;
}
return array;
}
// create array with values from 1 to 16
const array = Array(16).fill().map((_, i) => i + 1);
// shuffle
const shuffled = shuffle(array);
console.log(shuffled);
Compared to your approach and the approach of other answers to this question, the code above will only make 15 calls to the random number generator, while the others will make anywhere between 16 and an infinite number of calls(1).
(1) In probability theory, this is called the coupon collector's problem. For a value n of 16, on average 54 calls would have to be made to collect all 16 values.
Try like this :
let num = [];
function createNum () {
for (i = 0; num.length <= 17; i++) {
let numGen = Math.floor(Math.random() * 16) + 1;
if (!num.includes(numGen)) {
num.push(numGen);
};
};
}
console.log(createNum());
document.getElementById("selectedNumbersShownHere").innerHTML = num;
console.log(num);
Please find the working demo here
This is an infinite loop error. Because your loop variable "i" is always less than or equal to 15. and your i++ is inside the if statement. You can achieve it in multiple ways. Below is one sample.
let num = [];
function createNum () {
for (i = 0; i <= 15;) {
let numGen = Math.floor(Math.random() * 15) + 1;
if (!num.includes(numGen)) {
num.push(numGen);
};
i++;
};
}
console.log(createNum());
document.getElementById("selectedNumbersShownHere").innerHTML = num;
console.log(num);
sorry, I'm "passionate" about this question, and I can't resist presenting a second answer, which is IMHO: the best!
function createNum ()
{
let num = []
for (let len=0;len<16;)
{
let numGen = Math.ceil(Math.random() * 16)
if (!num.includes(numGen))
{ len = num.push(numGen) }
}
return num
}
let unOrdered = createNum();
console.log( JSON.stringify( unOrdered ) );
/*
document.getElementById("selectedNumbersShownHere").textContent = unOrdered_16_vals.join('');
*/
I would like to generate x random number between a and b. Each of this x random number should not be closer that y to the others ones:
If y = 100 then I shouldn't have 500 and 555 generated but 500 and 601 would be okay.
More context:
I would like to generate x circles with d3.js, that don't touch each other (so y would be the radius of the the bigger circle). I could use something like this but I would prefer something without using force().
The easiest thing to do would be to loop on Math.random() until you get an answer that isn't in the no-fly zone. The downside is some unnecessary calls to Math.random, but if the no-fly space is small compared to the range it wouldn't be significant.
UPDATE: avoid history of results. fun.
let history = [];
function myRand(range, margin) {
while(true) {
let result = Math.trunc(Math.random() * range);
if (history.every(last => {
return Math.abs(result - last) > margin;
})) {
history.push(result);
return result;
}
}
}
for (let i = 0; i < 5; i++)
console.log(myRand(100, 10));
I would make a simple b-tree and keep track of the nodes as I go along. This should be very fast and dependable.
let MAX = 100
let MIN = 0
let BUFFER = 10
let MAXCOUNT = 6
function randomBetween(min, max) {
return {
val: Math.floor(Math.random() * (max - min + 1) + min)
}
}
function addLeaves(f, min = MIN - BUFFER, max = MAX + BUFFER, arr = []) {
if (arr.length >= MAXCOUNT) return arr
arr.push(f.val)
f.left = (min + BUFFER < f.val - BUFFER) && addLeaves(randomBetween(min + BUFFER, f.val - BUFFER), min, f.val, arr)
f.right = (f.val + BUFFER < max - BUFFER) && addLeaves(randomBetween(f.val + BUFFER, max - BUFFER), f.val, max, arr)
return arr
}
let res = addLeaves(randomBetween(MIN, MAX))
console.log(res)
This will give at most MAXCOUNT values separated by BUFFER. It's possible it will return fewer than MAXCOUNT if there is no room in the range given a large buffer. Because of the nature of the b-tree, it will fill out gaps as necessary.
EDIT:
Since we don't actually use the tree structure(it might be useful in other circumstances thought), this can be rewritten to use the raw numbers. This changes it to a single function call to make reuse easier:
function getRandom(min, max, buffer, maxcount) {
let randomBetween = (min, max) => Math.floor(Math.random() * (max - min + 1) + min)
function addLeaves(f, min, max, arr = []) {
if (arr.length < maxcount) {
arr.push(f);
if(min + buffer < f - buffer) addLeaves(randomBetween(min + buffer, f - buffer), min, f, arr);
if(f + buffer < max - buffer) addLeaves(randomBetween(f + buffer, max - buffer), f, max, arr);
}
return arr
}
return addLeaves(randomBetween(min, max), min - buffer, max + buffer)
}
// now you can call with just: min, max, buffer, maxcount
let res = getRandom(0, 100, 10, 6)
console.log(res)
Here's another approach, though it feels a little less random to me. It doesn't run the risk of infinite loops in the case of a narrow space.
function randomWithMargin(start, stop, count, margin) {
let spacing = Math.trunc((stop - start) / count);
let left = start;
let right = start + spacing;
let results = [];
while (count--) {
let r = left + Math.trunc(Math.random() * (right - left));
results.push(r);
left = r + margin; // ensure next number is at least margin from last
right += spacing;
}
return results;
}
console.log(randomWithMargin(10, 110, 10, 7));
How can I generate random whole numbers between two specified variables in JavaScript, e.g. x = 4 and y = 8 would output any of 4, 5, 6, 7, 8?
There are some examples on the Mozilla Developer Network page:
/**
* Returns a random number between min (inclusive) and max (exclusive)
*/
function getRandomArbitrary(min, max) {
return Math.random() * (max - min) + min;
}
/**
* Returns a random integer between min (inclusive) and max (inclusive).
* The value is no lower than min (or the next integer greater than min
* if min isn't an integer) and no greater than max (or the next integer
* lower than max if max isn't an integer).
* Using Math.round() will give you a non-uniform distribution!
*/
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
Here's the logic behind it. It's a simple rule of three:
Math.random() returns a Number between 0 (inclusive) and 1 (exclusive). So we have an interval like this:
[0 .................................... 1)
Now, we'd like a number between min (inclusive) and max (exclusive):
[0 .................................... 1)
[min .................................. max)
We can use the Math.random to get the correspondent in the [min, max) interval. But, first we should factor a little bit the problem by subtracting min from the second interval:
[0 .................................... 1)
[min - min ............................ max - min)
This gives:
[0 .................................... 1)
[0 .................................... max - min)
We may now apply Math.random and then calculate the correspondent. Let's choose a random number:
Math.random()
|
[0 .................................... 1)
[0 .................................... max - min)
|
x (what we need)
So, in order to find x, we would do:
x = Math.random() * (max - min);
Don't forget to add min back, so that we get a number in the [min, max) interval:
x = Math.random() * (max - min) + min;
That was the first function from MDN. The second one, returns an integer between min and max, both inclusive.
Now for getting integers, you could use round, ceil or floor.
You could use Math.round(Math.random() * (max - min)) + min, this however gives a non-even distribution. Both, min and max only have approximately half the chance to roll:
min...min+0.5...min+1...min+1.5 ... max-0.5....max
└───┬───┘└────────┬───────┘└───── ... ─────┘└───┬──┘ ← Math.round()
min min+1 max
With max excluded from the interval, it has an even less chance to roll than min.
With Math.floor(Math.random() * (max - min +1)) + min you have a perfectly even distribution.
min... min+1... ... max-1... max.... (max+1 is excluded from interval)
└───┬───┘└───┬───┘└─── ... ┘└───┬───┘└───┬───┘ ← Math.floor()
min min+1 max-1 max
You can't use ceil() and -1 in that equation because max now had a slightly less chance to roll, but you can roll the (unwanted) min-1 result too.
var randomnumber = Math.floor(Math.random() * (maximum - minimum + 1)) + minimum;
Math.random()
Returns an integer random number between min (included) and max (included):
function randomInteger(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
Or any random number between min (included) and max (not included):
function randomNumber(min, max) {
return Math.random() * (max - min) + min;
}
Useful examples (integers):
// 0 -> 10
Math.floor(Math.random() * 11);
// 1 -> 10
Math.floor(Math.random() * 10) + 1;
// 5 -> 20
Math.floor(Math.random() * 16) + 5;
// -10 -> (-2)
Math.floor(Math.random() * 9) - 10;
** And always nice to be reminded (Mozilla):
Math.random() does not provide cryptographically secure random
numbers. Do not use them for anything related to security. Use the Web
Crypto API instead, and more precisely the
window.crypto.getRandomValues() method.
Use:
function getRandomizer(bottom, top) {
return function() {
return Math.floor( Math.random() * ( 1 + top - bottom ) ) + bottom;
}
}
Usage:
var rollDie = getRandomizer( 1, 6 );
var results = ""
for ( var i = 0; i<1000; i++ ) {
results += rollDie() + " "; // Make a string filled with 1000 random numbers in the range 1-6.
}
Breakdown:
We are returning a function (borrowing from functional programming) that when called, will return a random integer between the the values bottom and top, inclusive. We say 'inclusive' because we want to include both bottom and top in the range of numbers that can be returned. This way, getRandomizer( 1, 6 ) will return either 1, 2, 3, 4, 5, or 6.
('bottom' is the lower number, and 'top' is the greater number)
Math.random() * ( 1 + top - bottom )
Math.random() returns a random double between 0 and 1, and if we multiply it by one plus the difference between top and bottom, we'll get a double somewhere between 0 and 1+b-a.
Math.floor( Math.random() * ( 1 + top - bottom ) )
Math.floor rounds the number down to the nearest integer. So we now have all the integers between 0 and top-bottom. The 1 looks confusing, but it needs to be there because we are always rounding down, so the top number will never actually be reached without it. The random decimal we generate needs to be in the range 0 to (1+top-bottom) so we can round down and get an integer in the range 0 to top-bottom:
Math.floor( Math.random() * ( 1 + top - bottom ) ) + bottom
The code in the previous example gave us an integer in the range 0 and top-bottom, so all we need to do now is add bottom to that result to get an integer in the range bottom and top inclusive. :D
NOTE: If you pass in a non-integer value or the greater number first you'll get undesirable behavior, but unless anyone requests it I am not going to delve into the argument checking code as it’s rather far from the intent of the original question.
All these solutions are using way too much firepower. You only need to call one function: Math.random();
Math.random() * max | 0;
This returns a random integer between 0 (inclusive) and max (non-inclusive).
Return a random number between 1 and 10:
Math.floor((Math.random()*10) + 1);
Return a random number between 1 and 100:
Math.floor((Math.random()*100) + 1)
function randomRange(min, max) {
return ~~(Math.random() * (max - min + 1)) + min
}
Alternative if you are using Underscore.js you can use
_.random(min, max)
If you need a variable between 0 and max, you can use:
Math.floor(Math.random() * max);
The other answers don't account for the perfectly reasonable parameters of 0 and 1. Instead you should use the round instead of ceil or floor:
function randomNumber(minimum, maximum){
return Math.round( Math.random() * (maximum - minimum) + minimum);
}
console.log(randomNumber(0,1)); # 0 1 1 0 1 0
console.log(randomNumber(5,6)); # 5 6 6 5 5 6
console.log(randomNumber(3,-1)); # 1 3 1 -1 -1 -1
Cryptographically strong
To get a cryptographically strong random integer number in the range [x,y], try:
let cs = (x,y) => x + (y - x + 1)*crypto.getRandomValues(new Uint32Array(1))[0]/2**32 | 0
console.log(cs(4, 8))
Here's what I use to generate random numbers.
function random(min,max) {
return Math.floor((Math.random())*(max-min+1))+min;
}
Math.random() returns a number between 0 (inclusive) and 1 (exclusive). We multiply this number by the range (max-min). This results in a number between 0 (inclusive), and the range.
For example, take random(2,5). We multiply the random number 0≤x<1 by the range (5-2=3), so we now have a number, x where 0≤x<3.
In order to force the function to treat both the max and min as inclusive, we add 1 to our range calculation: Math.random()*(max-min+1). Now, we multiply the random number by the (5-2+1=4), resulting in an number, x, such that 0≤x<4. If we floor this calculation, we get an integer: 0≤x≤3, with an equal likelihood of each result (1/4).
Finally, we need to convert this into an integer between the requested values. Since we already have an integer between 0 and the (max-min), we can simply map the value into the correct range by adding the minimum value. In our example, we add 2 our integer between 0 and 3, resulting in an integer between 2 and 5.
Use this function to get random numbers in a given range:
function rnd(min, max) {
return Math.floor(Math.random()*(max - min + 1) + min);
}
Here is the Microsoft .NET Implementation of the Random class in JavaScript—
var Random = (function () {
function Random(Seed) {
if (!Seed) {
Seed = this.milliseconds();
}
this.SeedArray = [];
for (var i = 0; i < 56; i++)
this.SeedArray.push(0);
var num = (Seed == -2147483648) ? 2147483647 : Math.abs(Seed);
var num2 = 161803398 - num;
this.SeedArray[55] = num2;
var num3 = 1;
for (var i_1 = 1; i_1 < 55; i_1++) {
var num4 = 21 * i_1 % 55;
this.SeedArray[num4] = num3;
num3 = num2 - num3;
if (num3 < 0) {
num3 += 2147483647;
}
num2 = this.SeedArray[num4];
}
for (var j = 1; j < 5; j++) {
for (var k = 1; k < 56; k++) {
this.SeedArray[k] -= this.SeedArray[1 + (k + 30) % 55];
if (this.SeedArray[k] < 0) {
this.SeedArray[k] += 2147483647;
}
}
}
this.inext = 0;
this.inextp = 21;
Seed = 1;
}
Random.prototype.milliseconds = function () {
var str = new Date().valueOf().toString();
return parseInt(str.substr(str.length - 6));
};
Random.prototype.InternalSample = function () {
var num = this.inext;
var num2 = this.inextp;
if (++num >= 56) {
num = 1;
}
if (++num2 >= 56) {
num2 = 1;
}
var num3 = this.SeedArray[num] - this.SeedArray[num2];
if (num3 == 2147483647) {
num3--;
}
if (num3 < 0) {
num3 += 2147483647;
}
this.SeedArray[num] = num3;
this.inext = num;
this.inextp = num2;
return num3;
};
Random.prototype.Sample = function () {
return this.InternalSample() * 4.6566128752457969E-10;
};
Random.prototype.GetSampleForLargeRange = function () {
var num = this.InternalSample();
var flag = this.InternalSample() % 2 == 0;
if (flag) {
num = -num;
}
var num2 = num;
num2 += 2147483646.0;
return num2 / 4294967293.0;
};
Random.prototype.Next = function (minValue, maxValue) {
if (!minValue && !maxValue)
return this.InternalSample();
var num = maxValue - minValue;
if (num <= 2147483647) {
return parseInt((this.Sample() * num + minValue).toFixed(0));
}
return this.GetSampleForLargeRange() * num + minValue;
};
Random.prototype.NextDouble = function () {
return this.Sample();
};
Random.prototype.NextBytes = function (buffer) {
for (var i = 0; i < buffer.length; i++) {
buffer[i] = this.InternalSample() % 256;
}
};
return Random;
}());
Use:
var r = new Random();
var nextInt = r.Next(1, 100); // Returns an integer between range
var nextDbl = r.NextDouble(); // Returns a random decimal
I wanted to explain using an example:
Function to generate random whole numbers in JavaScript within a range of 5 to 25
General Overview:
(i) First convert it to the range - starting from 0.
(ii) Then convert it to your desired range ( which then will be very
easy to complete).
So basically, if you want to generate random whole numbers from 5 to 25 then:
First step: Converting it to range - starting from 0
Subtract "lower/minimum number" from both "max" and "min". i.e
(5-5) - (25-5)
So the range will be:
0-20 ...right?
Step two
Now if you want both numbers inclusive in range - i.e "both 0 and 20", the equation will be:
Mathematical equation: Math.floor((Math.random() * 21))
General equation: Math.floor((Math.random() * (max-min +1)))
Now if we add subtracted/minimum number (i.e., 5) to the range - then automatically we can get range from 0 to 20 => 5 to 25
Step three
Now add the difference you subtracted in equation (i.e., 5) and add "Math.floor" to the whole equation:
Mathematical equation: Math.floor((Math.random() * 21) + 5)
General equation: Math.floor((Math.random() * (max-min +1)) + min)
So finally the function will be:
function randomRange(min, max) {
return Math.floor((Math.random() * (max - min + 1)) + min);
}
After generating a random number using a computer program, it is still considered as a random number if the picked number is a part or the full one of the initial one. But if it was changed, then mathematicians do not accept it as a random number and they can call it a biased number.
But if you are developing a program for a simple task, this will not be a case to consider. But if you are developing a program to generate a random number for a valuable stuff such as lottery program, or gambling game, then your program will be rejected by the management if you are not consider about the above case.
So for those kind of people, here is my suggestion:
Generate a random number using Math.random() (say this n):
Now for [0,10) ==> n*10 (i.e. one digit) and for[10,100) ==> n*100 (i.e., two digits) and so on. Here square bracket indicates that the boundary is inclusive and a round bracket indicates the boundary is exclusive.
Then remove the rest after the decimal point. (i.e., get the floor) - using Math.floor(). This can be done.
If you know how to read the random number table to pick a random number, you know the above process (multiplying by 1, 10, 100 and so on) does not violate the one that I was mentioned at the beginning (because it changes only the place of the decimal point).
Study the following example and develop it to your needs.
If you need a sample [0,9] then the floor of n10 is your answer and if you need [0,99] then the floor of n100 is your answer and so on.
Now let’s enter into your role:
You've asked for numbers in a specific range. (In this case you are biased among that range. By taking a number from [1,6] by roll a die, then you are biased into [1,6], but still it is a random number if and only if the die is unbiased.)
So consider your range ==> [78, 247]
number of elements of the range = 247 - 78 + 1 = 170; (since both the boundaries are inclusive).
/* Method 1: */
var i = 78, j = 247, k = 170, a = [], b = [], c, d, e, f, l = 0;
for(; i <= j; i++){ a.push(i); }
while(l < 170){
c = Math.random()*100; c = Math.floor(c);
d = Math.random()*100; d = Math.floor(d);
b.push(a[c]); e = c + d;
if((b.length != k) && (e < k)){ b.push(a[e]); }
l = b.length;
}
console.log('Method 1:');
console.log(b);
/* Method 2: */
var a, b, c, d = [], l = 0;
while(l < 170){
a = Math.random()*100; a = Math.floor(a);
b = Math.random()*100; b = Math.floor(b);
c = a + b;
if(c <= 247 || c >= 78){ d.push(c); }else{ d.push(a); }
l = d.length;
}
console.log('Method 2:');
console.log(d);
Note: In method one, first I created an array which contains numbers that you need and then randomly put them into another array.
In method two, generate numbers randomly and check those are in the range that you need. Then put it into an array. Here I generated two random numbers and used the total of them to maximize the speed of the program by minimizing the failure rate that obtaining a useful number. However, adding generated numbers will also give some biasedness. So I would recommend my first method to generate random numbers within a specific range.
In both methods, your console will show the result (press F12 in Chrome to open the console).
function getRandomInt(lower, upper)
{
//to create an even sample distribution
return Math.floor(lower + (Math.random() * (upper - lower + 1)));
//to produce an uneven sample distribution
//return Math.round(lower + (Math.random() * (upper - lower)));
//to exclude the max value from the possible values
//return Math.floor(lower + (Math.random() * (upper - lower)));
}
To test this function, and variations of this function, save the below HTML/JavaScript to a file and open with a browser. The code will produce a graph showing the distribution of one million function calls. The code will also record the edge cases, so if the the function produces a value greater than the max, or less than the min, you.will.know.about.it.
<html>
<head>
<script type="text/javascript">
function getRandomInt(lower, upper)
{
//to create an even sample distribution
return Math.floor(lower + (Math.random() * (upper - lower + 1)));
//to produce an uneven sample distribution
//return Math.round(lower + (Math.random() * (upper - lower)));
//to exclude the max value from the possible values
//return Math.floor(lower + (Math.random() * (upper - lower)));
}
var min = -5;
var max = 5;
var array = new Array();
for(var i = 0; i <= (max - min) + 2; i++) {
array.push(0);
}
for(var i = 0; i < 1000000; i++) {
var random = getRandomInt(min, max);
array[random - min + 1]++;
}
var maxSample = 0;
for(var i = 0; i < max - min; i++) {
maxSample = Math.max(maxSample, array[i]);
}
//create a bar graph to show the sample distribution
var maxHeight = 500;
for(var i = 0; i <= (max - min) + 2; i++) {
var sampleHeight = (array[i]/maxSample) * maxHeight;
document.write('<span style="display:inline-block;color:'+(sampleHeight == 0 ? 'black' : 'white')+';background-color:black;height:'+sampleHeight+'px"> [' + (i + min - 1) + ']: '+array[i]+'</span> ');
}
document.write('<hr/>');
</script>
</head>
<body>
</body>
</html>
For a random integer with a range, try:
function random(minimum, maximum) {
var bool = true;
while (bool) {
var number = (Math.floor(Math.random() * maximum + 1) + minimum);
if (number > 20) {
bool = true;
} else {
bool = false;
}
}
return number;
}
Here is a function that generates a random number between min and max, both inclusive.
const randomInt = (max, min) => Math.round(Math.random() * (max - min)) + min;
To get a random number say between 1 and 6, first do:
0.5 + (Math.random() * ((6 - 1) + 1))
This multiplies a random number by 6 and then adds 0.5 to it. Next round the number to a positive integer by doing:
Math.round(0.5 + (Math.random() * ((6 - 1) + 1))
This round the number to the nearest whole number.
Or to make it more understandable do this:
var value = 0.5 + (Math.random() * ((6 - 1) + 1))
var roll = Math.round(value);
return roll;
In general, the code for doing this using variables is:
var value = (Min - 0.5) + (Math.random() * ((Max - Min) + 1))
var roll = Math.round(value);
return roll;
The reason for taking away 0.5 from the minimum value is because using the minimum value alone would allow you to get an integer that was one more than your maximum value. By taking away 0.5 from the minimum value you are essentially preventing the maximum value from being rounded up.
Using the following code, you can generate an array of random numbers, without repeating, in a given range.
function genRandomNumber(how_many_numbers, min, max) {
// Parameters
//
// how_many_numbers: How many numbers you want to
// generate. For example, it is 5.
//
// min (inclusive): Minimum/low value of a range. It
// must be any positive integer, but
// less than max. I.e., 4.
//
// max (inclusive): Maximum value of a range. it must
// be any positive integer. I.e., 50
//
// Return type: array
var random_number = [];
for (var i = 0; i < how_many_numbers; i++) {
var gen_num = parseInt((Math.random() * (max-min+1)) + min);
do {
var is_exist = random_number.indexOf(gen_num);
if (is_exist >= 0) {
gen_num = parseInt((Math.random() * (max-min+1)) + min);
}
else {
random_number.push(gen_num);
is_exist = -2;
}
}
while (is_exist > -1);
}
document.getElementById('box').innerHTML = random_number;
}
Random whole number between lowest and highest:
function randomRange(low, high) {
var range = (high-low);
var random = Math.floor(Math.random()*range);
if (random === 0) {
random += 1;
}
return low + random;
}
It is not the most elegant solution, but something quick.
I found this simple method on W3Schools:
Math.floor((Math.random() * max) + min);
Math.random() is fast and suitable for many purposes, but it's not appropriate if you need cryptographically-secure values (it's not secure), or if you need integers from a completely uniform unbiased distribution (the multiplication approach used in others answers produces certain values slightly more often than others).
In such cases, we can use crypto.getRandomValues() to generate secure integers, and reject any generated values that we can't map uniformly into the target range. This will be slower, but it shouldn't be significant unless you're generating extremely large numbers of values.
To clarify the biased distribution concern, consider the case where we want to generate a value between 1 and 5, but we have a random number generator that produces values between 1 and 16 (a 4-bit value). We want to have the same number of generated values mapping to each output value, but 16 does not evenly divide by 5: it leaves a remainder of 1. So we need to reject 1 of the possible generated values, and only continue when we get one of the 15 lesser values that can be uniformly mapped into our target range. Our behaviour could look like this pseudocode:
Generate a 4-bit integer in the range 1-16.
If we generated 1, 6, or 11 then output 1.
If we generated 2, 7, or 12 then output 2.
If we generated 3, 8, or 13 then output 3.
If we generated 4, 9, or 14 then output 4.
If we generated 5, 10, or 15 then output 5.
If we generated 16 then reject it and try again.
The following code uses similar logic, but generates a 32-bit integer instead, because that's the largest common integer size that can be represented by JavaScript's standard number type. (This could be modified to use BigInts if you need a larger range.) Regardless of the chosen range, the fraction of generated values that are rejected will always be less than 0.5, so the expected number of rejections will always be less than 1.0 and usually close to 0.0; you don't need to worry about it looping forever.
const randomInteger = (min, max) => {
const range = max - min;
const maxGeneratedValue = 0xFFFFFFFF;
const possibleResultValues = range + 1;
const possibleGeneratedValues = maxGeneratedValue + 1;
const remainder = possibleGeneratedValues % possibleResultValues;
const maxUnbiased = maxGeneratedValue - remainder;
if (!Number.isInteger(min) || !Number.isInteger(max) ||
max > Number.MAX_SAFE_INTEGER || min < Number.MIN_SAFE_INTEGER) {
throw new Error('Arguments must be safe integers.');
} else if (range > maxGeneratedValue) {
throw new Error(`Range of ${range} (from ${min} to ${max}) > ${maxGeneratedValue}.`);
} else if (max < min) {
throw new Error(`max (${max}) must be >= min (${min}).`);
} else if (min === max) {
return min;
}
let generated;
do {
generated = crypto.getRandomValues(new Uint32Array(1))[0];
} while (generated > maxUnbiased);
return min + (generated % possibleResultValues);
};
console.log(randomInteger(-8, 8)); // -2
console.log(randomInteger(0, 0)); // 0
console.log(randomInteger(0, 0xFFFFFFFF)); // 944450079
console.log(randomInteger(-1, 0xFFFFFFFF));
// Error: Range of 4294967296 covering -1 to 4294967295 is > 4294967295.
console.log(new Array(12).fill().map(n => randomInteger(8, 12)));
// [11, 8, 8, 11, 10, 8, 8, 12, 12, 12, 9, 9]
Here is an example of a JavaScript function that can generate a random number of any specified length without using Math.random():
function genRandom(length)
{
const t1 = new Date().getMilliseconds();
var min = "1", max = "9";
var result;
var numLength = length;
if (numLength != 0)
{
for (var i = 1; i < numLength; i++)
{
min = min.toString() + "0";
max = max.toString() + "9";
}
}
else
{
min = 0;
max = 0;
return;
}
for (var i = min; i <= max; i++)
{
// Empty Loop
}
const t2 = new Date().getMilliseconds();
console.log(t2);
result = ((max - min)*t1)/t2;
console.log(result);
return result;
}
Use:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<script>
/*
Assuming that window.crypto.getRandomValues
is available, the real range would be from
0 to 1,998 instead of 0 to 2,000.
See the JavaScript documentation
for an explanation:
https://developer.mozilla.org/en-US/docs/Web/API/RandomSource/getRandomValues
*/
var array = new Uint8Array(2);
window.crypto.getRandomValues(array);
console.log(array[0] + array[1]);
</script>
</body>
</html>
Uint8Array creates an array filled with a number up to three digits which would be a maximum of 999. This code is very short.
This is my take on a random number in a range, as in I wanted to get a random number within a range of base to exponent. E.g., base = 10, exponent = 2, gives a random number from 0 to 100, ideally, and so on.
If it helps using it, here it is:
// Get random number within provided base + exponent
// By Goran Biljetina --> 2012
function isEmpty(value) {
return (typeof value === "undefined" || value === null);
}
var numSeq = new Array();
function add(num, seq) {
var toAdd = new Object();
toAdd.num = num;
toAdd.seq = seq;
numSeq[numSeq.length] = toAdd;
}
function fillNumSeq (num, seq) {
var n;
for(i=0; i<=seq; i++) {
n = Math.pow(num, i);
add(n, i);
}
}
function getRandNum(base, exp) {
if (isEmpty(base)) {
console.log("Specify value for base parameter");
}
if (isEmpty(exp)) {
console.log("Specify value for exponent parameter");
}
fillNumSeq(base, exp);
var emax;
var eseq;
var nseed;
var nspan;
emax = (numSeq.length);
eseq = Math.floor(Math.random()*emax) + 1;
nseed = numSeq[eseq].num;
nspan = Math.floor((Math.random())*(Math.random()*nseed)) + 1;
return Math.floor(Math.random()*nspan) + 1;
}
console.log(getRandNum(10, 20), numSeq);
//Testing:
//getRandNum(-10, 20);
//console.log(getRandNum(-10, 20), numSeq);
//console.log(numSeq);
This I guess, is the most simplified of all the contributions.
maxNum = 8,
minNum = 4
console.log(Math.floor(Math.random() * (maxNum - minNum) + minNum))
console.log(Math.floor(Math.random() * (8 - 4) + 4))
This will log random numbers between 4 and 8 into the console, 4 and 8 inclusive.
Ionuț G. Stan wrote a great answer, but it was a bit too complex for me to grasp. So, I found an even simpler explanation of the same concepts at Math.floor( Math.random () * (max - min + 1)) + min) Explanation by Jason Anello.
Note: The only important thing you should know before reading Jason's explanation is a definition of "truncate". He uses that term when describing Math.floor(). Oxford dictionary defines "truncate" as:
Shorten (something) by cutting off the top or end.
A function called randUpTo that accepts a number and returns a random whole number between 0 and that number:
var randUpTo = function(num) {
return Math.floor(Math.random() * (num - 1) + 0);
};
A function called randBetween that accepts two numbers representing a range and returns a random whole number between those two numbers:
var randBetween = function (min, max) {
return Math.floor(Math.random() * (max - min - 1)) + min;
};
A function called randFromTill that accepts two numbers representing a range and returns a random number between min (inclusive) and max (exclusive)
var randFromTill = function (min, max) {
return Math.random() * (max - min) + min;
};
A function called randFromTo that accepts two numbers representing a range and returns a random integer between min (inclusive) and max (inclusive):
var randFromTo = function (min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
};
You can you this code snippet,
let randomNumber = function(first, second) {
let number = Math.floor(Math.random()*Math.floor(second));
while(number < first) {
number = Math.floor(Math.random()*Math.floor(second));
}
return number;
}
I would like get a random number in a range excluding one number (e.g. from 1 to 1000 exclude 577). I searched for a solution, but never solved my issue.
I want something like:
Math.floor((Math.random() * 1000) + 1).exclude(577);
I would like to avoid for loops creating an array as much as possible, because the length is always different (sometimes 1 to 10000, sometimes 685 to 888555444, etc), and the process of generating it could take too much time.
I already tried:
Javascript - Generating Random numbers in a Range, excluding certain numbers
How can I generate a random number within a range but exclude some?
How could I achieve this?
The fastest way to obtain a random integer number in a certain range [a, b], excluding one value c, is to generate it between a and b-1, and then increment it by one if it's higher than or equal to c.
Here's a working function:
function randomExcluded(min, max, excluded) {
var n = Math.floor(Math.random() * (max-min) + min);
if (n >= excluded) n++;
return n;
}
This solution only has a complexity of O(1).
One possibility is not to add 1, and if that number comes out, you assign the last possible value.
For example:
var result = Math.floor((Math.random() * 100000));
if(result==577) result = 100000;
In this way, you will not need to re-launch the random method, but is repeated. And meets the objective of being a random.
As #ebyrob suggested, you can create a function that makes a mapping from a smaller set to the larger set with excluded values by adding 1 for each value that it is larger than or equal to:
// min - integer
// max - integer
// exclusions - array of integers
// - must contain unique integers between min & max
function RandomNumber(min, max, exclusions) {
// As #Fabian pointed out, sorting is necessary
// We use concat to avoid mutating the original array
// See: http://stackoverflow.com/questions/9592740/how-can-you-sort-an-array-without-mutating-the-original-array
var exclusionsSorted = exclusions.concat().sort(function(a, b) {
return a - b
});
var logicalMax = max - exclusionsSorted.length;
var randomNumber = Math.floor(Math.random() * (logicalMax - min + 1)) + min;
for(var i = 0; i < exclusionsSorted.length; i++) {
if (randomNumber >= exclusionsSorted[i]) {
randomNumber++;
}
}
return randomNumber;
}
Example Fiddle
Also, I think #JesusCuesta's answer provides a simpler mapping and is better.
Update: My original answer had many issues with it.
To expand on #Jesus Cuesta's answer:
function RandomNumber(min, max, exclusions) {
var hash = new Object();
for(var i = 0; i < exclusions.length; ++i ) { // TODO: run only once as setup
hash[exclusions[i]] = i + max - exclusions.length;
}
var randomNumber = Math.floor((Math.random() * (max - min - exclusions.length)) + min);
if (hash.hasOwnProperty(randomNumber)) {
randomNumber = hash[randomNumber];
}
return randomNumber;
}
Note: This only works if max - exclusions.length > maximum exclusion. So close.
You could just continue generating the number until you find it suits your needs:
function randomExcluded(start, end, excluded) {
var n = excluded
while (n == excluded)
n = Math.floor((Math.random() * (end-start+1) + start));
return n;
}
myRandom = randomExcluded(1, 10000, 577);
By the way this is not the best solution at all, look at my other answer for a better one!
Generate a random number and if it matches the excluded number then add another random number(-20 to 20)
var max = 99999, min = 1, exclude = 577;
var num = Math.floor(Math.random() * (max - min)) + min ;
while(num == exclude || num > max || num < min ) {
var rand = Math.random() > .5 ? -20 : 20 ;
num += Math.floor((Math.random() * (rand));
}
import random
def rng_generator():
a = random.randint(0, 100)
if a == 577:
rng_generator()
else:
print(a)
#main()
rng_generator()
Exclude the number from calculations:
function toggleRand() {
// demonstration code only.
// this algorithm does NOT produce random numbers.
// return `0` - `576` , `578` - `n`
return [Math.floor((Math.random() * 576) + 1)
,Math.floor(Math.random() * (100000 - 578) + 1)
]
// select "random" index
[Math.random() > .5 ? 0 : 1];
}
console.log(toggleRand());
Alternatively, use String.prototype.replace() with RegExp /^(577)$/ to match number that should be excluded from result; replace with another random number in range [0-99] utilizing new Date().getTime(), isNaN() and String.prototype.slice()
console.log(
+String(Math.floor(Math.random()*(578 - 575) + 575))
.replace(/^(577)$/,String(isNaN("$1")&&new Date().getTime()).slice(-2))
);
Could also use String.prototype.match() to filter results:
console.log(
+String(Math.floor(Math.random()*10))
.replace(/^(5)$/,String(isNaN("$1")&&new Date().getTime()).match(/[^5]/g).slice(-1)[0])
);