-
-
Save ashnur/4827292c701ac899fea2 to your computer and use it in GitHub Desktop.
Demonstrates how to replicate a stream of Math.random() given two past observations for **Chrome**. Code was only tried on Chrome 43 (07/2015). Answers the question from this issue: https://github.com/fta2012/ReplicatedRandom/issues/2.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var rngstate; | |
function MathRandom() { | |
// Our own implementation of Math.random(). | |
// Source code was copied from https://code.google.com/p/chromium/codesearch#chromium/src/v8/src/math.js&q=MathRandom&sq=package:chromium&type=cs&l=130 | |
// You need to solve for the rngstate first before this can be used. | |
console.assert(rngstate, "You need to set the global variable `rngstate` first. For example: `rngstate = solve(Math.random(), Math.random());`"); | |
if (!rngstate) return; | |
var r0 = (Math.imul(18030, rngstate[0] & 0xFFFF) + (rngstate[0] >>> 16)) | 0; | |
rngstate[0] = r0; | |
var r1 = (Math.imul(36969, rngstate[1] & 0xFFFF) + (rngstate[1] >>> 16)) | 0; | |
rngstate[1] = r1; | |
var x = ((r0 << 16) + (r1 & 0xFFFF)) | 0; | |
// Division by 0x100000000 through multiplication by reciprocal. | |
return (x < 0 ? (x + 0x100000000) : x) * 2.3283064365386962890625e-10; | |
} | |
function solve(first, second, gap) { | |
// Given two values of Math.random() and number of other calls to Math.random() in between, solve for the rngstate. | |
var first_x = first / 2.3283064365386962890625e-10; | |
var first_r0_lower = first_x >>> 16; | |
var first_r1_lower = first_x & 0xFFFF; | |
var second_x = second / 2.3283064365386962890625e-10; | |
var second_r0_lower = second_x >>> 16; | |
var second_r1_lower = second_x & 0xFFFF; | |
var first_r0_upper, first_r1_upper, second_r0, second_r1; | |
var found = false; | |
for (first_r0_upper = 0; first_r0_upper <= 0xFFFF; first_r0_upper++) { | |
second_r0 = (first_r0_upper << 16) | first_r0_lower; | |
for (var i = 0; i <= gap; i++) { | |
second_r0 = (Math.imul(18030, second_r0 & 0xFFFF) + (second_r0 >>> 16)) | 0; | |
} | |
if ((second_r0 & 0xFFFF) == second_r0_lower) { | |
found = true; | |
break; | |
} | |
} | |
if (!found) { | |
return null; | |
} | |
found = false; | |
for (first_r1_upper = 0; first_r1_upper <= 0xFFFF; first_r1_upper++) { | |
second_r1 = (first_r1_upper << 16) | first_r1_lower; | |
for (var i = 0; i <= gap; i++) { | |
second_r1 = (Math.imul(36969, second_r1 & 0xFFFF) + (second_r1 >>> 16)) | 0; | |
} | |
if ((second_r1 & 0xFFFF) == second_r1_lower) { | |
found = true; | |
break; | |
} | |
} | |
if (!found) { | |
return null; | |
} | |
return [second_r0, second_r1]; | |
} | |
// Example usage | |
var num_unknown_values = 0; // Change this to the number of calls to Math.random() between our two observations | |
var observation1 = Math.random(); | |
for (var i = 0; i < num_unknown_values; i++) { | |
Math.random(); | |
} | |
var observation2 = Math.random(); | |
rngstate = solve(observation1, observation2, num_unknown_values); | |
// Test | |
for (var i = 0; i < 10; i++) { | |
var predicted = MathRandom(); | |
var observation = Math.random(); | |
console.assert(observation == predicted); | |
console.log(observation, predicted); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment