Javascript - Generate random small intervals in a range - javascript

I am trying to generate random intervals between the range: 2,5k - 10M.
Currently, I am doing the following:
const MIN_NUMBER = 2500;
const MAX_NUMBER = 10000000;
const random = (min, max, floating = false) => {
const result = Math.random() * max + min;
return floating ? result : Math.floor(result);
};
const min = random(MIN_NUMBER, MAX_NUMBER / 10);
const max = random(min, min * 10);
const interval = `[${min}, ${max}]`;
console.log(interval);
But as you can see, the probability that the generated interval is small/medium is not very high.
I want to get random intervals like:
[2500, 10400]
[2500, 9919]
[3000000, 3301029]
[500000, 611223]
I am not following any specific rule, but as you can see, in relation with
[2500, 400000]
[2500, 71000]
[3000000, 10000000]
[500000, 3120000]
they are considered "small/medium", because there is not a "really huge" diff between the max and the min).
With my current algorithm, you can check that the generated average diff is high:
const MIN_NUMBER_OF_LIKES = 2500;
const MAX_NUMBER_OF_LIKES = 10000000;
const random = (min, max, floating = false) => {
const result = Math.random() * max + min;
return floating ? result : Math.floor(result);
};
let averageDiff = 0;
const numIterations = 1000;
for (let i = 0; i < numIterations; i++) {
const min = random(MIN_NUMBER_OF_LIKES, MAX_NUMBER_OF_LIKES / 10);
const max = random(min, min * 10);
averageDiff += max - min;
}
averageDiff /= numIterations;
console.log({ averageDiff });
How can I do for getting random small segments instead?
--Note: the difference between the randomly generated intervals is random too, but it has to be "small/medium" (not as huge as with my current solution).

What about first choosing random size of interval within the size you wish - so you get the value of INTERVAL_SIZE. As second step you randomly find the minimum between MIN_NUMBER_OF_LIKES and MAX_NUMBER_OF_LIKES - INTERVAL so you get STARTPOINT.
So final INTERVAL will have STARTPOINT and ENDPOINT = STARTPOINT + INTERVAL_SIZE

Based on #krnz solution:
const MIN = 2500;
const MAX = 10000000;
const random = (min, max, floating = false) => {
const result = Math.random() * max + min;
return floating ? result : Math.floor(result);
};
function generateRandomSmallInterval() {
const intervalSize = random(1000, 10000);
const start = random(MIN, MAX-intervalSize);
const end = start + intervalSize;
return {start, end};
}
const interval = generateRandomSmallInterval();
console.log({ interval });
console.log(`Diff: ${interval.end - interval.start}`);
Refactored and generalized code using lodash:
import { random } from 'lodash';
function generateRandomIntervalInRange(min, max, maxIntervalSize = max - min) {
if (max - min <= 0) {
throw new Error("The `max` argument must be greater than `min`.");
}
if (maxIntervalSize <= 0) {
throw new Error("The maximum interval size must be greater than 0.");
}
if (maxIntervalSize > max - min) {
throw new Error(
`The maximum interval size mustn't be greater than ${max - min}.`
);
}
const intervalSize = random(1, maxIntervalSize);
const start = random(min, max - intervalSize);
const end = start + intervalSize;
return { start, end };
}
//
// MAIN
//
const MIN = 1;
const MAX = 10;
const MAX_INTERVAL_SIZE = 5;
console.log(generateRandomIntervalInRange(MIN, MAX, MAX_INTERVAL_SIZE));

Related

Generate array of numbers between max and min value with an interval between 2 values

I have the following data structure and I need to create an array of numbers that matches given configuration -
{
min: 1000,
max: 10000,
interval: 1000
}
What would be a proper function that outputs below array -
[1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000]
It can be done through a for loop -
const output = [];
for (let i = input.min; i <= input.max; i += input.interval) {
output.push(i);
}
console.log(output)
But I want to see if there's a cleaner way to do this using Array.fill & map -
new Array((max - min + interval)).fill(undefined).map((_, i) => (i + min))
You can figure out how many items should be in the result by dividing the difference between max and min by the interval, then create the array with Array.from, using the mapper's index to figure out how much to add to the min for each value of the array:
const min = 1000,
max = 10000,
interval = 1000;
const length = (max - min) / interval + 1;
const arr = Array.from({ length }, (_, i) => min + i * interval);
console.log(arr);
Get the array's length by rounding up (max - min) / interval. Get each step's value by multiplying the current index (i) by the interval and add the min.
const fn = ({ min, max, interval }) =>
new Array(Math.ceil((max - min) / interval)) // the array's length as a function of max, min, and interval
.fill(undefined)
.map((_, i) => (i * interval + min)) // a step is a product of i and interval + min
const result = fn ({
min: 1000,
max: 10001,
interval: 1000
})
console.log(result)
You can use spread operator to generate the required array.
const min = 1000;
const max = 10000;
const interval = 1000;
const ret = [...Array((max - min) / interval + 1)].map(
(_, i) => min + interval * i
);
console.log(ret);
I don't know if it's cleaner, but an interesting approach would be to use a generator. One example inspired from the Mozilla documentation
function* makeRangeIterator(start = 0, end = 100, step = 1) {
while(start <= end) {
yield start;
start += step
}
}
const sequenceGenerator = makeRangeIterator(1000, 10000, 1000);
//you have to loop through the generator
for(let value of sequenceGenerator) {
console.log(value)
}

Getting infinity loop while checking factors of a number in while loop

I am struggling with infinite loop problem while Array exercise implementation which needs to be done with Java Script functional way:
I have a code which creates an array and fills its values with numbers which fulfil condition:
Each array element has a value,
which we draw from the range <100, 200> until the sum of digits is
a number having exactly two dividers, not counting 1 and this one
numbers.
I have a code like below:
const generateNumber = (min, max) =>
Math.floor(Math.random() * (max - min + 1)) + Math.floor(min);
const unities = number => number % 10;
const hundreds = number => Math.floor((number % 1000) / 100);
const tens = number => Math.floor((number % 100) / 10);
const sumDigits = (number) => unities(number) + hundreds(number) + tens(number);
const countNumberFactors = number => Array
.from(Array(number + 1), (_, i) => i)
.filter(i => number % i === 0)
.slice(1, -1)
.length;
const generateNumberUntilConditionNotAchieve = (min, max) => {
let number = generateNumber(min, max);
const digitsSum = sumDigits(number);
while (countNumberFactors(digitsSum) === 2) {
number = generateNumber(min, max)
}
return number;
}
const generateArray = (minArrSize, maxArrSize, minItemValue, maxItemValue) =>
Array(generateNumber(minArrSize, maxArrSize))
.fill(0)
.map(
() => generateNumberUntilConditionNotAchieve(minItemValue,
maxItemValue));
const main = () => {
const generatedArray = generateArray(1, 5, 100, 200);
console.log("Array -> " + generatedArray);
}
main();
For small minArraySize and maxArraySize values sometimes I am receiving desirable result but for params like <10, 100> my IDE is freezing. On online editor with pasted above code, I am receiving information about the infinite loop on line:
while (countNumberFactors(digitsSum) === 2)
I tried to investigate a root cause by trial and error but I did not find out a solution. I will be grateful for suggestions on how to solve the above infinite loop problem.
You are changing number but checking digitsSum. All you need to do to fix this is add digitsSum = sumDigits(number) in the while loop. e.g.
const generateNumberUntilConditionNotAchieve = (min, max) => {
let number = generateNumber(min, max);
const digitsSum = sumDigits(number);
while (countNumberFactors(digitsSum) === 2) {
number = generateNumber(min, max);
digitsSum = sumDigits(number);
}
return number;
}

Compounding interest monthly with a deposit

I want to compound interest on a weekly/fortnightly/monthly/annual basis.
I also want an option to have a deposit amount that can be added in.
I have already tried the standard formula of calculating the final amount accrued, as seen here:
(source: gstatic.com)
For example here is my method for calculating the interest compounding weekly:
function calculateWeekly(state: any) {
const { savings, deposit ,interest, timePeriodSelector, timePeriodLength } = state;
let numberOfYears = 0;
if (timePeriodSelector === "weekly") {
numberOfYears = timePeriodLength / weeksInAYear;
} else if (timePeriodSelector === "fortnightly") {
numberOfYears = (timePeriodLength / weeksInAYear) * 2;
} else if (timePeriodSelector === "monthly") {
numberOfYears = (timePeriodLength / weeksInAYear) * weeksInAMonth;
} else if (timePeriodSelector === "annually") {
numberOfYears = (timePeriodLength / weeksInAYear) * weeksInAYear;
}
const weeklyRate = interest / 100 / weeksInAYear;
const lengthOfCompunding = numberOfYears * weeksInAYear;
let startingFigure = parseInt(savings) + parseInt(deposit);
//total gets added on for every time cycle of week
let total =
(startingFigure * (Math.pow(1 + weeklyRate, lengthOfCompunding) - 1)) / weeklyRate;
return roundToTwoDP(total);
}
The issue with the above code is that the deposit gets added into the calculation every time the interest accrues. So a deposit of $10 weekly for 10 weeks will actually get added up to $100.
I attempted a method to accrue the interest using a loop for each week here:
// loops how many times to compound the interest
for(let i = numberOfYears - (1/weeksInAYear); i > 0; i-= (1/weeksInAYear)){
let interestGained = (total * (Math.pow((1 + weeklyRate), lengthOfCompunding))) - total;
total += interestGained + savings;
}
Thanks for any help!
This should do what you want:
const range = (min, max) => {
const size = 1 + max - min
return [...Array(size).keys()].map(n => n + min)
}
const weeksInAYear = 52
const addWeeklyInterest = interestRatePerWeek => (savings, _) => savings + savings * interestRatePerWeek
const calculateTotal = (savings, numberOfYears, interestRatePerWeek) => {
const numberOfWeeks = numberOfYears * weeksInAYear
return range(1, numberOfWeeks).reduce(addWeeklyInterest(interestRatePerWeek), savings)
}
console.log(calculateTotal(1000.00, 1, 0.02))
Output is 2800.328185448178. You might want to round that for display purposes, but also keep in mind that if accuracy is important, you can't use floating-point numbers.

Variables occasionally don't generate: max call stack size issue

Error detailed towards bottom of post, but before getting to that, I'll give some background info. I have the following script, which generates:
1) TWO DIFFERENT NUMBERS BETWEEN 2 & 20
var GenerateRandomNumber1to20No1 = GenerateRandomNumber1to20No1();
$('.GenerateRandomNumber1to20No1').html(GenerateRandomNumber1to20No1);
function GenerateRandomNumber1to20No2() {
var min = 2, max = 20;
var random = Math.floor(Math.random() * (max - min + 1)) + min;
return (random !== GenerateRandomNumber1to20No1) ? random: GenerateRandomNumber1to20No2();
}
var GenerateRandomNumber1to20No2 = GenerateRandomNumber1to20No2();
$('.GenerateRandomNumber1to20No2').html(GenerateRandomNumber1to20No2);
function GenerateRandomNumber1to20No3() {
var min = 2, max = 20;
var random = Math.floor(Math.random() * (max - min + 1)) + min;
return (random !== GenerateRandomNumber1to20No1 && random!==GenerateRandomNumber1to20No2) ? random: GenerateRandomNumber1to20No3();
}
2) TWO DIFFERENT NUMBERS LESS THAN THE PREVIOUS 2 NUMBERS
function GenerateRandomNumber1to20lessthanNo1() {
var min = 2, max = GenerateRandomNumber1to20No1-1;
var random = Math.floor(Math.random() * (max - min + 1)) + 1;
return random;
}
var GenerateRandomNumber1to20lessthanNo1= GenerateRandomNumber1to20lessthanNo1();
$('.GenerateRandomNumber1to20lessthanNo1').html(GenerateRandomNumber1to20lessthanNo1);
function GenerateRandomNumber1to20lessthanNo2() {
var min = 2, max = (GenerateRandomNumber1to20No2 - 1);
var random = Math.floor(Math.random() * (max - min + 1)) + min;
return (random !== GenerateRandomNumber1to20lessthanNo1) ? random: GenerateRandomNumber1to20lessthanNo2();
}
var GenerateRandomNumber1to20lessthanNo2 = GenerateRandomNumber1to20lessthanNo2();
$('.GenerateRandomNumber1to20lessthanNo2').html(GenerateRandomNumber1to20lessthanNo2);
3) 2 DIFFERENT PRIME NUMBERS
function PrimeNumber1() {
var PrimeNumber1= ['3', '5', '7', '11'];
var PrimeNumber1random= PrimeNumber1[Math.floor(Math.random() * PrimeNumber1.length)];
return PrimeNumber1random;
}
var PrimeNumber1replacer= PrimeNumber1();
$('.PrimeNumber1replacer').html(PrimeNumber1replacer);
function PrimeNumber2() {
var PrimeNumber2= ['3', '5', '7', '11'];
var PrimeNumber2random= PrimeNumber2[Math.floor(Math.random() * PrimeNumber2.length)];
return (PrimeNumber2random !== PrimeNumber1replacer) ? PrimeNumber2random: PrimeNumber2();
}
var PrimeNumber2replacer= PrimeNumber2();
$('.PrimeNumber2replacer').html(PrimeNumber2replacer);
I USE THESE VARIABLES TO REPLACE ELEMENTS WITH CORRESPONDING CLASSES WITH THE VALUES OF THE RESPECTIVE VARIABLES
<span class = "GenerateRandomNumber1to20nNo2"></span>
<span class = "GenerateRandomNumber1to20nNo2"></span>
<span class = "GenerateRandomNumber1to20lessthanNo1"></span>
<span class = "GenerateRandomNumber1to20lessthanNo2"></span>
<span class = "PrimeNumber1replacer"></span>
<span class = "PrimeNumber2replacer"></span>
Sometimes, the code works fine: the variables generate and the elements are replaced with those variables. Other times, the variables don't populate and I get one of the two following errors:
Uncaught RangeError: Maximum call stack size exceeded
at GenerateRandomNumber1to20lessthanNo2 *[or No1]*
OR
Uncaught TypeError: PrimeNumber2 *[or 1]* is not a function
at PrimeNumber2
I tried to do some research on Stackoverflow and it seems it might be an issue with recursion, but I have no idea how to fix this issue. If anyone has any advice, I would appreciate it.
Thank you!
You get a stack overflow because almost none of the JS engines are ES6 compliant yet so even though you use tail recursion you blow the stack. The best thing right now is to rewrite it into a loop that does the same until you have succeded.
function generateRandomNumber(predicate = v => true) {
const min = 2;
const max = 20;
let random;
do {
random = Math.floor(Math.random() * (max - min + 1)) + 1;
} while (!predicate(random));
return random;
}
// two different numbers
const first1 = generateRandomNumber();
const second1 = generateRandomNumber(v => v !== first1);
// two different number less than previous
const first2 = generateRandomNumber();
const second2 = generateRandomNumber(v => v < first2); // possible infinite loop
// two different prime numbers
function isPrime(n) {
if (n % 2 === 0) return n == 2
const limit = Math.sqrt(n);
for (let i = 3; i <= limit; i += 2) {
if (n % i === 0)
return false;
}
return true;
}
const first3 = generateRandomNumber(isPrime);
const second3 = generateRandomNumber(v => isPrime(v) && v !== first3);
I've left out the code that puts the values onto the DOM since it's not very interesting. I don't name the variables after the function since they share the same namespace and thus after setting the name GenerateRandomNumber1to20No1 the function has been replaced with the value.
Note that i mention the "two different number less than previous" that you might get an infinite loop. There is a 5,5% chance that the first random number is 2. There is no number generated by that same function which is smaller than 2 and thus it will not terminate.

Generate incremented random numbers between 0 to 100 every second where new number should be greater than the previous nymber

I need a javascript where with every second call i get a random number between 0-100 where current number should be greater than previous number. I wrote a code to get random numbers every second but stuck at generating it with increment.
<script>
var span = document.getElementsByTagName("span")[3];
var i = 100;
(function randNumber() {
var a = Math.floor((Math.random() * i) + 1);
span.innerHTML = "RandNumber: " + a;
setTimeout( randNumber, 1000);
})();
Note : The numbers should generate randomly.
example result may be : 2,5,7,8,22,23,34,56,78,88.....
You should not create a new random number between zero and your maximum (i), but just between the last created number (lastNumber) and max (i).
Also you might want to stop, when the random numbers reached the maximum.
var span = document.getElementsByTagName("span")[3],
i = 100,
lastNumber = 0;
function randNumber() {
lastNumber = lastNumber + Math.floor( Math.random() * (i - lastNumber) + 1 );
span.innerHTML = lastNumber;
if( lastNumber < i ) {
setTimeout( randNumber, 1000 );
}
}
randNumber();
AS for the comments and the requirement of a minimum amount of steps until reaching the maximum:
In each iteration, just increase you number by a random value between 1 and ((max - min) / steps. It is pretty much the same code as above.
var span = document.getElementsByTagName("span")[3],
max = 100,
min = 0,
lastNumber = 0,
minSteps = 30;
// how wide can the average step be at most?
var stepWidth = (max - min) / minSteps;
function randNumber() {
lastNumber = lastNumber + Math.floor( Math.random() * stepWidth + 1 );
span.innerHTML = lastNumber;
if( lastNumber < max ) {
setTimeout( randNumber, 1000 );
}
}
randNumber();
If you extract the "between two numbers" logic into its own function like this, this becomes a lot easier. This way, you just generate a number between your last generated number and your maximum.
var max = 100
var last_number = 1
var interval_id = setInterval(function(){
last_number = get_random_number_greater_between(last_number, max)
span.innerHTML = "RandNumber: " + last_number;
if(last_number >= max){
window.clearInterval(interval_id)
}
}, 1000)
function get_random_number_greater_between(low, high){
return Math.floor(Math.random() * (high - low + 1) + low);
}
Try this:
// generate a random number from 0 to max - 1.
function rand(max) {
return Math.floor(max * Math.random());
}
// generate a random number from min to max.
function range(min, max) {
return min + rand(1 + max - min);
}
// the loop function
function next(n, callback) {
var m = range(n, 100);
if (m < 100) setTimeout(next, 1000, m + 1, callback);
callback(m);
}
var span = document.getElementById("rand");
next(0, function (m) {
span.innerHTML = "Random number: " + m;
});
<span id="rand"></span>

Categories