📖 第3课:规则30与随机性

规则30 伪随机数 密码学 PRNG硬件

🎯 本课目标

深入分析规则30的混沌行为,理解确定性系统如何产生伪随机序列,并实现一个基于规则30的硬件伪随机数生成器(PRNG)。

🌀 规则30的神奇特性

规则30(LUT = 00011110)是Wolfram在1984年发现的"最简单的混沌系统"。它的行为令人震惊:

规则30的转移函数

f(L, C, R) = L XOR (C OR R)

验证:000→0, 001→1, 010→1, 011→1, 100→1, 101→0, 110→0, 111→0 ✓

等价布尔式:f = L ⊕ (C ∨ R)

规则30的左右不对称性

观察规则30的演化图案,你会发现:

信息传播方向分析

对于规则30:f(L,C,R) = L ⊕ (C ∨ R)

右侧信号传播速度:v_R = 1(每步向右移动1格)

左侧信号传播速度:v_L = -1,但被规则"吸收"

这解释了为什么左半边呈现三角形——左侧信息被限制在光锥内

🎲 规则30作为PRNG

Wolfram在1985年提出用规则30的中心列作为伪随机数序列。具体方法:

提取方法:在每一步演化后,取中心元胞的值作为随机位

序列:r_0, r_1, r_2, ...,其中 r_t = CA_{N/2}(t)

这是一个确定性过程,但输出序列在统计上与真随机无法区分

测试结果说明
频率测试✅ 通过0和1的比例接近1:1
游程测试✅ 通过连续0/1的长度分布正确
自相关测试✅ 通过序列与自身移位版本不相关
Diehard测试套件✅ 通过全面的随机性测试
线性复杂度✅ 高不可用短LFSR近似
⚠️ 密码学安全性:规则30 PRNG的统计随机性很好,但其密码学安全性尚未被充分证明。对于安全关键应用,仍建议使用经过密码学审查的CSPRNG(如AES-CTR、ChaCha20)。规则30 PRNG更适合仿真和测试场景。

⚡ 规则30 PRNG的Verilog实现

规则30伪随机数生成器

// ============================================================================
// prng_rule30.v - 基于规则30的硬件伪随机数生成器
// 从中心列提取随机位,支持多位并行输出
// ============================================================================
module prng_rule30 #(
    parameter WIDTH     = 64,      // CA网格宽度(建议≥32)
    parameter OUT_WIDTH = 32       // 随机数输出位宽
)(
    input  wire                  clk,
    input  wire                  rst_n,
    input  wire                  enable,      // 使能
    input  wire                  init,        // 初始化
    input  wire [WIDTH-1:0]      seed,        // 初始种子
    output wire [OUT_WIDTH-1:0]  random_out,  // 随机数输出
    output wire                  valid,       // 输出有效
    output wire [WIDTH-1:0]      state_debug  // 调试:当前CA状态
);

    // ---- CA状态寄存器 ----
    reg [WIDTH-1:0] curr;
    reg [WIDTH-1:0] nxt;

    // ---- 规则30: f(L,C,R) = L XOR (C OR R) ----
    // 优化实现:无需8:1 MUX,直接用布尔逻辑
    wire left_ext  = curr[WIDTH-1]; // 环形:左边界的左邻居
    wire right_ext = curr[0];       // 环形:右边界的右邻居

    genvar i;
    generate
        for (i = 0; i < WIDTH; i = i + 1) begin : gen_rule30
            wire L = (i == 0)        ? left_ext  : curr[i-1];
            wire C = curr[i];
            wire R = (i == WIDTH-1)  ? right_ext : curr[i+1];
            assign nxt[i] = L ^ (C | R);
        end
    endgenerate

    // ---- 状态更新 ----
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n)
            curr <= {WIDTH{1'b0}};
        else if (init)
            curr <= seed;
        else if (enable)
            curr <= nxt;
    end

    // ---- 随机位提取 ----
    // 方法1:取中心列连续OUT_WIDTH步的值(需要OUT_WIDTH步才能输出一个数)
    // 方法2:从当前状态的多列并行提取(1步即可输出)

    // 这里采用方法2:从网格中均匀采样OUT_WIDTH个位
    // 采样间隔 = WIDTH / OUT_WIDTH
    localparam SAMPLE_GAP = WIDTH / OUT_WIDTH;

    genvar j;
    generate
        for (j = 0; j < OUT_WIDTH; j = j + 1) begin : gen_sample
            // 从右半部分采样(右半部分更混沌)
            localparam IDX = WIDTH/2 + (j * SAMPLE_GAP);
            // 确保索引在范围内
            if (IDX < WIDTH)
                assign random_out[j] = curr[IDX];
            else
                assign random_out[j] = curr[WIDTH-1];
        end
    endgenerate

    assign valid       = enable;
    assign state_debug = curr;

endmodule

多位并行输出PRNG

对于需要高速随机数的应用(如蒙特卡洛仿真),我们可以利用规则30的列独立性,从不同列同时提取随机位:

高速并行PRNG

// ============================================================================
// prng_rule30_parallel.v - 高速并行规则30 PRNG
// 每时钟周期输出一个完整的随机数
// 使用多个CA列同时提取,通过列间延迟保证独立性
// ============================================================================
module prng_rule30_parallel #(
    parameter WIDTH = 128,           // 加宽CA以提供更多独立列
    parameter OUT_W = 32             // 输出位宽
)(
    input  wire              clk,
    input  wire              rst_n,
    input  wire              reseed,        // 重新播种
    input  wire [WIDTH-1:0]  new_seed,
    output wire [OUT_W-1:0]  rand_data,     // 随机数据
    output wire              rand_valid     // 数据有效
);

    // ---- CA阵列 ----
    reg [WIDTH-1:0] ca_state;
    wire [WIDTH-1:0] ca_next;

    // 规则30组合逻辑
    genvar i;
    generate
        for (i = 0; i < WIDTH; i = i + 1) begin : gen_ca
            wire L = (i == 0)        ? ca_state[WIDTH-1] : ca_state[i-1];
            wire C = ca_state[i];
            wire R = (i == WIDTH-1)  ? ca_state[0]       : ca_state[i+1];
            assign ca_next[i] = L ^ (C | R);
        end
    endgenerate

    // ---- 采样逻辑 ----
    // 从CA状态的右半部分均匀采样OUT_W个位
    // 右半部分混沌度更高,随机性更好
    localparam HALF = WIDTH / 2;
    localparam GAP  = HALF / OUT_W;

    genvar k;
    generate
        for (k = 0; k < OUT_W; k = k + 1) begin : gen_out
            assign rand_data[k] = ca_state[HALF + k * GAP];
        end
    endgenerate

    // ---- 状态更新 ----
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n)
            ca_state <= {WIDTH{1'b0}};
        else if (reseed)
            ca_state <= new_seed;
        else
            ca_state <= ca_next;  // 每周期自动步进
    end

    assign rand_valid = ~reseed;  // 非重新播种时输出有效

endmodule

📐 规则30的数学分析

李雅普诺夫指数与敏感性

李雅普诺夫指数衡量系统对初始条件的敏感性:

λ = lim(t→∞) (1/t) × Σ ln|δx(t)/δx(0)|

规则30的右半部分:λ > 0(混沌,初始条件敏感)

规则30的左半部分:λ ≈ 0(规则结构,不敏感)

正的李雅普诺夫指数是混沌的标志——微小的初始扰动被指数放大

周期长度分析

对于有限宽度W的环形CA,状态空间大小为 2^W

因此最大周期 P ≤ 2^W

实际观测:

周期随W指数增长,对于WIDTH≥64,周期远超实际应用需求

与LFSR的比较

特性规则30 PRNGLFSR
线性度非线性线性
硬件开销中等(XOR+OR per bit)极低(XOR+移位)
随机性质量极高中等(通过多项式选择改善)
可预测性难以从输出推断状态2n个输出位即可完全推断
周期约2^(W/2)精确2^n-1(最长相移位)
吞吐率1 bit/cycle(单列)1 bit/cycle
💡 选择建议:如果只需要"看起来随机"的数(仿真、测试),LFSR更简单。如果需要统计质量更高的随机数,规则30 PRNG更好。如果需要密码学安全,两者都不够——请使用专门的CSPRNG。

🧪 统计验证Testbench

PRNG统计测试

// ============================================================================
// prng_rule30_stat_tb.v - 规则30 PRNG统计测试
// 验证输出序列的频率分布和自相关性
// ============================================================================
\`timescale 1ns/1ps

module prng_rule30_stat_tb;

    parameter WIDTH = 64;
    parameter OUT_W = 8;
    parameter SAMPLES = 10000;  // 采样数

    reg  clk, rst_n, enable, init;
    reg  [WIDTH-1:0] seed;
    wire [OUT_W-1:0] rand_out;
    wire valid;
    wire [WIDTH-1:0] debug;

    prng_rule30 #(.WIDTH(WIDTH), .OUT_WIDTH(OUT_W)) dut (
        .clk(clk), .rst_n(rst_n), .enable(enable),
        .init(init), .seed(seed),
        .random_out(rand_out), .valid(valid), .state_debug(debug)
    );

    initial clk = 0;
    always #5 clk = ~clk;

    // 统计计数器
    integer counts [0:255];  // 对8位输出的256种值计数
    integer total;
    real    chi_square;
    real    expected;

    integer n;

    initial begin
        // 初始化计数器
        for (n = 0; n < 256; n = n + 1) counts[n] = 0;
        total = 0;

        $dumpfile("prng_rule30.vcd");
        $dumpvars(0, prng_rule30_stat_tb);

        // 复位
        rst_n = 0; enable = 0; init = 0;
        #50 rst_n = 1;

        // 初始化:中心位为1
        seed = 64'h1 << 31;
        init = 1; #10; init = 0;

        // 采集样本
        enable = 1;
        for (n = 0; n < SAMPLES; n = n + 1) begin
            @(posedge clk);
            counts[rand_out] = counts[rand_out] + 1;
            total = total + 1;
        end

        // 计算卡方统计量
        expected = real'(total) / 256.0;
        chi_square = 0.0;
        for (n = 0; n < 256; n = n + 1) begin
            chi_square = chi_square +
                ((real'(counts[n]) - expected) *
                 (real'(counts[n]) - expected)) / expected;
        end

        $display("=== Rule 30 PRNG Statistics ===");
        $display("Samples: %0d", total);
        $display("Expected per bin: %0.1f", expected);
        $display("Chi-square: %0.2f", chi_square);
        $display("Chi-square critical (df=255, p=0.05): 293.25");
        if (chi_square < 293.25)
            $display("✅ PASSED: Distribution appears uniform");
        else
            $display("❌ FAILED: Distribution may be non-uniform");

        $finish;
    end

endmodule

🏋️ 练习

练习3.1:用Python或C实现规则30的中心列PRNG,运行10000步,用chi-square测试验证输出位的均匀性。
练习3.2:修改prng_rule30,增加一个"预热"计数器——初始化后先运行T步(不输出),再开始提取随机数。T应该是多少?为什么需要预热?
练习3.3:实现规则30与LFSR的混合PRNG——用LFSR的输出作为规则30的种子,周期性地重新播种。分析这种混合方案的优缺点。
练习3.4:测量规则30 PRNG在FPGA上的最大时钟频率和资源占用。与同位宽的LFSR比较。
练习3.5(挑战):尝试从规则30 PRNG的输出序列反推CA的初始状态。如果WIDTH=32,你需要多少个输出位?这说明了什么?

🏆 成就解锁

🏅 混沌猎手

你已理解确定性系统如何产生混沌行为,并实现了基于规则30的硬件PRNG。

🔬 确定性与随机性的悖论

规则30揭示了一个深刻的哲学问题:完全确定的规则可以产生统计上不可预测的行为。这是"决定论混沌"的核心——可计算≠可预测。

📖 扩展阅读

元胞自动机课程 · 从Conway到Langton到Lattice Gas