如何在不使用Math.random的情况下编写JS随机函数并优化随机数生成
Hey there! Let's break down your two requests and fix things step by step.
1. Building a Random 0/1 Generator Without Math.random
First, let's replace your current get_zero_or_one function (which relies on Math.random) with a couple of reliable alternatives:
Option A: Web Crypto API (Recommended)
This is the most secure and unbiased method for generating random bits in browsers or Node.js (v14+). It uses system-level entropy sources, so it's way more robust than timing-based hacks:
const get_zero_or_one = () => { const byteArray = new Uint8Array(1); // Fetch a cryptographically secure random byte crypto.getRandomValues(byteArray); // Return 0 or 1 using the least significant bit return byteArray[0] % 2; };
Option B: Timing-Based Fallback (For Environments Without Crypto)
If you're working in an environment where the Web Crypto API isn't available (like some older runtimes), you can use high-resolution timestamps to get weak entropy. Note: This isn't secure for sensitive use cases (like cryptography), but it works for basic randomness:
const get_zero_or_one = () => { // Use high-resolution time to get a constantly changing value const highResTime = performance.now(); // Extract a fluctuating bit from the fractional part of the timestamp return Math.floor(highResTime * 1000000) % 2; };
2. Fixing the Bias in Your Random Number Generator
The issue with your current RandomResult function is that it produces a binomial distribution, not a uniform one. Adding max_number-1 random 0s and 1s means middle values (like (max_number-1)/2) are way more likely than extreme values (0 or max_number-1), which is why the error grows as max_number increases.
To fix this, we need to generate a uniform random number within [0, max_number-1] using our 0/1 generator properly. Here's how:
const getUniformRandom = (max_number) => { // Edge case: if max is 0 or 1, only possible result is 0 if (max_number <= 1) return 0; // Calculate how many bits we need to cover the range up to max_number-1 let bitLength = 0; let temp = max_number - 1; while (temp > 0) { bitLength++; temp = temp >> 1; // Shift right to count bits } let result; do { result = 0; // Build a number bit by bit using our 0/1 generator for (let i = 0; i < bitLength; i++) { result = (result << 1) | get_zero_or_one(); } // Reject numbers that are outside the unbiased range to avoid modulo bias } while (result >= max_number); return result; };
Why This Works:
- We generate a number in a range that's a power of two (e.g., if
max_numberis 5, we generate 3-bit numbers from 0 to 7). - We discard any number that's >=
max_number(in the 5 example, we discard 5,6,7) and try again. - This ensures every valid number (0-4 in the example) has an equal chance of being selected, eliminating the bias from your original approach.
Test this with large max_number values—you'll see the distribution is uniform, and the "error" (bias) disappears completely.
内容的提问来源于stack exchange,提问作者Geon

