如何基于概率生成符合幂律分布的大范围随机数?以Stake Limbo为例
Hey there! Awesome question about how Stake's Limbo game nails that tight alignment between multipliers and their corresponding probabilities. Let's break this down clearly, since it's a great example of turning uniform randomness into the power-law distribution that makes Limbo tick.
Core Concept: Inverse Transform Sampling
The key here is that Limbo's multiplier distribution follows a power-law decay—higher multipliers have exponentially lower probabilities. To get from a uniform random number (like Math.random() outputs) to this distribution, we use a standard stats technique called inverse transform sampling.
First, let's formalize Limbo's probability rule (with its 1% house edge):
- For any multiplier
x, the probability that the game's result exceedsxisP = 0.99 / x(as a decimal).
Let's confirm with your examples:
- For
x=2:0.99/2 = 0.495→ 49.5% chance of exceeding x=2, which matches your observation. - For
x=1,000,000:0.99/1e6 = 0.000099→ 0.00099% chance of hitting that multiplier, exactly as you noted.
The cumulative distribution function (CDF) for "result ≤ x" is 1 - 0.99/x. To generate a random sample from this distribution, we apply the inverse of this CDF to a uniform random number.
Step-by-Step Implementation Code
Here's how this translates to practical code (JavaScript, since web-based games use this):
// Account for Stake's 1% house edge const HOUSE_EDGE_FACTOR = 0.99; function generateLimboMultiplier() { // Get a uniform random number between 0 (inclusive) and 1 (exclusive) const uniformRandom = Math.random(); // Apply inverse CDF to convert uniform randomness to power-law distribution let multiplier = HOUSE_EDGE_FACTOR / (1 - uniformRandom); // In practice, Limbo caps the maximum multiplier at 1,000,000 multiplier = Math.min(multiplier, 1000000); // Round to 9 decimal places for consistent display return parseFloat(multiplier.toFixed(9)); }
Quick Probability Check
Let's verify the x=2 case:
- The probability of getting a multiplier >2 is the chance that
uniformRandom > 1 - 0.99/2 = 0.505. - The range from 0.505 to 1 is 49.5% of the total 0-1 interval, which is exactly the probability we need.
Why Math.floor(Math.random()*1000000) Doesn't Work
You're spot-on that scaling Math.random() to integers gives a uniform distribution—every integer from 0 to 999999 has equal odds. Limbo needs the opposite: small multipliers should be common, and huge ones should be extremely rare. The inverse transform sampling we used converts that flat uniform distribution into the skewed power-law shape Limbo relies on.
Bonus: Provably Fair System
Stake doesn't just use Math.random()—they use a provably fair system to let players verify game outcomes aren't rigged. This uses a combination of server seed, client seed, and nonce to generate cryptographically secure uniform random numbers. But the core transformation from uniform to power-law distribution is exactly what we walked through above.
Resources to Learn More
If you want to dive deeper:
- Inverse Transform Sampling: This is a foundational technique in simulation and Monte Carlo methods. Intro stats textbooks or online courses on probability will cover it in detail.
- Power-Law Distributions: Look for resources on heavy-tailed distributions, since Limbo's multiplier curve falls into this category.
- Provably Fair Gaming: Stake's internal documentation explains how their verifiable randomness system works (focus on how seeds are combined to generate the uniform random input for the multiplier formula).
内容的提问来源于stack exchange,提问作者rt10

