- 测试
【免费下载链接】chancejs
Chance - Random generator helper for JavaScript
本文介绍 Chance.js 中一种高级初始化方式:向new Chance()传入一个自定义函数,用它替换库内置的 Mersenne Twister 随机数生成器。读完本文,你将理解这一模式的底层实现原理、函数的取值约束,并掌握它在性能优化、确定性测试等场景中的实际用法。
为什么需要替换随机数生成器
Chance.js 默认构建在 Mersenne Twister。
但种子并不是唯一的初始化途径。Chance.js 允许你不提供种子,而是直接传入一个函数,让这个函数成为整个库生成一切数据时的随机数来源。也就是说,从chance.address()到chance.integer(),所有生成器的底层随机值都来自你传入的这个函数。
核心用法:传入一个随机函数
官方 function 文档 给出的最简示例是直接用 JavaScript 内置的Math.random()替换 Mersenne Twister:
// Use Math.random() instead of our Mersenne Twister var chance = new Chance(Math.random); chance.address() => '131 Asmun Pike' chance.address() => '261 Pawnaf Highway'同样的address()调用,Chance 表面上工作方式与默认完全一致,但底层随机生成器已经不同——这正是该模式的精髓:换芯不换壳。
源码级原理:构造函数如何识别并接管函数
Chance.js 的构造函数(见 chance.js 构造函数)在初始化时对传入参数做了明确分支:
function Chance (seed) { if (!(this instanceof Chance)) { if (!seed) { seed = null; } return seed === null ? new Chance() : new Chance(seed); } // if user has provided a function, use that as the generator if (typeof seed === 'function') { this.random = seed; return this; } // ... 其余分支处理数值/字符串种子,构建 Mersenne Twister 实例 }关键逻辑在于typeof seed === 'function'这一判断:一旦检测到传入的是函数,就直接把this.random指向该函数并立即返回,完全绕过种子的散列计算与 Mersenne Twister 实例化。
这解释了“库其余部分都会使用这个函数”的原因:Chance.js 内部几乎所有生成器都通过this.random()取得基础随机值。从源码结构可以看到大量这样的调用,例如:
- bool 生成器 用
this.random() * 100 < options.likelihood判断真假; - integer 生成器 用
Math.floor(this.random() * (options.max - options.min + 1) + options.min)计算整数; - weighted 加权选择 用
this.random() * sum定位加权区间。
因此,只要替换了this.random,上述所有生成逻辑都会自动切换到底层随机源,无需任何额外改动。
作为对照,默认路径下 Chance 会基于种子构造 MT19937 实例(实现见 chance.js 中的 MersenneTwister),其random()方法返回[0, 1)区间内的实数。当不传任何参数时,MT 会用Math.floor(Math.random() * Math.pow(10, 13))自动生成一个随机种子(见 chance.js#L11295-L11298)。
函数的取值约束
文档明确指出:传入的函数必须返回 0 到 1 之间的任意数字(This function should return any number between 0 and 1)。
这一约束与 Mersenne Twister 默认random()的输出区间保持一致——它返回[0, 1)区间内的实数。库内各个生成器都会基于这个基础值做缩放、取整、区间映射等运算,如果函数返回超出该区间的值,后续生成的整数、地址、坐标等数据的分布范围就会偏离预期。因此在自定义生成器时,请务必保证返回值落在[0, 1)内。
实战场景一:确定性测试与结果精确控制
自定义函数并不一定要是“真随机”的。测试用例 test/test.basic.js#L138-L144 展示了传入一个恒定返回值函数的用法:
test('Chance() will take an arbitrary function for the seed and use it', t => { let chance = new Chance(() => 123) _.times(1000, () => { t.is(chance.random(), 123) }) })这里new Chance(() => 123)让chance.random()恒等于123,测试连续调用 1000 次都返回相同值。这说明该模式可用于:
- 构造完全确定的随机序列,便于单元测试中断言具体的生成结果;
- 通过注入固定返回值的函数,直接验证某个生成器在特定基础值下的输出是否符合预期;
- 在不需要统计随机性的场合消除 MT 的初始化与状态维护开销。
实战场景二:性能优化的基准测试
在 test/test.helpers.js 的weighted()测试中,官方代码就大量使用new Chance(Math.random)来加速测试,其注释明确指出原因:
Use Math.random as the random function rather than our Mersenne twister just to speed things up here because this test takes awhile to gather enough data to have a large enough sample size to adequately test. This increases speed by a few orders of magnitude at the cost of repeatability (which we aren't using here)
即在需要海量采样(如 50000 次加权抽样,见 test/test.helpers.js#L283-L302)来验证分布比例时,用原生Math.random()代替 Mersenne Twister 可以提升数个数量级的执行速度,代价是失去可复现性。类似的用法还出现在该文件的加权分布验证与小数权重测试中(test/test.helpers.js#L337-L361、test/test.helpers.js#L363-L379)。
这为业务侧提供了清晰参考:当不关心可复现性、但追求吞吐量时,传入Math.random是官方认可的优化手段。
与种子模式的取舍
两种初始化方式各有用武之地,可对照 seed 文档 理解:
| 初始化方式 | 随机源 | 可复现性 | 典型用途 |
|---|---|---|---|
new Chance(seed) | Mersenne Twister(基于种子初始化) | 相同种子产生相同序列 | 测试、演示、需要稳定结果的场景 |
new Chance(func) | 任意自定义函数 | 取决于函数本身 | 接入自定义随机源、性能优先、精确控制输出 |
一句话概括:种子模式追求“同因同果”,函数模式追求“自由换芯”。无论选择哪种,Chance.js 对外暴露的数十个生成器 API 都不受影响——这正是其在初始化阶段做“随机源抽象”的价值所在。
- 测试
【免费下载链接】chancejs
Chance - Random generator helper for JavaScript
相关推荐
Chance.js与Mersenne Twister:深入理解伪随机数生成原理
Chance.js与Mersenne Twister:深入理解伪随机数生成原理 随机数在编程中无处不在,从游戏开发到数据模拟,从测试用例到密码学应用。但你是否曾
测试Chance.js 随机生成器完全指南:基于 Mersenne Twister 的可重复随机数据生成
Chance.js 随机生成器完全指南:基于 Mersenne Twister 的可重复随机数据生成 本篇指南以 README.md https://link.
测试Chance.js `set()` 数据覆盖指南:用自定义数据集替换随机生成器的内置词库
Chance.js set 数据覆盖指南:用自定义数据集替换随机生成器的内置词库 Chance 是一个用于 JavaScript 的随机数据生成助手(Rando
测试
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考