概览
在上一章建立压缩流水线后,我们聚焦为这些流程供能的数值引擎:确定性的伪随机数生成器、行为良好的数学助手与在速度与安全间平衡的哈希原语。Zig 0.15.2 保持这些组件模块化——std.Random生成可复现序列,std.math提供严谨的容差与常量,标准库将哈希分为非加密与加密两大类,以便你按工作负载选择合适工具。math.zigwyhash.zigsha2.zig
学习目标
- 播种、推进和重现
std.Random生成器,同时采样常见分布。Xoshiro256.zig - 应用
std.math实用工具——常量、夹紧、容差和几何助手——以保持数值代码稳定。hypot.zig - 区分像 Wyhash 这样的快速哈希器和像 SHA-256 这样的密码学摘要,并负责任地将两者连接到文件处理作业中。
随机数基础
Zig 将伪随机生成器作为一等值公开:您播种引擎,请求整数、浮点数或索引,您的代码拥有状态转换。这种透明性让您可以控制模糊器、仿真和确定性测试。Random.zig
可复现序列的确定性生成器
std.Random.DefaultPrng 包装 Xoshiro256++,当您调用 init(seed) 时通过 SplitMix64 为自身播种。从那里您获得一个 Random 外观,它公开高级助手——范围、洗牌、浮点数——同时保持底层状态私有。
const std = @import("std");
pub fn main() !void {
var stdout_buffer: [4096]u8 = undefined;
var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer);
const stdout = &stdout_writer.interface;
const seed: u64 = 0x0006_7B20; // 424,224 in decimal
var prng = std.Random.DefaultPrng.init(seed);
var rand = prng.random();
const dice_roll = rand.intRangeAtMost(u8, 1, 6);
const coin = if (rand.boolean()) "heads" else "tails";
var ladder = [_]u8{ 0, 1, 2, 3, 4, 5 };
rand.shuffle(u8, ladder[0..]);
const unit_float = rand.float(f64);
var reproducible = [_]u32{ undefined, undefined, undefined };
var check_prng = std.Random.DefaultPrng.init(seed);
var check_rand = check_prng.random();
for (&reproducible) |*slot| {
slot.* = check_rand.int(u32);
}
try stdout.print("seed=0x{X:0>8}\n", .{seed});
try stdout.print("d6 roll -> {d}\n", .{dice_roll});
try stdout.print("coin flip -> {s}\n", .{coin});
try stdout.print("shuffled ladder -> {any}\n", .{ladder});
try stdout.print("unit float -> {d:.6}\n", .{unit_float});
try stdout.print("first three u32 -> {any}\n", .{reproducible});
try stdout.flush();
}
$ zig run prng_sequences.zigseed=0x00067B20
d6 roll -> 5
coin flip -> tails
shuffled ladder -> { 0, 4, 3, 2, 5, 1 }
unit float -> 0.742435
first three u32 -> { 2135551917, 3874178402, 2563214192 }uintLessThan 的公平性保证依赖于生成器的均匀输出;当常数时间行为比完美分布更重要时,回退为 uintLessThanBiased。
分布与采样启发式
除了均匀抽取,Random.floatNorm 和 Random.floatExp 公开 Ziggurat 支持的正态和指数样本——非常适合合成工作负载或噪声注入。ziggurat.zig 加权选择来自 weightedIndex,而 Xoshiro 引擎上的 .jump() 以确定性方式向前跳跃 2^128 步,以在无重叠的情况下跨线程分区流。29 对于密码学用途,交换到 std.crypto.random 或 std.Random.DefaultCsprng 来继承基于 ChaCha 的熵,而不是快速但可预测的 PRNG。tlcsprng.zig
实用数学工具
std.math命名空间将基础常量与实用工具结合:截断、近似相等与几何助手在各 CPU 目标上共享一致的语义。
数值卫生工具包
组合少数几个助手——sqrt、clamp、近似相等和黄金比例常数——保持报告代码可读且可移植。sqrt.zig
const std = @import("std");
pub fn main() !void {
var stdout_buffer: [4096]u8 = undefined;
var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer);
const stdout = &stdout_writer.interface;
const m = std.math;
const latencies = [_]f64{ 0.94, 1.02, 0.87, 1.11, 0.99, 1.05 };
var sum: f64 = 0;
var sum_sq: f64 = 0;
var minimum = latencies[0];
var maximum = latencies[0];
for (latencies) |value| {
sum += value;
sum_sq += value * value;
minimum = @min(minimum, value);
maximum = @max(maximum, value);
}
const mean = sum / @as(f64, @floatFromInt(latencies.len));
const rms = m.sqrt(sum_sq / @as(f64, @floatFromInt(latencies.len)));
const normalized = m.clamp((mean - 0.8) / 0.6, 0.0, 1.0);
const turn_degrees: f64 = 72.0;
const turn_radians = turn_degrees * m.rad_per_deg;
const right_angle = m.pi / 2.0;
const approx_right = m.approxEqRel(f64, turn_radians, right_angle, 1e-12);
const hyp = m.hypot(3.0, 4.0);
try stdout.print("sample count -> {d}\n", .{latencies.len});
try stdout.print("min/max -> {d:.2} / {d:.2}\n", .{ minimum, maximum });
try stdout.print("mean -> {d:.3}\n", .{mean});
try stdout.print("rms -> {d:.3}\n", .{rms});
try stdout.print("normalized mean -> {d:.3}\n", .{normalized});
try stdout.print("72deg in rad -> {d:.6}\n", .{turn_radians});
try stdout.print("close to right angle? -> {s}\n", .{if (approx_right) "yes" else "no"});
try stdout.print("hypot(3,4) -> {d:.1}\n", .{hyp});
try stdout.print("phi constant -> {d:.9}\n", .{m.phi});
try stdout.flush();
}
$ zig run math_inspector.zigsample count -> 6
min/max -> 0.87 / 1.11
mean -> 0.997
rms -> 1.000
normalized mean -> 0.328
72deg in rad -> 1.256637
close to right angle? -> no
hypot(3,4) -> 5.0
phi constant -> 1.618033989大幅值比较优先使用approxEqRel,接近零时优先approxEqAbs;两者都兼顾 IEEE-754 的边界情况而不触发 NaN 问题。
容差、缩放与派生量
角度转换使用 rad_per_deg/deg_per_rad,而 hypot 通过避免灾难性抵消在毕达哥拉斯计算中保持精度。链接变换时,即使您的公共 API 使用更窄的浮点数,也要将中间结果保持在 f64 中——std.math 中的混合类型重载会正确处理并避免编译器警告。39
哈希:可复现性与完整性
Zig 明确分离了哈希策略:std.hash 系列针对内存桶的速度和低碰撞率,而 std.crypto.hash.sha2 为完整性检查或签名流水线提供标准摘要。
用于桶的非加密哈希
std.hash.Wyhash.hash 生成一个您任意播种的 64 位值,非常适合哈希映射或布隆过滤器,其中雪崩特性比抗对手性更重要。如果您需要具有编译时类型感知功能的结构化哈希,std.hash.autoHash 会递归遍历您的字段并将它们输入到可配置的后端。44auto_hash.zig
具有务实护栏的 SHA-256 摘要流水线
即使您的 CLI 只需要校验和,也要将 SHA-256 视为完整性原语——而不是真实性保证——并为用户记录这一区别。
const std = @import("std");
pub fn main() !void {
var stdout_buffer: [4096]u8 = undefined;
var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer);
const stdout = &stdout_writer.interface;
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer std.debug.assert(gpa.deinit() == .ok);
const allocator = gpa.allocator();
const args = try std.process.argsAlloc(allocator);
defer std.process.argsFree(allocator, args);
const input_path = if (args.len > 1) args[1] else "payload.txt";
var file = try std.fs.cwd().openFile(input_path, .{ .mode = .read_only });
defer file.close();
var sha256 = std.crypto.hash.sha2.Sha256.init(.{});
var buffer: [4096]u8 = undefined;
while (true) {
const read = try file.read(&buffer);
if (read == 0) break;
sha256.update(buffer[0..read]);
}
var digest: [std.crypto.hash.sha2.Sha256.digest_length]u8 = undefined;
sha256.final(&digest);
const sample = "payload preview";
const wyhash = std.hash.Wyhash.hash(0, sample);
try stdout.print("wyhash(seed=0) {s} -> 0x{x:0>16}\n", .{ sample, wyhash });
const hex_digest = std.fmt.bytesToHex(digest, .lower);
try stdout.print("sha256({s}) ->\n {s}\n", .{ input_path, hex_digest });
try stdout.print("(remember: sha256 certifies integrity, not authenticity.)\n", .{});
try stdout.flush();
}
$ zig run hash_digest_tool.zig -- chapters-data/code/50__random-and-math/payload.txtwyhash(seed=0) payload preview -> 0x30297ecbb2bd0c02
sha256(chapters-data/code/50__random-and-math/payload.txt) ->
0498ca2116fb55b7a502d0bf3ad5d0e0b3f4e23ad919bdc0f9f151ca3637a6fa
(remember: sha256 certifies integrity, not authenticity.)注意与警示
Random结构体不是线程安全的;为每个工作器分割不同的生成器或使用原子保护访问以避免共享状态竞争。29std.math函数遵循 IEEE-754 NaN 传播——在无效操作后永远不要依赖比较而不进行显式检查。- 密码学摘要应与签名检查、HMAC 或可信分发配对;单独的 SHA-256 仅检测损坏,而非篡改。hash_composition.zig
练习
- 在第一个示例中将
DefaultPrng替换为std.Random.DefaultCsprng,并测量不同构建模式下的性能差异。39ChaCha.zig - 扩展
math_inspector.zig以使用approxEqRel计算置信区间,从而标记延迟报告中的异常值。47 - 修改
hash_digest_tool.zig,为第49章中 TAR 归档内的每个文件计算并存储 SHA-256 摘要,同时发出一个清单与归档一起。tar.zig
注意事项、替代方案与边界情况
- Xoshiro 上的跳跃函数会不可逆地改变状态;如果您以后需要回退,请在调用
jump()之前快照您的生成器。 - 在巨型文件上避免使用
bytesToHex进行流式输出——首选增量编码器以避开大型栈分配。 - 巨型文件(>4 GiB)的 SHA-256 摘要必须考虑特定于平台的路径编码;在流水线早期规范化 UTF-8/UTF-16 以避免哈希不同的字节流。45