我喜欢 itertools.combinations 并想要一个内存效率高的 JavaScript generator function,但无法快速找到可接受的库,所以我自己开发了。
它在 TypeScript 中(帮助我跟踪簿记),但我会在底部附加转译后的 JavaScript。
function* range(start: number, end: number) {
for (; start <= end; ++start) { yield start; }
}
function last<T>(arr: T[]) { return arr[arr.length - 1]; }
function* numericCombinations(n: number, r: number, loc: number[] = []): IterableIterator<number[]> {
const idx = loc.length;
if (idx === r) {
yield loc;
return;
}
for (let next of range(idx ? last(loc) + 1 : 0, n - r + idx)) { yield* numericCombinations(n, r, loc.concat(next)); }
}
function* combinations<T>(arr: T[], r: number) {
for (let idxs of numericCombinations(arr.length, r)) { yield idxs.map(i => arr[i]); }
}
所有的黑魔法都在 numericCombinations 函数中,它是一个递归生成器——请参阅 yield* 上的 MDN 文档。实际的 combinations 函数只是将其包装以匹配 Python API。
我可以把它放在一个.ts 文件中并把它放在它的底部:
if (module === require.main) {
const shorts = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');
let i = 0;
for (let o of combinations(shorts, 7)) { i++; }
console.log(i);
}
Node 在不到 2.5 分钟的时间内打印出 133784560,并且在我的 2015 年老式笔记本电脑上使用最少的内存。也就是说,它生成了所有亿三千四百万种方式,您可以从一副标准的五十二张扑克牌中选择七张牌(即所有完整的德州扑克牌),而不会增加记忆力或过度使用- 嵌套递归函数调用。
(Python3 可以在 20 秒内完成此操作,或者快 7 倍……加速到上述欢迎。)
JavaScript 代码:
function* range(start, end) {
for (; start <= end; ++start) { yield start; }
}
function last(arr) { return arr[arr.length - 1]; }
function* numericCombinations(n, r, loc = []) {
const idx = loc.length;
if (idx === r) {
yield loc;
return;
}
for (let next of range(idx ? last(loc) + 1 : 0, n - r + idx)) { yield* numericCombinations(n, r, loc.concat(next)); }
}
function* combinations(arr, r) {
for (let idxs of numericCombinations(arr.length, r)) { yield idxs.map(i => arr[i]); }
}