How to generate two dependent numbers in javascript - javascript

Im trying to generate two random numbers
let them x and y,
and i want x to be always greater than y.
how can i do that?

You mean something like that?
// get random number between min and max
function random(min, max) {
return Math.floor(min + Math.random() * (max - min));
}
const y = random(0, 10);
const x = random(y + 1, 10);
console.log('x', x);
console.log('y', y);

How about just generating 2 random values and swapping the values if x is smaller?
let [x,y] = [Math.random(), Math.random()];
console.log(x, y);
if (x < y)
[x,y] = [y,x];
console.log(x, y);

/** random integer between min (inclusive) and max (inclusive) */
const random = (min, max) =>
Math.floor(Math.random() * (max-min+1)) + min;
const generateTwoNumbers = (min, max) => {
let r1 = random(min, max-1);
let r2 = random(min, max-1);
if (r2 >= r1)
r2 += 1;
return [Math.max(r1, r2), Math.min(r1, r2)];
}
const [x, y] = generateTwoNumbers(1, 10);
console.log(`x: ${x} y: ${y}`);
First, see Generating random whole numbers in JavaScript in a specific range for how random number generation can be done.
The values r1 and r2 are guaranteed to be different random numbers in the full range. Math.max() and Math.min() ensure that the higher of the two is first, the other second.
The function will generate incorrect numbers if the range given is too small, e.g., generateTwoNumbers(1, 1) will only produce x = 2 and y = 1. Handling the case of such input is left at the discretion of the reader.

One liner.
// Create a tuple of two random numbers, then sort them.
const randomNumbers = () => [...Array(2)].map(() => Math.random()).sort((a, b) => (a > b ? -1 : 1));
// test
for (const _ of Array(50).keys()) {
const [x, y] = randomNumbers();
console.log(x > y);
}

Related

How do I make this function more precise?

I'm a beginner and I made a function to calculate the length of a semi-circular infinite snake figure. I took in two arguments; one of the radius the initial circle, and the next to be the precision (which is just the number of semi-circles).
Here's a diagram of the snake I'm talking about.
Here's what I wrote:
function snake(radius, precision) {
var pi = 3.14159265359
var exp = 1 - precision;
var sub = Math.pow(2, exp);
var product = 2 - sub;
var length = pi * radius * product
return length
}
I'm noticing that at one point the precision doesn't matter when I go really high as the value it return is the same. Is there a way to make it more precise?
You may use the constant Math.PI instead of your pi variable.
Feels like Number.toPrecision() is what you are looking for.
Below is slightly cleaned up version of your code snippet.
For the return value I'm using length.toPrecision(50):
function snake(radius, precision) {
const exp = 1 - precision;
const sub = Math.pow(2, exp);
const product = 2 - sub;
const length = Math.PI * radius * product;
return length.toPrecision(50);
}
console.log(snake(5, 55));
Yo can find out more about toPrecision() here.
The max precision value is 100.
This will be my method,
Length can be get by sum of the below series according to the screen shot,
PI X R (1 + 1/2 + 1/4 + ...)
S(n) = PIxR(1-0.5^n)/(1-0.5)
So the JavaScript function is,
function length(r, n) {
const ln = Math.PI * r * (1 - Math.pow(0.5, n))/(1 - 0.5);
return ln.toPrecision(50);
}

How to round a decimal to the nearest 0.5 (0.5, 1.5, 2.5, 3.5)

I want to round a number to the nearest 0.5. Not every factor of 0.5, just the 0.5s.
For example, 0.5, 1.5, 2.5, -1.5, -2.5. NOT 1, 1.5, 2, 2.5.
I'm confusing myself just explaining it, so here are some examples of expected outputs.
0.678 => 0.5
0.999 => 0.5
1.265 => 1.5
-2.74 => -2.5
-19.2 => -19.5
I have tried the following code with no luck,
let x = 1.296;
let y = Math.round(x);
let z = y + Math.sign(y) * .5; // 1.5 (Correct!)
let x = -2.6;
let y = Math.round(x);
let z = y + Math.sign(y) * .5; // -3.5 (WRONG, should be -2.5)
The code makes sense in my head, but dosen't work for negative numbers. What am I missing that would make this work?
First, you can round to integer by
let x = 1.296;
let y = Math.round(x);
Then, you can subtract 0.5 first, then round, then add 0.5
let x = 1.296;
let y = Math.round(x-0.5);
let z = y + 0.5;
function getValue (a){
var lowerNumber = Math.floor(a);
console.log(lowerNumber +0.5);
}
getValue(0.678);
getValue(0.999);
getValue(1.265);
getValue(-2.74);
getValue(-19.2);
looks like you want lower full number + 0.5 ;
You can try this logic:
Get the decimal part from number.
Check if value is positive or negative. Based on this initialise a factor
For positive keep it 1
For negative keep it -1
Multiply 0.5 with factor and add it to decimal
var data = [ 0.678, -0.678, 0.999, 1.265, -2.74, -19.2 ]
const output = data.map((num) => {
const decimal = parseInt(num)
const factor = num < 0 ? -1 : 1;
return decimal + (0.5 * factor)
})
console.log(output)

Improve discontinuity detection algorithm

I'm trying to create an algorithm that detects discontinuities (like vertical asymptotes) within functions between an interval for the purpose of plotting graphs without these discontinuous connecting lines. Also, I only want to evaluate within the interval so bracketing methods like bisection seems good for that.
EDIT
https://en.wikipedia.org/wiki/Classification_of_discontinuities
I realize now there are a few different kinds of discontinuities. I'm mostly interested in jump discontinuities for graphical purposes.
I'm using a bisection method as I've noticed that discontinuities occur where the slope tends to infinity or becomes vertical, so why not narrow in on those sections where the slope keeps increasing and getting steeper and steeper. The point where the slope is a vertical line, that's where the discontinuity exists.
Approach
Currently, my approach is as follows. If you subdivide the interval using a midpoint into 2 sections and compare which section has the steepest slope, then that section with the steepest slope becomes the new subinterval for the next evaluation.
Termination
This repeats until it converges by either slope becoming undefined (reaching infinity) or the left side or the right side of the interval equaling the middle (I think this is because the floating-point decimal runs out of precision and cannot divide any further)
(1.5707963267948966 + 1.5707963267948968) * .5 = 1.5707963267948966
Example
function - floor(x)
(blue = start leftX and rightX, purple = midpoint, green = 2nd iteration midpoints points, red = slope lines per iteration)
As you can see from the image, each bisection narrows into the discontinuity and the slope keeps getting steeper until it becomes a vertical line at the discontinuity point at x=1.
To my surprise this approach seems to work for step functions like floor(x) and tan(x), but it's not that great for 1/x as it takes too many iterations (I'm thinking of creating a hybrid method where I use either illinois or ridders method on the inverse of 1/x as it those tend to find the root in just one iteration).
Javascript Code
/* Math function to test on */
function fn(x) {
//return (Math.pow(Math.tan(x), 3));
return 1/x;
//return Math.floor(x);
//return x*((x-1-0.001)/(x-1));
}
function slope(x1, y1, x2, y2) {
return (y2 - y1) / (x2 - x1);
}
function findDiscontinuity(leftX, rightX, fn) {
while (true) {
let leftY = fn(leftX);
let rightY = fn(rightX);
let middleX = (leftX + rightX) / 2;
let middleY = fn(middleX);
let leftSlope = Math.abs(slope(leftX, leftY, middleX, middleY));
let rightSlope = Math.abs(slope(middleX, middleY, rightX, rightY));
if (!isFinite(leftSlope) || !isFinite(rightSlope)) return middleX;
if (middleX === leftX || middleX === rightX) return middleX;
if (leftSlope > rightSlope) {
rightX = middleX;
rightY = middleY;
} else {
leftX = middleX;
leftY = middleY;
}
}
}
Problem 1 - Improving detection
For the function x*((x-1-0.001)/(x-1)), the current algorithm has a hard time detecting the discontinuity at x=1 unless I make the interval really small. As an alternative, I could also add most subdivisions but I think the real problem is using slopes as they trick the algorithm into choosing the incorrect subinterval (as demonstrated in the image below), so this approach is not robust enough. Maybe there are some statistical methods that can help determine a more probable interval to select. Maybe something like least squares for measuring the differences and maybe applying weights or biases!
But I don't want the calculations to get too heavy and 5 points of evaluation are the max I would go with per iteration.
EDIT
After looking at problem 1 again, where it selects the wrong (left-hand side) subinterval. I noticed that the only difference between the subintervals was the green midpoint distance from their slope line. So taking inspiration from linear regression, I get the squared distance from the slope line to the midpoints [a, fa] and [b, fb] corresponding to their (left/right) subintervals. And which subinterval has the greatest change/deviation is the one chosen for further subdivision, that is, the greater of the two residuals.
This further improvement resolves problem 1. Although, it now takes around 593 iterations to find the discontinuity for 1/x. So I've created a hybrid function that uses ridders method to find the roots quicker for some functions and then fallback to this new approach. I have given up on slopes as they don't provide enough accurate information.
Problem 2 - Jump Threshold
I'm not sure how to incorporate a jump threshold and what to use for that calculation, don't think slopes would help.
Also, if the line thickness for the graph is 2px and 2 lines of a step function were on top of each other then you wouldn't be able to see the gap of 2px between those lines. So the minimum jump gap would be calculated as such
jumpThreshold = height / (ymax-ymin) = cartesian distance per pixel
minJumpGap = jumpThreshold * 2
But I don't know where to go from here! And once again, maybe there are statistical methods that can help to determine the change in function so that the algorithm can terminate quickly if there's no indication of a discontinuity.
Overall, any help or advice in improving what I got already would be much appreciated!
EDIT
As the above images explains, the more divergent the midpoints are the greater the need for more subdivisions for further inspection for that subinterval. While, if the points mostly follow a straight line trend where the midpoints barely deviate then should exit early. So now it makes sense to use the jumpThreshold in this context.
Maybe there's further analysis that could be done like measuring the curvature of the points in the interval to see whether to terminate early and further optimize this method. Zig zag points or sudden dips would be the most promising. And maybe after a certain number of intervals, keep widening the jumpThreshold as for a discontinuity you expect the residual distance to rapidly increase towards infinity!
Updated code
let ymax = 5, ymin = -5; /* just for example */
let height = 500; /* 500px screen height */
let jumpThreshold = Math.pow(.5 * (ymax - ymin) / height, 2); /* fraction(half) of a pixel! */
/* Math function to test on */
function fn(x) {
//return (Math.pow(Math.tan(x), 3));
return 1 / x;
//return Math.floor(x);
//return x * ((x - 1 - 0.001) / (x - 1));
//return x*x;
}
function findDiscontinuity(leftX, rightX, jumpThreshold, fn) {
/* try 5 interations of ridders method */
/* usually this approach can find the exact reciprocal root of a discountinuity
* in 1 iteration for functions like 1/x compared to the bisection method below */
let iterations = 5;
let root = inverseRidderMethod(leftX, rightX, iterations, fn);
let limit = fn(root);
if (Math.abs(limit) > 1e+16) {
if (root >= leftX && root <= rightX) return root;
return NaN;
}
root = discontinuityBisection(leftX, rightX, jumpThreshold, fn);
return root;
}
function discontinuityBisection(leftX, rightX, jumpThreshold, fn) {
while (true) {
let leftY = fn(leftX);
let rightY = fn(rightX);
let middleX = (leftX + rightX) * .5;
let middleY = fn(middleX);
let a = (leftX + middleX) * .5;
let fa = fn(a);
let b = (middleX + rightX) * .5;
let fb = fn(b);
let leftResidual = Math.pow(fa - (leftY + middleY) * .5, 2);
let rightResidual = Math.pow(fb - (middleY + rightY) * .5, 2);
/* if both subinterval midpoints (fa,fb) barely deviate from their slope lines
* i.e. they're under the jumpThreshold, then return NaN,
* indicating no discountinuity with the current threshold,
* both subintervals are mostly straight */
if (leftResidual < jumpThreshold && rightResidual < jumpThreshold) return NaN;
if (!isFinite(fa) || a === leftX || a === middleX) return a;
if (!isFinite(fb) || b === middleX || b === rightX) return b;
if (leftResidual > rightResidual) {
/* left hand-side subinterval */
rightX = middleX;
middleX = a;
} else {
/* right hand-side subinterval */
leftX = middleX;
middleX = b;
}
}
}
function inverseRidderMethod(min, max, iterations, fn) {
/* Modified version of RiddersSolver from Apache Commons Math
* http://commons.apache.org/
* https://www.apache.org/licenses/LICENSE-2.0.txt
*/
let x1 = min;
let y1 = 1 / fn(x1);
let x2 = max;
let y2 = 1 / fn(x2);
// check for zeros before verifying bracketing
if (y1 == 0) {
return min;
}
if (y2 == 0) {
return max;
}
let functionValueAccuracy = 1e-55;
let relativeAccuracy = 1e-16;
let oldx = Number.POSITIVE_INFINITY;
let i = 0;
while (i < iterations) {
// calculate the new root approximation
let x3 = 0.5 * (x1 + x2);
let y3 = 1 / fn(x3);
if (!isFinite(y3)) return NaN;
if (Math.abs(y3) <= functionValueAccuracy) {
return x3;
}
let delta = 1 - (y1 * y2) / (y3 * y3); // delta > 1 due to bracketing
let correction = (signum(y2) * signum(y3)) * (x3 - x1) / Math.sqrt(delta);
let x = x3 - correction; // correction != 0
if (!isFinite(x)) return NaN;
let y = 1 / fn(x);
// check for convergence
let tolerance = Math.max(relativeAccuracy * Math.abs(x), 1e-16);
if (Math.abs(x - oldx) <= tolerance) {
return x;
}
if (Math.abs(y) <= functionValueAccuracy) {
return x;
}
// prepare the new interval for the next iteration
// Ridders' method guarantees x1 < x < x2
if (correction > 0.0) { // x1 < x < x3
if (signum(y1) + signum(y) == 0.0) {
x2 = x;
y2 = y;
} else {
x1 = x;
x2 = x3;
y1 = y;
y2 = y3;
}
} else { // x3 < x < x2
if (signum(y2) + signum(y) == 0.0) {
x1 = x;
y1 = y;
} else {
x1 = x3;
x2 = x;
y1 = y3;
y2 = y;
}
}
oldx = x;
}
}
function signum(a) {
return (a < 0.0) ? -1.0 : ((a > 0.0) ? 1.0 : a);
}
/* TEST */
console.log(findDiscontinuity(.5, .6, jumpThreshold, fn));
Python Code
I don't mind if the solution is provided in Javascript or Python
import math
def fn(x):
try:
# return (math.pow(math.tan(x), 3))
# return 1 / x
# return math.floor(x)
return x * ((x - 1 - 0.001) / (x - 1))
except ZeroDivisionError:
return float('Inf')
def slope(x1, y1, x2, y2):
try:
return (y2 - y1) / (x2 - x1)
except ZeroDivisionError:
return float('Inf')
def find_discontinuity(leftX, rightX, fn):
while True:
leftY = fn(leftX)
rightY = fn(rightX)
middleX = (leftX + rightX) / 2
middleY = fn(middleX)
leftSlope = abs(slope(leftX, leftY, middleX, middleY))
rightSlope = abs(slope(middleX, middleY, rightX, rightY))
if not math.isfinite(leftSlope) or not math.isfinite(rightSlope):
return middleX
if middleX == leftX or middleX == rightX:
return middleX
if leftSlope > rightSlope:
rightX = middleX
rightY = middleY
else:
leftX = middleX
leftY = middleY

Procedurally Generated Heart In JavaScript

slowly learning JavaScript on the side and wanted to try and animate this with Three.JS:
https://www.reddit.com/r/gifs/comments/ag6or3/send_this_to_your_loved_ones_for_valentines/
I was trying to re-create that equation but ran into a wall in that the code below is not producing the right result. I had read that JS has some big issues with floating point numbers and in particular cubed roots don't really work all that well.
for (var x = -100; x < 100; x++)
{
y = Math.pow(x, 2/3) + 0.9 * (Math.pow(3.0 - (x*x), 0.5)) * Math.sin(10 *
Math.PI * x)
}
Does that look right to you JS masters?
Here is my code implementation in trying to get this to work including the fix mentioned below.
https://codesandbox.io/s/vjm4xox185
Look at the range of the graph you linked to. The heart is being drawn in the range of x: [-2, 2] , but your loop is from x: [-100, 100]. This means you'll probably get undefined results for all x values except -1, 0, 1. Try narrowing down the range of your for() loop, and you should get the desired result.
The problem is that the result of the calculation of (Math.pow(3.0 - (x*x), 0.5)) return NAN as if not realistic number
Read here for more information about Math.pow(negativeNumber, 0.5)
so i added validPow that will validate the x is positive or negative and return the right result.
for (var x = -100; x < 100; x++)
{
y = (Math.pow(x, 2/3) + 0.9) * (validPow(3.0 - (x*x), 0.5)) *
Math.sin(10 * Math.PI * x)
console.log(y)
}
function validPow(x, y)
{
var result = Math.pow(x, y);
if (x > 0)
{
return result;
}
else
{
return -1 * Math.pow(-x, y);
}
}
Finally solved this.
It came down to this line with the key being to use Math.abs(x) inside the first Math.pow statement:
var y = Math.pow(Math.abs(x), 0.66) + (0.9 * Math.sqrt(3.3 - x * x)) * Math.sin(10 * Math.PI * x);
Thanks for everyone who provided input and help!
You can view the final result here:
https://codesandbox.io/s/vjm4xox185

Weighted Random Number Generator in Javascript

I am using the following code to generate a random number:
function getRandomInt (min, max) {
return Math.floor((Math.random() * (max - min + 1)) + min;
}
What I want to do is add a weighting that favours the numbers at the lower end of the range.
I thought about maybe trying to multiply the numbers by 1/cosine.
Would this work and does anyone know how I might go about it?
Many thanks!
First Solution
You need a function which contains the points (0, 0) and (1, 1). For instance: x^n when n > 0
Math.pow(1, n) === 1
And
Math.pow(0, n) === 0
Therefore, you would just change n depending on how you want the weighting to work.
When n = 1 : y === x
When n > 1 : y <= x
When 0 < n < 1 : y >= x
So, if you want lower values to be favored over higher values, simply use n > 1.
var weighted = Math.pow(Math.random(), 2);
Then you can scale the result as usual.
var scaled = Math.floor(weighted * (max - min + 1)) + min;
Other Functions
Likewise, you could use any continuous function which contains the points (0, 0), (1, 1), and has range and domain of [0, 1].
Sine
y = sin(xπ/2)
Cosine
y = 1 - cos(xπ/2)
EDIT: there was a type in the final formula, log(2+log(x)) is incorrect it should have been log(1+log(x))+1, its fixed now.
If you are using logarithmic weighting, using something like
var x = Math.random();
var weighted = x * Math.log(1+x);
would make 0.5 weigh in at around 0.2, but 1 would only weigh in at around 0.69.
Using this
var x = Math.random();
var weighted = x * Math.log(2 + Math.log(x));
would allow 1 to weigh in at 1. So combine them, and this
var x = Math.random();
var weighted = (x <= 0.5) ? x * Math.log(1 + x) : x * Math.log(1 + Math.log(x))+1;
should do the trick

Categories

Resources