生成随机性检测测试样本——e、pi、√2、√3的前一亿比特
1. 背景
随机性检测文档NIST SP800-22rev1a (A Statistical Test Suite for Random and Pseudorandom Number Generators for Cryptographic Applications)中提到,他们提供了四个样本——由Mathematica生成的经典常数的二进制展开,长度均超过 100 万位,分别为
- data.e(自然常数 e 的二进制展开)、
- data.pi(圆周率 π 的二进制展开)、
- data.sqrt2(√2 的二进制展开)
- data.sqrt3(√3 的二进制展开)。
生成这些文件所使用的 Mathematica 程序见NIST SP800-22rev1a的附录 F。在NIST SP800-22rev1a的附录 B 给出这些样本数据的实证结果,对每个数据文件,均应用了NIST SP800-22rev1a的所有统计测试,并将结果记录在表格中。
GM/T 0005—2021《随机性检测规范》中给出三种样本长度,分别是20,000比特、1,000,000比特、100,000,000比特。NIST SP800-22rev1a中给出的四个样本可用于GM/T 0005—2021中的前两种样本长度的检测,但无法满足100,000,000比特的要求。
以下给出生成这四种样本的100,000,000比特的python代码,以便测试使用。
2. python代码
python代码生成四种样本(e、π、√2、√3)的前100,000,000比特。注意,这里沿用NIST的设定——输出的比特是包括整数部分的比特序列,而不是仅小数部分的二进制展开。
例如,NIST提供的data.pi就是把整个pi进行二进制展开。data.pi的第一行给出了24比特数据,为“110010010000111111011010”,这里的前两比特“11”就是表示整数部分3。如果认为这24比特是纯小数部分的展开,则“110010010000111111011010”= 1/2 + 1/4 + 0/8 + 0/16 + 1/32... = 0.785398126,这显然不等于pi的纯小数部分。
AI生成的python代码如下。
import mpmath import time def set_precision(bit_length: int) -> None: """设置mpmath的二进制精度(添加10位冗余抵消误差)""" mpmath.mp.prec = bit_length + 10 # 冗余位确保计算精度 def get_high_precision_number(num_type: str): """获取指定类型的高精度数(mpmath.mpf类型)""" if num_type == "e": return mpmath.e elif num_type == "pi": return mpmath.pi elif num_type == "sqrt2": return mpmath.sqrt(2) elif num_type == "sqrt3": return mpmath.sqrt(3) else: raise ValueError(f"不支持的数类型:{num_type},可选值:e、pi、sqrt2、sqrt3") def number_to_binary(fname: str, num, num_type: str, total_bits: int, chunk_size: int = 10**6) -> None: """ 将高精度数转换为指定长度的二进制串(块级处理优化版) :param num: 高精度数(mpmath.mpf类型) :param num_type: 数的类型(用于文件名) :param total_bits: 目标二进制总长度 :param chunk_size: 每次处理的块大小(比特) """ start_time = time.perf_counter() #fname = f"{num_type}_binary_{total_bits}.txt" print(f"正在计算{num_type}的{total_bits}比特二进制串,将写入文件:{fname}") print(f"write int(num)...") # 步骤1:处理整数部分 int_part = mpmath.floor(num) frac_part = num - int_part # 纯小数部分(0 ≤ frac_part < 1) int_bin = bin(int(mpmath.nint(int_part)))[2:] # 整数部分二进制(不含'0b') write_bits = len(int_bin) # 若整数部分已超过总长度,直接截断 if write_bits >= total_bits: with open(fname, "w", encoding="ascii") as f: f.write(int_bin[:total_bits]) print(f"整数部分过长,已截断为{total_bits}比特") return # 步骤2:处理小数部分(块级批量处理) print(f"write frac(num)...") remaining = total_bits - write_bits # 还需生成的小数位数 current_frac = frac_part with open(fname, "w", encoding="ascii") as f: f.write(int_bin) # 先写入整数部分 while remaining > 0: # 本次处理的块大小(最后一块可能不足chunk_size) current_chunk = min(chunk_size, remaining) # 计算2^current_chunk(用于批量提取位) scale = mpmath.power(2, current_chunk) # 批量获取当前块的整数部分 product = current_frac * scale c = int(mpmath.floor(product)) # 块内二进制对应的整数 # 转换为二进制字符串,不足current_chunk位则在前面补0 bin_str = format(c, f'0{current_chunk}b') # 写入文件 f.write(bin_str) # 更新剩余小数部分和计数 current_frac = product - c # 仅保留小数部分 remaining -= current_chunk # 打印进度 print(f".", end='') print(f"\n成功生成{total_bits}比特的二进制串,文件:{fname}") end_time = time.perf_counter() print(f"number_to_binary 耗时:{end_time - start_time:.3f}秒") if __name__ == "__main__": # 配置参数 num_type = "pi" # 可选:e、pi、sqrt2、sqrt3 total_bits = 10**8 # 目标二进制长度(1亿比特) chunk_size = 10**6 # 块大小(100万比特/块,可根据内存调整) fname = f"d:\\{num_type}_binary_{total_bits}.txt"#生成的比特序列文件 # 执行流程 set_precision(total_bits) high_prec_num = get_high_precision_number(num_type) number_to_binary(fname, high_prec_num, num_type, total_bits, chunk_size)以上代码生成的π的前一百万比特已与NIST提供的data.pi核对通过。
如果需要将比特序列转为字节序列,请自行增补相关功能。
附录 样本数据的测试结果
以下数据来自NIST SP800-22rev1a的附录B。
示例 1:圆周率(π)的二进制展开
统计测试 | P值 |
频率测试 | 0.578211 |
块频率测试(m=128) | 0.380615 |
累积和测试 - 正向 | 0.628308 |
累积和测试 - 反向 | 0.663369 |
游程测试 | 0.419268 |
最长连续 1 串测试 | 0.024390 |
秩测试 | 0.083553 |
频谱 DFT 测试 | 0.010186 |
非重叠模板匹配测试(m=9,模板 B=000000001) | 0.165757 |
重叠模板匹配测试(m=9) | 0.296897 |
通用统计测试 | 0.669012 |
近似熵测试(m=10) | 0.361595 |
随机游走测试(状态 x=+1) | 0.844143 |
随机游走变体测试(状态 x=-1) | 0.760966 |
线性复杂度测试(M=500) | 0.255475 |
串行测试(m=16,∇Ψ²ᵐ) | 0.143005 |
示例 2:自然常数(e)的二进制展开
统计测试 | P值 |
频率测试 | 0.953749 |
块频率测试(m=128) | 0.211072 |
累积和测试 - 正向 | 0.669887 |
累积和测试 - 反向 | 0.724266 |
游程测试 | 0.561917 |
最长连续 1 串测试 | 0.718945 |
秩测试 | 0.306156 |
频谱 DFT 测试 | 0.847187 |
非重叠模板匹配测试(m=9,模板 B=000000001) | 0.078790 |
重叠模板匹配测试(m=9) | 0.110434 |
通用统计测试 | 0.282568 |
近似熵测试(m=10) | 0.700073 |
随机游走测试(状态 x=+1) | 0.786868 |
随机游走变体测试(状态 x=-1) | 0.826009 |
线性复杂度测试(M=500) | 0.826335 |
串行测试(m=16,∇Ψ²ᵐ) | 0.766182 |
示例 4:√2 的二进制展开
统计测试 | P值 |
频率测试 | 0.811881 |
块频率测试(m=128) | 0.833222 |
累积和测试 - 正向 | 0.879009 |
累积和测试 - 反向 | 0.957206 |
游程测试 | 0.313427 |
最长连续 1 串测试 | 0.012117 |
秩测试 | 0.823810 |
频谱 DFT 测试 | 0.581909 |
非重叠模板匹配测试(m=9,模板 B=000000001) | 0.569461 |
重叠模板匹配测试(m=9) | 0.791982 |
通用统计测试 | 0.130805 |
近似熵测试(m=10) | 0.884740 |
随机游走测试(状态 x=+1) | 0.216235 |
随机游走变体测试(状态 x=-1) | 0.566118 |
线性复杂度测试(M=500) | 0.317127 |
串行测试(m=16,∇Ψ²ᵐ) | 0.861925 |
示例 5:√3 的二进制展开
统计测试 | P值 |
频率测试 | 0.610051 |
块频率测试(m=128) | 0.473961 |
累积和测试 - 正向 | 0.917121 |
累积和测试 - 反向 | 0.689519 |
游程测试 | 0.261123 |
最长连续 1 串测试 | 0.446726 |
秩测试 | 0.314498 |
频谱 DFT 测试 | 0.776046 |
非重叠模板匹配测试(m=9,模板 B=000000001) | 0.532235 |
重叠模板匹配测试(m=9) | 0.082716 |
通用统计测试 | 0.165981 |
近似熵测试(m=10) | 0.180481 |
随机游走测试(状态 x=+1) | 0.783283 |
随机游走变体测试(状态 x=-1) | 0.155066 |
线性复杂度测试(M=500) | 0.346469 |
串行测试(m=16,∇Ψ²ᵐ) | 0.157500 |