JS: get x amount of random numbers inside interval with while loop - javascript

I'm working on a card game with six available suits, but there can be 2-6 players and I only want the number of suits to match the amount of players.
let players = 4
const suits = [1, 2, 3, 4, 5, 6]
Of course, I want them to come out randomly.
I have come up with the following solution:
export function getRandomSuits(nrOfPlayers) {
const rangeMin = 1;
const rangeMax = 6;
const amount = nrOfPlayers;
let nums = [];
let getRandomNumber = function() {
return Math.floor((Math.random() * rangeMax) + rangeMin);
}
while (nums.length < amount) {
let nr = getRandomNumber();
var numberExists = _.indexOf(nums, nr);
console.log(numberExists);
if(numberExists < 0) {
nums.push(nr);
}
}
return nums;
}
It's been a while since I used the "while loop" thingy, so I'm not feeling very comfortable with it.
My questions are:
Is this good practice or is there a better solution?
Are there any performance or other practical reasons why this solution is bad?
Am I overthinking it?

In my opinion, I see no need of the function getRandomNumber(). Nonetheless, this is up to preference.
I would go with:
export function getRandomSuits(nrOfPlayers) {
const rangeMin = 1;
const rangeMax = 6;
let nums = [];
while (nums.length < nrOfPlayers) {
let nr = Math.floor((Math.random() * rangeMax) + rangeMin);
var numberExists = _.indexOf(nums, nr);
if(numberExists < 0) {
nums.push(nr);
}
}
return nums;
}
Not necessarily. Merely a matter of cleanliness and preference.
Maybe? :-)
Second method (Slightly better) with a temporary array:
export function getRandomSuits(nrOfPlayers) {
const rangeMin = 1;
const rangeMax = 6;
var tempNum = [];
for(i = 0; i <= rangeMax - rangeMin; i++){
tempNum[i] = rangeMin + i;
}
let nums = [];
while (nums.length < nrOfPlayers) {
let index = Math.floor(Math.random() * tempNum.length);
var numberExists = _.indexOf(nums, tempNum[index]);
if(numberExists < 0) {
nums.push(tempNum[index]);
tempNum.splice(tempNum, index));
}
}
return nums;
}

Related

generate 2 different random numbers from an Array?

my only issue so far is when the ships have the same value, I get an error message if I'm using VSC.
TypeError: Assignment to constant variable.
I was trying to get to different values by using the condition but I think it doesn't work.
let grid = [[]];
const letters = "abc".toUpperCase();
// create the Grid
const createGrid = (size) => {
let row;
let col = 0;
for (row = 0; row < size; row++) {
grid[row] = [];
for (col = 0; col < size; col++) {
grid[row][col] = `${letters[row]}${col + 1}`;
}
}
};
createGrid(letters.length);
console.table(grid);
// Start the game
//create ships
const flatArray = grid.reduce((acc, curr) => {
return [...acc, ...curr];
}, []);
function getRandomNumber(len) {
return Math.floor(Math.random() * len);
}
const randomNumber1 = getRandomNumber(flatArray.length);
const randomNumber2 = getRandomNumber(flatArray.length);
while(randomNumber1 == randomNumber2) {
getRandomNumber();
}
var shipOne = flatArray[randomNumber1];
var shipTwo = flatArray[randomNumber2];
console.log(shipOne);
console.log(shipTwo);
Fix an infinite loop
I created a snippet from your example code and it runs forever in the error case you are describing.
A problem is that you have a while loop that will run forever:
while(randomNumber1 == randomNumber2) {
getRandomNumber();
}
You run this code as long as randomNumber1 and randomNumber2 are the same but you update neither of them in the loop so it can never be false.
Try this:
while(randomNumber1 == randomNumber2) {
randomNumber2 = getRandomNumber(flatArray.length);
}
You might also make your code a little smarter and remove elements from the flatArray array you have already chosen.
One other approach you could try is to create a function pickRandomEntries() that will pick N random entries from an array, without ever picking the same ones.
To do this, we shuffle a copy of your flatArray(), then pick the first two items.
This way we never have to check for the ships having the same value:
let grid = [[]];
const letters = "abc".toUpperCase();
const createGrid = (size) => {
let row;
let col = 0;
for (row = 0; row < size; row++) {
grid[row] = [];
for (col = 0; col < size; col++) {
grid[row][col] = `${letters[row]}${col + 1}`;
}
}
};
createGrid(letters.length);
const flatArray = grid.reduce((acc, curr) => {
return [...acc, ...curr];
}, []);
// Shuffle an array into a random order (using Fisher-Yates)
function shuffle(arr) {
for(let i = arr.length - 1; i > 0; i--) {
let index = Math.floor(Math.random() * (i + 1));
[arr[index], arr[i]] = [arr[i], arr[index]];
}
return arr;
}
// Pick count random items from the array
function pickRandomEntries(arr, count) {
let shuffled = shuffle([...arr]);
return shuffled.slice(0, count);
}
let [shipOne, shipTwo] = pickRandomEntries(flatArray, 2);
console.log( { shipOne, shipTwo });
Inside of your while loop if the two random numbers were the same you would be stuck inside an infinite loop. Like Peter stated.
To fix this use the code Peter stated but don't forget you still need to pass in the array length into this function. Otherwise you will have an error:
while(randomNumber1 == randomNumber2) {
randomNumber2 = getRandomNumber(flatArray.length);
}
Thanks!!

Need to upsample audio from 8k to 16k

I was using below code for downsampling my audio while recording
let inputSampleRate; let inputBuffer = [];
function init(x) {
inputSampleRate = x;
}
function process(inputFrame) {
for (let i = 0; i < inputFrame.length; i++) {
inputBuffer.push((inputFrame[i]) * 32767);
}
const PV_SAMPLE_RATE = 16000;
const PV_FRAME_LENGTH = 512;
while ((inputBuffer.length * PV_SAMPLE_RATE / inputSampleRate) > PV_FRAME_LENGTH) {
let outputFrame = new Int16Array(PV_FRAME_LENGTH);
let sum = 0;
let num = 0;
let outputIndex = 0;
let inputIndex = 0;
while (outputIndex < PV_FRAME_LENGTH) {
sum = 0;
num = 0;
while (inputIndex < Math.min(inputBuffer.length, (outputIndex + 1) * inputSampleRate / PV_SAMPLE_RATE)) {
sum += inputBuffer[inputIndex];
num++;
inputIndex++;
}
outputFrame[outputIndex] = sum / num;
outputIndex++;
}
postMessage(outputFrame);
inputBuffer = inputBuffer.slice(inputIndex);
}
}
Can anyone please suggest how can I edit this one, so that it can be used to upsample my audio from 8k to 16k?
The traditional way to do upsampling (and downsampling) can be found at Wikipedia article about upsampling.
If you want something really cheap and dirty, just linearly interpolate between samples. So if you have samples x0 and x1, the upsampled values are y0=x0, y2=x1, and the new sample y1=(x0+x1)/2. This isn't great and you might hear artifacts.
Edit:
In your code, you can try something like this:
s0 = inputBuffer[inputIndex];
s1 = inputBuffer[inputIndex + 1];
outputFrame[outputIndex] = s0;
outputFrame[outputIndex + 1] = (s0 + s1)/2;
outputFrame[outputIndex + 2] = s1
You'll have to keep track of the indices so you don't try to access beyond the length of the arrays. This is totally untested.

I need to make an array of 15 random integers. I have a function but dont want numbers to repeat

I'm working on a project for school. I need to generate an array of 15 random integers between 1 & 50. I have a function, but I would not like for the numbers to repeat. (for example, if the number 3 is located at index 0, I would not like for it to show up again in the array.) If I could get some help on not getting repeat numbers, that would be great.
Thank you for any help!
var arr;
function genArray() {
//generates random array
arr = [];
for (var i = 0; i < 15; i++) {
var min = 1;
var max = 50;
var arrayValue = Math.floor(Math.random() * (max - min + 1)) + min;
arr.push(arrayValue);
}
arr.sort(function(a, b) {
return a - b
});
console.log(arr);
}
In the loop generate a new random number while the number is in the array. In other words only continue when the new number is not in the array already.
var arr;
function genArray() {
//generates random array
arr = [];
for (var i = 0; i < 15; i++) {
var min = 1;
var max = 50;
do
{
var arrayValue = Math.floor(Math.random() * (max - min + 1)) + min;
}while(arr.includes(arrayValue))
arr.push(arrayValue);
}
arr.sort(function(a, b) {
return a - b
});
console.log(arr);
}
genArray();
You can make a function in which check the number if its already in array than regenrate the number else push the number in array
var arr;
function genArray() {
//generates random array
arr = [];
for (var i = 0; i < 15; i++) {
var min = 1;
var max = 50;
var arrayValue = Math.floor(Math.random() * max) + min;
if(checkno(arrayValue)==true)
arr.push(arrayValue);
}
arr.sort(function(a, b) {
return a - b
});
console.log(arr);
}
function checkno(var no)
{
for(var i=0;i<arr.length;i++)
{
if(arr[i]==no)
return false;
else
return true;
}
}
An alternate solution involves the Set object, sets only have unique elements, multiple elements of the same value are ignored.
Example of the set object implemented for this use:
var temp = new Set();
while (temp.size < 15) {
var min = 1;
var max = 50;
temp.add(Math.floor(Math.random()*(max-min+1))+min);
}
This approach uses Arrow functions, forEach and includes functions.
let LENGTH = 15;
let numbers = new Array(LENGTH).fill();
let findRandomNumber = (i) => {
let rn;
while (numbers.includes((rn = Math.floor(Math.random() * 50) + 1))) {}
numbers[i] = rn;
};
numbers.forEach((_, i) => findRandomNumber(i));
console.log(numbers.sort((a, b) => a - b));
.as-console-wrapper {
max-height: 100% !important
}
You do not need to check the resulting array and regenerate the number. It is not efficient.
Please take a look at the following snippet:
function get_N_rand(N = 15, min = 1, max = 50) { // set default values
var N_rand = [], range = [];
for (var i = min; i <= max;) range.push(i++); // make array [min..max]
while (N_rand.length < N) { // cut element from [min..max] and put it into result
var rand_idx = ~~(Math.random() * range.length);
N_rand.push(range.splice(rand_idx, 1)[0]);
}
return N_rand;
}
console.log(JSON.stringify( get_N_rand() )); // run with defaults
console.log(JSON.stringify( get_N_rand(6, 10, 80) )); // run with arbitraries

Counting duplicate random numbers from a for loop

I am trying to create a score predictor based on a teams goal difference (football). I am new to JavaScript, and I have managed to get this far.
I want it to be like spinning a ten-sided dice 20 times + the team's goal difference. I have got this bit sorted I think. With my code now I have a list of random numbers logged in the console which is what I wanted. Now I would like to choose a number (e.g., 2) and see how many times this occurs in the list. I'd like to save this in a new variable called homeFinalScore (So if '2' occurs three times in the list of random numbers, the homeFinalScore variable should be 3). I've tried several things but have been unable to sort it yet!
Any help would be extremely helpful. Thank you in advance!
var homeFinalScore = 0;
function calculateScore(){
var homeTeam = document.getElementById("HomeTeam").value;
var awayTeam = document.getElementById("AwayTeam").value;
var homeGd = parseInt(document.getElementById("HomeGD").value);
var awayGd = parseInt(document.getElementById("AwayGD").value);
var homeGd = 20 + homeGd;
var awayGd = 15 + awayGd;
for (i = 0; i < homeGd; i++) {
var randNum = Math.floor(Math.random() * 11);
console.log(randNum);
}
}
You can create an array, use Array.prototype.push() to push randNum to the array, then use Array.prototype.filter(), .length to determine how many occurrences of a value are present within array.
var homeGd = 20 + 2;
var awayGd = 15 + 2;
var arr = [];
function countOccurrences(n, arr) {
return arr.filter(function(value) {
return value === n
}).length;
}
for (i = 0; i < homeGd; i++) {
var randNum = Math.floor(Math.random() * 11);
arr.push(randNum);
}
console.log(arr);
console.log(countOccurrences(2, arr));
Alternatively, you can increment a variable when randNum is equal to a value.
var homeGd = 20 + 2;
var awayGd = 15 + 2;
var n = 0;
var num = 2;
for (i = 0; i < homeGd; i++) {
var randNum = Math.floor(Math.random() * 11);
console.log(randNum);
if (randNum === num) {
++n
}
}
console.log("occurrences of 2:", n);
const homeGd = 10;
const randomNumbers = []; // array of random numbers
for (i = 0; i < homeGd; i++) {
randomNumbers.push(Math.floor(Math.random() * 11));
}
const countBy = randomNumbers.reduce((acc, current) => {
acc[current] = (acc[current] || 0) + 1;
return acc;
}, {});
console.log(countBy);

Lotto generator javascript

Hallo by lotto generator code I got some numbers double in one "generate" cycle. This is the code:
function lottery() {
for (var i=0; i<=7; i++) {
var lottery = Math.floor(Math.random() * 49);
document.getElementById ("lotto" + i).innerHTML = lottery;
}
}
Who knows where is the problem? Thank You!
Just for fun: O(N) solution (other posted are O(N^2))
It uses the Fisher-Yates shuffle algorithm and then after the first 7 elements have been shuffled it takes them as is.
So, no need to check if any of the numbers have already been drawn:
const arr = Array(50).fill(0).map((_, i) => i);
const DRAW = 7;
const rand = (from, to) => Math.floor(Math.random() * (to - from + 1)) + from;
for (let i = 0; i < DRAW; ++i) {
const r = rand(i, arr.length - 1);
[arr[r], arr[i]] = [arr[i], arr[r]];
}
const selected = arr.slice(0, DRAW);
References:
Online demo
Like #JNK commented, you need to store used values to avoid using them again.
The easiest way to do this would be to store them in an array, and check if they've been used.
function lottery() {
var used = [];
for (var i=0; i<=7; i++) {
var lottery;
while(true) { // this loop keeps going until a new number is found
lottery = Math.floor(Math.random() * 49);
var newNum = true;
for(var j=0; j<used.length; j++) {
if(used[j] == lottery) {
newNum = false; // if already used, set newNum to false
break;
}
}
if(newNum) { // if not already used, then add num to used array
used.push(lottery);
break;
}
}
document.getElementById ("lotto" + i).innerHTML = lottery;
}
}
All credits to JNK for this one from his link. This isn't very intuitive (it's minimized, like in code golf), but it's an interesting solution.
Optimized random number generator (86 bytes):
var getRandomLottoNumbers = function () {
var n=[],i=0;
for(;++i<50;)
n.push(i);
for(;--i>6;)
n.splice(i*Math.random()|0,1);
return n
};
Full explanation here.
Similar approach as the others, however just using one loop.
function lottery() {
var winners = [];
while (winners.length < 7) {
var lottery = Math.floor(Math.random() * 49);
if (winners.indexOf(lottery) === -1) {
winners.push(lottery);
document.getElementById ("lotto" + winners.length).innerHTML = lottery;
}
}
}

Categories