林浩盯着屏幕上那串看似随机的数字:1011001...,这是他刚写的一个“伪随机数生成器”。作为大三学生,他总觉得哪里不对劲——为什么每次重启程序,生成的序列都一模一样?他的教授说过,真正的随机性在计算机里根本不存在,只有“看起来像随机”的序列。林浩想知道,这背后的秘密到底藏在哪里。
一、从林浩的困惑说起:什么是线性反馈移位寄存器?
先别被名字吓住。LFSR(Linear Feedback Shift Register,线性反馈移位寄存器)其实是个非常直观的东西。想象你有一排小灯泡,每个灯泡要么亮(1),要么灭(0)。每隔一秒,所有灯泡往右移动一位,最右边那个灯泡的值会“反馈”到最左边,但反馈的方式是经过一些特殊的“异或”运算。
这个“反馈 tap”(抽头)的位置决定了整个序列的性质。如果选对了位置,你能得到一个长度极长、看起来完全随机的序列——这就是 LFSR 的魔力。
但这里有个关键区别:LFSR 生成的不是真正的随机数,而是伪随机数。它的周期最长为 \(2^n - 1\)(n 为寄存器位数),之后就会开始重复。对于密码学场景,这远远不够;但对于仿真、测试数据生成,它却是个高效的选择。
二、LFSR 的工作原理:用最简单的话讲清楚
2.1 基本结构
一个 n 位 LFSR 由以下部分组成:
- 移位寄存器:n 个二进制位,初始状态称为“种子”(seed)
- 反馈函数:通常是异或(XOR)运算
- 抽头位置:决定哪些位参与反馈
2.2 操作步骤
假设我们有一个 4 位 LFSR,抽头位置为 4 和 1(这是标准的 Fibonacci LFSR):
步骤1: 初始状态 1011
步骤2: 输出最右边位 1,寄存器向右移位
步骤3: 计算反馈位 = 第4位 XOR 第1位 = 1 XOR 1 = 0
步骤4: 将反馈位插入最左边
步骤5: 新状态 0101,重复...
让我们手动跟踪前几个状态:
| 步骤 | 寄存器状态 | 输出位 | 反馈计算 |
|---|---|---|---|
| 0 | 1011 | 1 | 1 XOR 1 = 0 |
| 1 | 0101 | 1 | 0 XOR 1 = 1 |
| 2 | 1010 | 0 | 1 XOR 0 = 1 |
| 3 | 1101 | 1 | 1 XOR 1 = 0 |
| 4 | 0110 | 0 | 0 XOR 1 = 1 |
| 5 | 1011 | 1 | ← 回到初始状态! |
等等,第5步又回到了 1011?这意味着周期只有 5?不对,我漏算了什么。让我重新检查……
实际上,对于 4 位 LFSR,最大周期应该是 \(2^4 - 1 = 15\)。问题出在我的抽头选择上。让我换一个更标准的抽头配置。
三、Java 实现:从基础到生产级
3.1 基础实现:理解核心逻辑
/**
* 基础 LFSR 实现 - Fibonacci 结构
*
* @author 林浩
* @version 1.0
*/
public class BasicLFSR {
private final int length; // 寄存器位数
private final int[] taps; // 抽头位置(1-indexed)
private int state; // 当前状态
/**
* 构造函数
* @param length 寄存器位数(推荐 8-32)
* @param taps 抽头位置数组,例如 {4, 1} 表示第4位和第1位参与反馈
*/
public BasicLFSR(int length, int... taps) {
if (length <= 0 || length > 32) {
throw new IllegalArgumentException("长度必须在 1-32 之间");
}
this.length = length;
this.taps = taps.clone();
this.state = 0;
// 验证抽头位置
for (int tap : taps) {
if (tap <= 0 || tap > length) {
throw new IllegalArgumentException("抽头位置必须在 1-" + length + " 之间");
}
}
}
/**
* 设置种子
*/
public void setSeed(int seed) {
// 确保种子不为全0(全0状态会导致死锁)
this.state = (seed & ~0) >>> 0; // 简单屏蔽高位
if (this.state == 0) {
this.state = 1; // 默认非零种子
}
}
/**
* 生成下一个伪随机位
*/
public int nextBit() {
int feedback = 0;
// 计算反馈值:所有抽头位置的值异或
for (int tap : taps) {
// 提取第 tap 位(从右往左数,1-indexed)
int bit = (state >>> (tap - 1)) & 1;
feedback ^= bit;
}
// 右移一位
state >>>= 1;
// 将反馈值插入最高位
state |= (feedback << (length - 1));
// 返回被移出的位(最右边那位)
return feedback;
}
/**
* 获取当前状态(用于调试)
*/
public int getState() {
return state;
}
/**
* 将状态转换为二进制字符串
*/
public String toBinaryString() {
return String.format("%" + length + "s", Integer.toBinaryString(state))
.replace(' ', '0');
}
public static void main(String[] args) {
// 4位 LFSR,抽头 {4, 1}
BasicLFSR lfsr = new BasicLFSR(4, 4, 1);
lfsr.setSeed(0b1011);
System.out.println("初始状态: " + lfsr.toBinaryString());
// 输出前15位
for (int i = 0; i < 15; i++) {
int bit = lfsr.nextBit();
System.out.println("步骤 " + (i + 1) + ": 输出=" + bit +
", 状态=" + lfsr.toBinaryString());
}
}
}
运行这个程序,你会看到 LFSR 遍历了所有15个非零状态,然后回到初始状态。这就是最大长度 LFSR 的特性。
3.2 优化版本:使用 long 处理 64 位 LFSR
/**
* 高性能 64 位 LFSR 实现
* 使用 Galois 结构,更适合现代 CPU
*/
public class HighPerformanceLFSR64 {
private static final long TAPS = 0x800000000000000DL; // 抽头掩码
private static final long POLYNOMIAL = 0xD00000000000000BL; // 本原多项式
private long state;
public HighPerformanceLFSR64() {
this.state = 1; // 默认种子
}
public HighPerformanceLFSR64(long seed) {
this.state = (seed == 0) ? 1 : seed;
}
/**
* Galois 结构的 nextLong 实现
* 比 Fibonacci 结构快 2-3 倍
*/
public long nextLong() {
long feedback = (state & 1) != 0 ? POLYNOMIAL : 0;
state = (state >>> 1) ^ feedback;
return state;
}
/**
* 生成 [0, n) 范围内的整数
*/
public int nextInt(int n) {
if (n <= 0) throw new IllegalArgumentException("n 必须大于 0");
// 使用 rejection sampling 避免偏差
int limit = Integer.MAX_VALUE - ((Integer.MAX_VALUE % n) + 1) % n;
int r;
do {
r = (int)(nextLong() >>> 33);
} while (r >= limit);
return r % n;
}
/**
* 生成 [0.0, 1.0) 范围内的 double
*/
public double nextDouble() {
return (nextLong() >>> 11) * 0x1.0p-53;
}
public static void main(String[] args) {
HighPerformanceLFSR64 lfsr = new HighPerformanceLFSR64(0xDEADBEEFL);
System.out.println("前10个随机long值:");
for (int i = 0; i < 10; i++) {
System.out.printf("%d: %d (0x%016X)%n",
i, lfsr.nextLong(), lfsr.nextLong());
}
System.out.println("\n测试均匀分布:");
HighPerformanceLFSR64 testLfsr = new HighPerformanceLFSR64();
int[] counts = new int[10];
for (int i = 0; i < 10000; i++) {
counts[testLfsr.nextInt(10)]++;
}
for (int i = 0; i < 10; i++) {
System.out.printf("%d: %d (%.1f%%)%n", i, counts[i], counts[i] * 100.0 / 10000);
}
}
}
3.3 完整项目:带统计分析和可视化的 LFSR 工具箱
”`java import java.util.; import java.io.;
/**
LFSR 工具箱 - 包含多种实现和统计分析功能
功能:
1. 多种 LFSR 实现(Fibonacci/Galois, 不同位数)
2. 周期检测
3. 统计检验(频率、游程、自相关)
4. NIST SP 800-22 部分测试
-
- CSV 导出 */ public class LFSRTutorial {
// ==================== LFSR 实现 ====================
/**
Fibonacci 结构 LFSR */ static class FibonacciLFSR { private final int length; private final int[] taps; private int state;
public FibonacciLFSR(int length, int… taps) {
this.length = length; this.taps = taps.clone(); this.state = 0; for (int tap : taps) { if (tap <= 0 || tap > length) { throw new IllegalArgumentException("抽头位置无效"); } }}
public void setSeed(int seed) {
this.state = seed & ((1 << length) - 1); if (this.state == 0) this.state = 1;}
public int next() {
int feedback = 0; for (int tap : taps) { feedback ^= (state >>> (tap - 1)) & 1; } state = (state >>> 1) | (feedback << (length - 1)); return feedback;}
public int getState() { return state; }
public int getPeriod() {
int initial = state; int count = 0; do { next(); count++; } while (state != initial); return count;} }
/**
Galois 结构 LFSR(更高效) */ static class GaloisLFSR { private final int length; private final int polynomial; private int state;
// 已知的最大长度多项式 private static final int[][] MAX_LENGTH_POLYNOMIALS = {
{8, 0x1D}, // x^8 + x^4 + x^3 + x^2 + 1 {16, 0x8005}, // x^16 + x^14 + x^13 + x^11 + 1 {32, 0x80000053} // x^32 + x^22 + x^2 + x^1 + 1};
public GaloisLFSR(int length) {
this.length = length; // 查找或生成最大长度多项式 this.polynomial = findMaxLengthPolynomial(length); this.state = 1;}
public GaloisLFSR(int length, int polynomial) {
this.length = length; this.polynomial = polynomial; this.state = 1;}
private int findMaxLengthPolynomial(int n) {
for (int[] poly : MAX_LENGTH_POLYNOMIALS) { if (poly[0] == n) return poly[1]; } // 如果没有预定义的,使用一个默认值(不保证最大周期) return 0x80000053; // 32位默认}
public void setSeed(int seed) {
this.state = (seed == 0) ? 1 : seed;}
public int next() {
int lsb = state & 1; state >>>= 1; if (lsb != 0) { // XOR 多项式,排除最高位 state ^= (polynomial << 1) & ((1 << length) - 1); } return lsb;}
public int getState() { return state; } }
// ==================== 统计分析 ====================
/**
频率测试(Monobit 测试)
检查 0 和 1 的数量是否大致相等 */ public static class FrequencyTestResult { public final double pValue; public final boolean passed;
public FrequencyTestResult(double pValue, boolean passed) {
this.pValue = pValue; this.passed = passed;}
@Override public String toString() {
return String.format("频率测试: p值=%.6f, %s", pValue, passed ? "通过" : "失败");} }
/**
游程测试 */ public static class RunTestResult { public final double pValue; public final boolean passed;
public RunTestResult(double pValue, boolean passed) {
this.pValue = pValue; this.passed = passed;}
@Override public String toString() {
return String.format("游程测试: p值=%.6f, %s", pValue, passed ? "通过" : "失败");} }
/**
执行频率测试 */ public static FrequencyTestResult frequencyTest(int[] bits) { int n = bits.length; int sum = 0; for (int bit : bits) sum += bit;
double s = (2 * sum - n) / Math.sqrt(n); double pValue = erfc(Math.abs(s) / Math.sqrt(2));
return new FrequencyTestResult(pValue, pValue > 0.01); }
/**
执行游程测试 */ public static RunTestResult runTest(int[] bits) { int n = bits.length;
// 计算 1 的比例 int ones = 0; for (int bit : bits) if (bit == 1) ones++; double pi = (double) ones / n;
if (pi == 0 || pi == 1) {
return new RunTestResult(0.0, false);}
// 计算游程数 int runs = 1; for (int i = 1; i < n; i++) {
if (bits[i] != bits[i - 1]) runs++;}
// 理论期望和方差 double E_runs = 2 * n * pi * (1 - pi) + 0.5; double variance = (2 * n * pi * (1 - pi) * (2 * n * pi * (1 - pi) - 1)) /
(4 * n * pi * pi * (1 - pi) * (1 - pi));double z = (runs - E_runs) / Math.sqrt(variance); double pValue = erfc(Math.abs(z) / Math.sqrt(2));
return new RunTestResult(pValue, pValue > 0.