[英]Shuffle a cartesian product iterator in Javascript
使用像https://www.npmjs.com/package/cartesian-product-generator這樣的庫,我能夠生成笛卡爾積迭代器。 當我有 4 個 arrays 時,這是必要的,每個長度為 100,給出 1 億個組合 - memory 太密集而無法生成數組。
但是,我想隨機化這 1 億個組合,以便我可以以隨機順序遍歷所有組合。 如何在不將所有 1 億個項目放入 memory 的情況下隨機化迭代順序?
您可以使用以下技巧。
想想從元素到數字的映射。
以彼得斯為例:
// ---------- 2 1 0
const arr1 = ['r', 'g', 'b'];
const arr2 = ['early', 'late'];
const arr3 = ['high', 'low'];
您可以 map arr1 到 (0, 1, 2),arr2 到 (0, 1) 和 arr3 到 (0, 1)。
因此,像 201 這樣的 3 位數字將編碼為 r-late-high。
但是您可以將映射轉換為十進制數,它是 3 2 =12 的組合,因此您的十進制數需要 2 位數字或適合 4 位(“1001”)。
這樣,您的 12 種組合可以通過生成數字 0-11 並洗牌來洗牌。 但更好:從 0-11 生成一個隨機數,並使用一個位數組來標記每個組合,這已經被看到/測試過。
在一般情況下,每個數組的大小都是 10,您可以直接使用該數字作為您的值的鍵。 最后一位表示最后一個數組的元素,第一個數組的第一位等等。
它需要更多的注意力來處理不同的數組大小和 10 以下和以上的數字,但它是簡單的乘法/除法和模運算。
例如,給定大小為 212、7 和 19 的 3 Arrays,您有 212 7 19 = 28196 種可能的組合。
您生成 sample=rnd (28196) 並得到 13936。現在您將該值取模 19 (% arr3.length()),即 9。因此選擇第三個數組的索引 9 處的元素。
你取 13936/19 得到 733。733 % 7 是 5,所以這表示 arr2 的第 5 個元素。
733/7=104, 104%212 是104 所以你從arr1 中選擇第104 個元素。 這是可重現的和快速的。 在您的位數組中,您將索引 13936 處的位標記為真(默認值:假)。
我寫了一個類似的算法,不是用 JS,而是 Scala,而且速度超快。
如果您碰巧搜索了幾乎整個數組,當數組的 99.999% 已標記為“真”時,它會變慢,並且重復的隨機數總是返回一個已經測試過的值。 那么你最好按順序訪問元素或計算看到的元素並只從大小計數生成一個隨機數,然后遍歷位數組並跳過所有看到的位。 你甚至可以從策略 1 開始,當事情變慢時切換到第二個。
您不能使用迭代器對結果進行隨機化,因為您需要將 memory 中的所有 arrays 進行隨機化。
但是,您可以構建自己的隨機笛卡爾積生成器:
function randomCartesianProduct() { return Object.keys(arguments).map(key => { let arr = arguments[key]; return arr[Math.floor(Math.random() * arr.length)] }); } const arr1 = ['r', 'g', 'b']; const arr2 = ['early', 'late']; const arr3 = ['high', 'low']; for(let i = 1; i <= 50; i++) { let r = randomCartesianProduct(arr1, arr2, arr3); console.log(i + ': ' + r.join(', ')); }
更新 1基於附加要求以避免重復任何產品:
class RandomCartesianProduct { constructor(...args) { this.arrays = args; this.productSize = this.arrays.reduce((acc, arr) => acc * arr.length, 1); this.seen = {}; this.seenSize = 0; } next() { if(this.seenSize >= this.productSize) { return null; // all combinations exhausted } let result; let hash; do { let idxes = []; result = this.arrays.map(arr => { let idx = Math.floor(Math.random() * arr.length); idxes.push(idx); return arr[idx]; }); hash = idxes.join('-'); } while(this.seen[hash]); this.seen[hash] = true; this.seenSize++; return result; } }; const arr1 = ['r', 'g', 'b']; const arr2 = ['early', 'late']; const arr3 = ['high', 'low']; const random = new RandomCartesianProduct(arr1, arr2, arr3); for(let i = 1; i <= 16; i++) { let r = random.next(); console.log(i + ': ' + r); }
Output:(許多隨機之一)
1: g,late,low
2: g,late,high
3: g,early,low
4: r,early,low
5: b,early,low
6: r,early,high
7: b,early,high
8: b,late,low
9: g,early,high
10: b,late,high
11: r,late,low
12: r,late,high
13: null
14: null
15: null
16: null
this.seen
hash 曲目已經返回了隨機值。 hash是根據隨機索引變成arrays,eg比較短。
一旦所有組合都用完,將返回null
。
請記住,由於多次重試,使用 many & big arrays 運行此程序會隨着時間的推移而變慢,例如,對看到的項目的點擊會隨着時間的推移而增加。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.