combination.ts 1.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465
  1. /**
  2. * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  5. */
  6. // adpated from https://github.com/dankogai/js-combinatorics, MIT 2013-2016 Dan Kogai
  7. import Iterator from '../iterator';
  8. function P(m: number, n: number) {
  9. let p = 1;
  10. while (n--) p *= m--;
  11. return p;
  12. }
  13. function C(m: number, n: number) {
  14. if (n > m) return 0;
  15. return P(m, n) / P(n, n);
  16. }
  17. function nextIndex(n: number) {
  18. const smallest = n & -n;
  19. const ripple = n + smallest;
  20. const newSmallest = ripple & -ripple;
  21. const ones = ((newSmallest / smallest) >> 1) - 1;
  22. return ripple | ones;
  23. };
  24. export class CombinationIterator<T> implements Iterator<ReadonlyArray<T>> {
  25. private value: T[]
  26. private index: number
  27. private maxIndex: number
  28. size: number
  29. hasNext: boolean = false;
  30. move() {
  31. if (this.hasNext) {
  32. let i = 0, j = 0, n = this.index;
  33. for (; n; n >>>= 1, i++) {
  34. if (n & 1) this.value[j++] = this.array[i];
  35. }
  36. this.index = nextIndex(this.index);
  37. this.hasNext = this.index < this.maxIndex;
  38. }
  39. return this.value;
  40. }
  41. constructor(private array: T[], count: number) {
  42. this.index = (1 << count) - 1;
  43. this.size = C(array.length, count);
  44. this.maxIndex = 1 << array.length,
  45. this.value = new Array(count);
  46. this.hasNext = count > 0 && count <= array.length;
  47. }
  48. }
  49. export function combinations<T>(array: T[], count: number): T[][] {
  50. const out: T[][] = [];
  51. const combinationIt = new CombinationIterator(array, count);
  52. while (combinationIt.hasNext) out.push(combinationIt.move().slice());
  53. return out;
  54. }