Xorshift128+ 與 PRNG 是什麼?偽隨機數產生演算法完整介紹

1. 什麼是 PRNG(Pseudo-Random Number Generator)?

PRNG(偽隨機數產生器)

是一種利用演算法產生「看起來隨機」數列的方法。

它產生的數值並不是真正的隨機,

而是:

完全由演算法決定(Deterministic)。

只要使用相同的 Seed(種子),

就一定會得到完全相同的數列。

因此,

PRNG 十分適合:

  • 模擬;
  • 遊戲;
  • 測試;
  • 科學計算;
  • 程式設計中的一般亂數需求。

為什麼稱為「偽」隨機?

因為電腦本身是一部:

確定性機器(Deterministic Machine)。

CPU 的每一個位元,

都有明確的計算來源。

真正完全不可預測的亂數,

並不存在於一般 CPU 運算之中。

PRNG

只是透過數學運算,

產生一串:

統計上近似隨機的數列。


PRNG 的主要特性

  • Deterministic:相同 Seed 一定得到相同結果
  • Uniform Distribution:各個值出現機率大致相同
  • Period(週期):經過一定次數後數列開始重複
  • High Performance:速度極快,每次產生通常只需數奈秒

以 Xorshift128+ 為例,

其週期為:

2¹²⁸ − 1

約等於:

3.4 × 10³⁸

對一般用途而言幾乎不可能用盡。


2. Xorshift128+ 演算法

Xorshift128+

Sebastiano Vigna 於 2014 年提出,

是 George Marsaglia 所提出 Xorshift 系列的改良版本。

目前,

Chrome(V8)、

Firefox(SpiderMonkey)、

Safari(JavaScriptCore)

都曾採用或採用過這類演算法,

作為:

Math.random()

背後的 PRNG。


名稱由來

  • Xor:XOR 位元運算
  • Shift:位元位移
  • 128:128-bit 內部狀態
  • +:輸出時加入加法混合

整個演算法,

幾乎只使用:

  • XOR
  • Left Shift
  • Right Shift
  • Addition

四種極快的 CPU 指令。


3. 演算法流程

內部狀態包含:

兩個 64-bit 整數。

總共:

128 bit。

流程如下:

x = s0
y = s1

s0 = y

x ^= x << 23
x ^= x >> 17
x ^= y >> 26

s1 = x

return x + y

雖然只有少量位元運算,

卻能產生品質相當高的偽隨機數列。


4. C 語言實作

#include <stdint.h>

static uint64_t s[2];

uint64_t xorshift128plus {
    uint64_t x = s[0];
    uint64_t const y = s[1];

    s[0] = y;

    x ^= x << 23;
    s[1] = x ^ y ^ (x >> 17) ^ (y >> 26);

    return s[1] + y;
}

5. JavaScript 範例

let s0 = 1n;
let s1 = 2n;

const MASK64 = (1n << 64n) - 1n;

function xorshift128plus() {
    let x = s0;
    const y = s1;

    s0 = y;

    x ^= (x << 23n) & MASK64;
    x ^= x >> 17n;
    x ^= y >> 26n;

    s1 = x;

    return Number(((x + y) & MASK64) >> 12n) / (2 ** 52);
}

此程式僅用於說明演算法。

實際瀏覽器中的

Math.random()

通常由 C++ 原生程式實作。


6. Python 範例

MASK64 = (1 << 64) - 1

def xorshift128plus:
    x = state[0]
    y = state[1]

    state[0] = y

    x ^= (x << 23) & MASK64
    x ^= x >> 17
    x ^= y >> 26

    state[1] = x

    return (x + y) & MASK64


state = [12345, 67890]

for _ in range(10):
    print

7. 為什麼瀏覽器採用 Xorshift128+?

在較早版本中,

Chrome 使用的是:

MWC(Multiply-with-Carry)。

其統計品質較差,

無法通過部分大型隨機測試。

之後,

主要 JavaScript Engine 改採:

Xorshift128+,

原因包括:

  1. 速度極快

僅需:

  • 3 次 XOR
  • 2 次 Shift
  • 1 次 Addition

即可完成一次亂數生成。


  1. 統計品質優秀

能通過:

  • BigCrush
  • PractRand

等大型隨機測試。


  1. 狀態非常小

只需要:

128 bit

(16 Bytes)。

十分適合 CPU Cache。


  1. 週期極長

2¹²⁸ − 1

足以滿足幾乎所有:

非密碼學用途。


8. 常見 PRNG 比較

| 演算法 | 特點 | |---------|------| | Xorshift128+ | 極快、128-bit 狀態、統計品質佳 | | Mersenne Twister | 週期極長(2¹⁹⁹³⁷−1),Python random 使用 | | PCG | 狀態小,統計品質極佳 | | SplitMix64 | 常用於產生 Seed | | LCG | 最古老,品質較差,C rand() 曾廣泛使用 |


9. PRNG 不適合哪些用途?

雖然 PRNG 很快,

但:

不能用於安全用途。

包括:

  • 密碼學;
  • Session ID;
  • API Token;
  • JWT Secret;
  • 一次性驗證碼;
  • 線上博彩;
  • 抽獎系統。

原因是:

只要取得足夠輸出,

攻擊者便可能:

推算內部狀態,

預測之後所有亂數。

因此,

安全需求必須改用:

CSPRNG(Cryptographically Secure PRNG)。


10. 為什麼不能用 Math.random() 產生 Token?

例如:

const token = Math.random().toString(36).slice(2);

這種做法:

並不安全。

原因是:

Math.random()

通常建立在:

一般 PRNG,

而不是:

密碼學安全亂數。

正確方式應使用:

const bytes = new Uint8Array(32);

crypto.getRandomValues;

const token = Array.from
    .mapb =&gt; b.toString(16).padStart(2, &quot;0&quot;)
    .join("");

其中:

crypto.getRandomValues()

使用的是:

瀏覽器提供的

CSPRNG

適合:

  • Token
  • Session ID
  • Password
  • Key
  • Salt
  • Security Nonce

等安全用途。


結論

Xorshift128+

是一種經典且高效率的

偽隨機數產生演算法(PRNG)。

它具有:

  • 極高速度;
  • 很長的週期;
  • 良好的統計品質;
  • 很小的記憶體需求。

因此,

非常適合:

  • 遊戲;
  • 模擬;
  • 科學計算;
  • 一般程式中的亂數需求。

但它仍然屬於:

Deterministic PRNG。

對於任何涉及:

密碼學或安全性的場景,

都不能使用 Xorshift128+ 或 Math.random()

而必須改用:

CSPRNG

例如:

crypto.getRandomValues()

os.urandom()

或其他作業系統提供的安全亂數來源。