Make a Psudo Random Number Generator Based on Many Inputs Javascript - javascript

The current project that I am working on involves a multidimensional world, which can have many more than just 3 dimensions, and need to get values for each position of that world. I already have a good Pseudo Random Number Generator (PRNG) that takes a seed and a single value. What I need is to have a function that can use as many inputs as are provided, and return a value based on those positions. The PRNG also should not have to rely on its previous values to determine it's next state, and should work (as close as possible to) the same on any browser or system.
My current PRNG, works very well for 1 input (xxHash):
function random(seed, x) {
/* mix around the bits in x: */
x = x * 3266489917 + 374761393;
x = (x << 17) | (x >> 15);
/* mix around the bits in y and mix those into x: */
x += seed * 3266489917;
/* Give x a good stir: */
x *= 668265263;
x ^= x >> 15;
x *= 2246822519;
x ^= x >> 13;
x *= 3266489917;
x ^= x >> 16;
/* trim the result and scale it to a float in [0,1): */
return (x & 0x00ffffff) * (1 / 0x1000000);
}
I tried adding more parameters and mixing them up, but that didn't go so well (Below):
function rand(seed, ...prams){
let x = prams[0] + seed;
x = x * 3266489917 + 374761393;
x = (x << 17) | (x >> 15);
/* mix around the bits in y and mix those into x: */
for(let i =1; i< prams.length; i++){
prams[i] *= seed;
x *= prams[i] * 3266489917
}
/* Give x a good stir: */
x *= 668265263;
x ^= x >> 15;
x *= 2246822519;
x ^= x >> 13;
x *= 3266489917;
x ^= x >> 16;
/* trim the result and scale it to a float in [0,1): */
let val = ((x & 0x00ffffff) * (1.0 / 0x1000000))
return val;
}
This one didn't return any errors, but if the inputs were in a different order, the value was the same, which means that rand(seed, 5, 1, 1) === rand(seed, 1, 5, 1) === rand(seed, 1, 1, 5) , Which is not great behavior.
I need a function random(seed, ...position) that will generate a pseudo random number between 0 and 1 that is affected by both the order and all the values in the position array.

Related

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

Getting X and Y coordinates from tile ID

I'm stumped on what is probably some pretty simple math. I need to get the X and Y coordinates from each tiles referenced ID. The grid below shows the order the ids are generated in. Each tile has a width and height of 32. Number ones x & y would be equal to (0,0). This is for a game I'm starting to make with canvas using a tileset.
1|2|3
4|5|6
7|8|9
So far for X, I've come up with...
(n % 3) * 32 - 32 // 3 is the width of the source image divded by 32
And for Y...
(n / 3) * 32
This is obviously wrong, but It's the closest I've come, and I don't think I'm too far off from the actual formula.
Here is my actual code so far:
function startGame() {
const canvas = document.getElementById("rpg");
const ctx = canvas.getContext("2d");
const tileSet = new Image();
tileSet.src = "dungeon_tiles.png";
let map = {
cols: 10,
rows: 10,
tsize: 32,
getTileX: function(counter, tiles) {
return ((tiles[counter] - 1) % 64) * 32;
},
getTileY: function(counter, tiles) {
return ((tiles[counter] - 1) / 64) * 32;
}
};
let counter = 0;
tileSet.onload = function() {
for (let c = 0; c < map.cols; c++) {
for (let r = 0; r < map.rows; r++) {
let x = map.getTileX(counter, mapObj.layers[0].data); // mapObj.layers[0].data is the array of values
let y = map.getTileY(counter, mapObj.layers[0].data);
counter += 1;
ctx.drawImage(
tileSet, // image
x, // source x
y, // source y
map.tsize, // source width
map.tsize, // source height
r * map.tsize, // target x
c * map.tsize, // target y
map.tsize, // target width
map.tsize // target height
);
}
}
};
}
If 1 is (0,0) and each tile is 32*32, then finding your horizontal position is a simple 32*(t-1) where t is your tile number. t-1 because your tiles start from 1 instead of 0. Now, you have 3 tiles per row so you want to reset every 3, so the final formula for your x is 32*((t-1)%3).
For the vertical position it's almost the same, but you want to increase your position by 32 only once every 3 tiles, so this is your y: 32*floor((t-1)/3).
floor((t-1)/3) is simply integer division since the numbers are always positive.
If I understand this correctly, you want to get the 1|2|3 values based on x, y correct? You can do something like this:
((y * total # of rows) + x) + 1
This would convert the 2D x, y index to a single index which is, as you stated, 1|2|3. This formula is based on your example where count starts at 1 and not 0. If you want to convert it to 0 base, just remove the + 1.
If you have the width and height, or probably location of input/character, you can have a GetX(int posX) and GetY(int posY) to get the x and y based on the position. Once you have converted the position to x, y values, use the formula above.
int GetX(int posX)
{
return (posX / 32);
}
int GetY(int posY)
{
return (posY / 32);
}
int GetIndex(int posX, int posY)
{
return ((GetY(posY) / totalRows) + GetX(posX)) + 1;
}

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

Exponential Formula for negative direction?

Ive got a bit stuck figuring it out for the negative direction? it must be really simple, but just cant seem to get it!
x = current x position
dir = direction of motion on x axis
if (tween == 'linear'){
if (dir == 1) {
x += (x / 5);
}
else if (dir == -1){
//what here??
}
}
What's missing here is that you need to consider deviations from the starting point, not x=0 (and also consider the sign of the direction as well, which others are stating correctly). That is, if your starting point is x0, your equation should be more like:
x += (x-x0)/5
Here's the figure for motion in the positive and negative directions (note that position is on the vertical axis and time on the horizontal)
And here's the Python code. (Note that I've added in a dt term, since it's too weird to do dynamic simulation without an explicit time.)
from pylab import *
x0, b, dt = 11.5, 5, .1
xmotion, times = [], []
for direction in (+1, -1):
x, t = x0+direction*dt/b, 0 # give system an initial kick in the direction it should move
for i in range(360):
x += dt*(x-x0)/b
t += dt
xmotion.append(x)
times.append(t)
plot(times, xmotion, '.')
xlabel('time (seconds)')
ylabel('x-position')
show()
x += (abs(x) / 5) * dir;
If you do something like x -= (x/5), it's going to be impossible to cross x = 0 - as x gets close to 0, it starts changing less and less. Try using a minimum increment
v = abs(x) / 5;
x += ((v > MINVEL) ? v : MINVEL) * dir;
if (tween == 'linear') {
x += (x / 5) * dir;
}
In the end I added a frame counter (t) and went with:
x = -(change*dir) * (t /= 10) * (t - 2) + x;
from my fav as3 tweener lib:
http://code.google.com/p/tweener/source/browse/trunk/as3/caurina/transitions/Equations.as

Categories

Resources