Bladeren bron

added combination iterator

Alexander Rose 6 jaren geleden
bovenliggende
commit
024c82ff92
2 gewijzigde bestanden met toevoegingen van 94 en 0 verwijderingen
  1. 29 0
      src/mol-data/util/_spec/combination.spec.ts
  2. 65 0
      src/mol-data/util/combination.ts

+ 29 - 0
src/mol-data/util/_spec/combination.spec.ts

@@ -0,0 +1,29 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { combinations } from '../combination'
+
+describe('Combination', () => {
+    it('test123-2', () => {
+        const c = combinations([1, 2, 3], 2)
+        expect(c).toEqual([[1, 2], [1, 3], [2, 3]]);
+    });
+
+    it('test1234-2', () => {
+        const c = combinations([1, 2, 3, 4], 2)
+        expect(c).toEqual([[1, 2], [1, 3], [2, 3], [1, 4], [2, 4], [3, 4]]);
+    });
+
+    it('test1234-1', () => {
+        const c = combinations([1, 2, 3, 4], 1)
+        expect(c).toEqual([[1], [2], [3], [4]]);
+    });
+
+    it('test1234-4', () => {
+        const c = combinations([1, 2, 3, 4], 4)
+        expect(c).toEqual([[1, 2, 3, 4]]);
+    });
+});

+ 65 - 0
src/mol-data/util/combination.ts

@@ -0,0 +1,65 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+// adpated from https://github.com/dankogai/js-combinatorics, MIT 2013-2016 Dan Kogai
+
+import Iterator from '../iterator'
+
+function P(m: number, n: number) {
+    let p = 1
+    while (n--) p *= m--
+    return p
+}
+
+function C(m: number, n: number) {
+    if (n > m) return 0
+    return P(m, n) / P(n, n)
+}
+
+function nextIndex(n: number) {
+    const smallest = n & -n
+    const ripple = n + smallest
+    const newSmallest = ripple & -ripple
+    const ones = ((newSmallest / smallest) >> 1) - 1
+    return ripple | ones
+};
+
+export class CombinationIterator<T> implements Iterator<T[]> {
+    private value: T[]
+    private index: number
+    private maxIndex: number
+
+    size: number
+    hasNext: boolean = false;
+
+    move() {
+        if (this.hasNext) {
+            let i = 0, j = 0, n = this.index
+            for (; n; n >>>= 1, i++) {
+                if (n & 1) this.value[j++] = this.array[i]
+            }
+            this.index = nextIndex(this.index)
+            this.hasNext = this.index < this.maxIndex
+        }
+        return this.value;
+    }
+
+    constructor(private array: T[], count: number) {
+        this.index = (1 << count) - 1
+        this.size = C(array.length, count)
+        this.maxIndex = 1 << array.length,
+
+        this.value = new Array(count)
+        this.hasNext = count > 0 && count <= array.length
+    }
+}
+
+export function combinations<T>(array: T[], count: number): T[][] {
+    const out: T[][] = []
+    const combinationIt = new CombinationIterator(array, count)
+    while (combinationIt.hasNext) out.push(combinationIt.move().slice())
+    return out
+}