-
Notifications
You must be signed in to change notification settings - Fork 31
/
product.ts
86 lines (78 loc) · 2.14 KB
/
product.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
/* eslint-disable no-param-reassign */
/**
* 遍历多个类数组对象的笛卡尔积.
* @param arrs `selects`中的每个类数组对象都是一个可选项列表.
* @param f 回调函数, 用于处理每个笛卡尔积的结果.返回`true`时停止遍历.
* @example
* ```ts
* enumerateProduct([['A', 'a'], ['1'], ['B', 'b'], ['2']], select => {
* console.log(select)
* })
* // [ 'A', '1', 'B', '2' ]
* // [ 'A', '1', 'b', '2' ]
* // [ 'a', '1', 'B', '2' ]
* // [ 'a', '1', 'b', '2' ]
* ```
* @complexity 11!(4e7) => 383.51ms
*/
function enumerateProduct<S>(
arrs: ArrayLike<S>[],
f: (groupView: readonly S[]) => boolean | void
): void {
const n = arrs.length
const bt = (pos: number, group: S[], ptr: number): boolean => {
if (pos === n) {
return !!f(group)
}
const arr = arrs[pos]
for (let i = 0; i < arr.length; i++) {
group[ptr] = arr[i]
if (bt(pos + 1, group, ptr + 1)) return true
}
return false
}
bt(0, Array(n), 0)
}
/**
* 模拟python的`itertools.product`.
* @complexity 11!(4e7) => 17.461s
* @deprecated
*/
function* product<S>(...arrs: ArrayLike<S>[]): Generator<S[]> {
const n = arrs.length
yield* bt(0, [])
function* bt(pos: number, path: S[]): Generator<S[]> {
if (pos === n) {
yield path.slice()
return
}
const arr = arrs[pos]
for (let i = 0; i < arr.length; i++) {
path.push(arr[i])
yield* bt(pos + 1, path)
path.pop()
}
}
}
export { enumerateProduct }
if (require.main === module) {
enumerateProduct([['A', 'a'], ['1'], ['B', 'b'], ['2']], select => {
console.log(select)
})
// performance test
const n = 11
const facs = Array.from({ length: n }, (_, i) => Array.from({ length: i + 1 }, (_, j) => j + 1))
console.time('enumerateProduct')
let count = 0
enumerateProduct(facs, select => {
count++
})
console.timeEnd('enumerateProduct') // !383.51ms
console.log(count) // !39916800
count = 0
console.time('product')
for (const _ of product(...facs)) {
count++
}
console.timeEnd('product') // !17.461s
}