fork(7) download
  1. /*
  2.  * This code demonstrates a solution to the problem described at
  3.  * https://stackoverflow.com/questions/61393463/
  4.  * Copyright (c) 2020, John McClane. All rights reserved.
  5.  *
  6.  */
  7.  
  8. import java.util.*;
  9. import java.util.function.Supplier;
  10. import java.util.function.BiConsumer;
  11. import java.math.BigInteger;
  12.  
  13. abstract class PartitionGenerator implements Supplier<int[]>{
  14. public static final Random rand = new Random();
  15. protected final int numberCount;
  16. protected final int min;
  17. protected final int range;
  18. protected final int sum; // shifted sum
  19. protected final boolean sorted;
  20.  
  21. protected PartitionGenerator(int numberCount, int min, int max, int sum, boolean sorted) {
  22. if (numberCount <= 0)
  23. throw new IllegalArgumentException("Number count should be positive");
  24. this.numberCount = numberCount;
  25. this.min = min;
  26. range = max - min;
  27. if (range < 0)
  28. throw new IllegalArgumentException("min > max");
  29. sum -= numberCount * min;
  30. if (sum < 0)
  31. throw new IllegalArgumentException("Sum is too small");
  32. if (numberCount * range < sum)
  33. throw new IllegalArgumentException("Sum is too large");
  34. this.sum = sum;
  35. this.sorted = sorted;
  36. }
  37.  
  38. // Whether this generator returns sorted arrays (i.e. combinations)
  39. public final boolean isSorted() {
  40. return sorted;
  41. }
  42.  
  43. public interface GeneratorFactory {
  44. PartitionGenerator create(int numberCount, int min, int max, int sum);
  45. }
  46. }
  47.  
  48. // Permutations with repetition (i.e. unsorted vectors) with given sum
  49. class PermutationPartitionGenerator extends PartitionGenerator {
  50. private final double[][] distributionTable;
  51.  
  52. public PermutationPartitionGenerator(int numberCount, int min, int max, int sum) {
  53. super(numberCount, min, max, sum, false);
  54. distributionTable = calculateSolutionCountTable();
  55. }
  56.  
  57. private double[][] calculateSolutionCountTable() {
  58. double[][] table = new double[numberCount + 1][sum + 1];
  59. BigInteger[] a = new BigInteger[sum + 1];
  60. BigInteger[] b = new BigInteger[sum + 1];
  61. for (int i = 1; i <= sum; i++)
  62. a[i] = BigInteger.ZERO;
  63. a[0] = BigInteger.ONE;
  64. table[0][0] = 1.0;
  65. for (int n = 1; n <= numberCount; n++) {
  66. double[] t = table[n];
  67. for (int s = 0; s <= sum; s++) {
  68. for (int i = Math.max(0, s - range); i <= s; i++)
  69. z = z.add(a[i]);
  70. b[s] = z;
  71. t[s] = z.doubleValue();
  72. }
  73. // swap a and b
  74. BigInteger[] c = b;
  75. b = a;
  76. a = c;
  77. }
  78. return table;
  79. }
  80.  
  81. @Override
  82. public int[] get() {
  83. int[] p = new int[numberCount];
  84. int s = sum; // current sum
  85. for (int i = numberCount - 1; i >= 0; i--) {
  86. double t = rand.nextDouble() * distributionTable[i + 1][s];
  87. double[] tableRow = distributionTable[i];
  88. int oldSum = s;
  89. // lowerBound is introduced only for safety, it shouldn't be crossed
  90. int lowerBound = s - range;
  91. if (lowerBound < 0)
  92. lowerBound = 0;
  93. s++;
  94. do
  95. t -= tableRow[--s];
  96. // s can be equal to lowerBound here with t > 0 only due to imprecise subtraction
  97. while (t > 0 && s > lowerBound);
  98. p[i] = min + (oldSum - s);
  99. }
  100. assert s == 0;
  101. return p;
  102. }
  103.  
  104. public static final PartitionGenerator.GeneratorFactory factory = (numberCount, min, max,sum) ->
  105. new PermutationPartitionGenerator(numberCount, min, max, sum);
  106. }
  107.  
  108. // Combinations with repetition (i.e. sorted vectors) with given sum
  109. class CombinationPartitionGenerator extends PartitionGenerator {
  110. private final double[][][] distributionTable;
  111.  
  112. public CombinationPartitionGenerator(int numberCount, int min, int max, int sum) {
  113. super(numberCount, min, max, sum, true);
  114. distributionTable = calculateSolutionCountTable();
  115. }
  116.  
  117. private double[][][] calculateSolutionCountTable() {
  118. double[][][] table = new double[numberCount + 1][range + 1][sum + 1];
  119. BigInteger[][] a = new BigInteger[range + 1][sum + 1];
  120. BigInteger[][] b = new BigInteger[range + 1][sum + 1];
  121. double[][] t = table[0];
  122. for (int m = 0; m <= range; m++) {
  123. a[m][0] = BigInteger.ONE;
  124. t[m][0] = 1.0;
  125. for (int s = 1; s <= sum; s++) {
  126. a[m][s] = BigInteger.ZERO;
  127. t[m][s] = 0.0;
  128. }
  129. }
  130. for (int n = 1; n <= numberCount; n++) {
  131. t = table[n];
  132. for (int m = 0; m <= range; m++)
  133. for (int s = 0; s <= sum; s++) {
  134. if (m == 0)
  135. z = a[0][s];
  136. else {
  137. z = b[m - 1][s];
  138. if (m <= s)
  139. z = z.add(a[m][s - m]);
  140. }
  141. b[m][s] = z;
  142. t[m][s] = z.doubleValue();
  143. }
  144. // swap a and b
  145. BigInteger[][] c = b;
  146. b = a;
  147. a = c;
  148. }
  149. return table;
  150. }
  151.  
  152. @Override
  153. public int[] get() {
  154. int[] p = new int[numberCount];
  155. int m = range; // current max
  156. int s = sum; // current sum
  157. for (int i = numberCount - 1; i >= 0; i--) {
  158. double t = rand.nextDouble() * distributionTable[i + 1][m][s];
  159. double[][] tableCut = distributionTable[i];
  160. if (s < m)
  161. m = s;
  162. s -= m;
  163. while (true) {
  164. t -= tableCut[m][s];
  165. // m can be 0 here with t > 0 only due to imprecise subtraction
  166. if (t <= 0 || m == 0)
  167. break;
  168. m--;
  169. s++;
  170. }
  171. p[i] = min + m;
  172. }
  173. assert s == 0;
  174. return p;
  175. }
  176.  
  177. public static final PartitionGenerator.GeneratorFactory factory = (numberCount, min, max, sum) ->
  178. new CombinationPartitionGenerator(numberCount, min, max, sum);
  179. }
  180.  
  181. class SmithTromblePartitionGenerator extends PartitionGenerator {
  182. public SmithTromblePartitionGenerator(int numberCount, int min, int max, int sum) {
  183. super(numberCount, min, max, sum, false);
  184. }
  185.  
  186. @Override
  187. public int[] get() {
  188. List<Integer> ls = new ArrayList<>(numberCount + 1);
  189. int[] ret = new int[numberCount];
  190. int increasedSum = sum + numberCount;
  191. while (true) {
  192. ls.add(0);
  193. while (ls.size() < numberCount) {
  194. int c = 1 + rand.nextInt(increasedSum - 1);
  195. if (!ls.contains(c))
  196. ls.add(c);
  197. }
  198. Collections.sort(ls);
  199. ls.add(increasedSum);
  200. boolean good = true;
  201. for (int i = 0; i < numberCount; i++) {
  202. int x = ls.get(i + 1) - ls.get(i) - 1;
  203. if (x > range) {
  204. good = false;
  205. break;
  206. }
  207. ret[i] = x;
  208. }
  209. if (good) {
  210. for (int i = 0; i < numberCount; i++)
  211. ret[i] += min;
  212. return ret;
  213. }
  214. ls.clear();
  215. }
  216. }
  217.  
  218. public static final PartitionGenerator.GeneratorFactory factory = (numberCount, min, max, sum) ->
  219. new SmithTromblePartitionGenerator(numberCount, min, max, sum);
  220. }
  221.  
  222. // Enumerates all partitions with given parameters
  223. class SequentialEnumerator extends PartitionGenerator {
  224. private final int max;
  225. private final int[] p;
  226. private boolean finished;
  227.  
  228. public SequentialEnumerator(int numberCount, int min, int max, int sum, boolean sorted) {
  229. super(numberCount, min, max, sum, sorted);
  230. this.max = max;
  231. p = new int[numberCount];
  232. startOver();
  233. }
  234.  
  235. private void startOver() {
  236. finished = false;
  237. int unshiftedSum = sum + numberCount * min;
  238. fillMinimal(0, Math.max(min, unshiftedSum - (numberCount - 1) * max), unshiftedSum);
  239. }
  240.  
  241. private void fillMinimal(int beginIndex, int minValue, int fillSum) {
  242. int fillRange = max - minValue;
  243. if (fillRange == 0)
  244. Arrays.fill(p, beginIndex, numberCount, max);
  245. else {
  246. int fillCount = numberCount - beginIndex;
  247. fillSum -= fillCount * minValue;
  248. int maxCount = fillSum / fillRange;
  249. int maxStartIndex = numberCount - maxCount;
  250. Arrays.fill(p, maxStartIndex, numberCount, max);
  251. fillSum -= maxCount * fillRange;
  252. Arrays.fill(p, beginIndex, maxStartIndex, minValue);
  253. if (fillSum != 0)
  254. p[maxStartIndex - 1] = minValue + fillSum;
  255. }
  256. }
  257.  
  258. @Override
  259. public int[] get() { // returns null when there is no more partition, then starts over
  260. if (finished) {
  261. startOver();
  262. return null;
  263. }
  264. int[] pCopy = p.clone();
  265. if (numberCount > 1) {
  266. int i = numberCount;
  267. int s = p[--i];
  268. while (i > 0) {
  269. int x = p[--i];
  270. if (x == max) {
  271. s += x;
  272. continue;
  273. }
  274. x++;
  275. s--;
  276. int minRest = sorted ? x : min;
  277. if (s < minRest * (numberCount - i - 1)) {
  278. s += x;
  279. continue;
  280. }
  281. p[i++]++;
  282. fillMinimal(i, minRest, s);
  283. return pCopy;
  284. }
  285. }
  286. finished = true;
  287. return pCopy;
  288. }
  289.  
  290. public static final PartitionGenerator.GeneratorFactory permutationFactory = (numberCount, min, max, sum) ->
  291. new SequentialEnumerator(numberCount, min, max, sum, false);
  292. public static final PartitionGenerator.GeneratorFactory combinationFactory = (numberCount, min, max, sum) ->
  293. new SequentialEnumerator(numberCount, min, max, sum, true);
  294. }
  295.  
  296. class Test {
  297. private final int numberCount;
  298. private final int min;
  299. private final int max;
  300. private final int sum;
  301. private final int repeatCount;
  302. private final BiConsumer<PartitionGenerator, Test> procedure;
  303.  
  304. public Test(int numberCount, int min, int max, int sum, int repeatCount,
  305. BiConsumer<PartitionGenerator, Test> procedure) {
  306. this.numberCount = numberCount;
  307. this.min = min;
  308. this.max = max;
  309. this.sum = sum;
  310. this.repeatCount = repeatCount;
  311. this.procedure = procedure;
  312. }
  313.  
  314. @Override
  315. public String toString() {
  316. return String.format("=== %d numbers from [%d, %d] with sum %d, %d iterations ===",
  317. numberCount, min, max, sum, repeatCount);
  318. }
  319.  
  320. private static class GeneratedVector {
  321. final int[] v;
  322.  
  323. GeneratedVector(int[] vect) {
  324. v = vect;
  325. }
  326.  
  327. @Override
  328. public int hashCode() {
  329. return Arrays.hashCode(v);
  330. }
  331.  
  332. @Override
  333. public boolean equals(Object obj) {
  334. if (this == obj)
  335. return true;
  336. return Arrays.equals(v, ((GeneratedVector)obj).v);
  337. }
  338.  
  339. @Override
  340. public String toString() {
  341. return Arrays.toString(v);
  342. }
  343. }
  344.  
  345. private static final Comparator<Map.Entry<GeneratedVector, Integer>> lexicographical = (e1, e2) -> {
  346. int[] v1 = e1.getKey().v;
  347. int[] v2 = e2.getKey().v;
  348. int len = v1.length;
  349. int d = len - v2.length;
  350. if (d != 0)
  351. return d;
  352. for (int i = 0; i < len; i++) {
  353. d = v1[i] - v2[i];
  354. if (d != 0)
  355. return d;
  356. }
  357. return 0;
  358. };
  359.  
  360. private static final Comparator<Map.Entry<GeneratedVector, Integer>> byCount =
  361. Comparator.<Map.Entry<GeneratedVector, Integer>>comparingInt(Map.Entry::getValue)
  362. .thenComparing(lexicographical);
  363.  
  364. public static int SHOW_MISSING_LIMIT = 10;
  365.  
  366. private static void checkMissingPartitions(Map<GeneratedVector, Integer> map, PartitionGenerator reference) {
  367. int missingCount = 0;
  368. while (true) {
  369. int[] v = reference.get();
  370. if (v == null)
  371. break;
  372. GeneratedVector gv = new GeneratedVector(v);
  373. if (!map.containsKey(gv)) {
  374. if (missingCount == 0)
  375. System.out.println(" Missing:");
  376. if (++missingCount > SHOW_MISSING_LIMIT) {
  377. System.out.println(" . . .");
  378. break;
  379. }
  380. System.out.println(gv);
  381. }
  382. }
  383. }
  384.  
  385. public static final BiConsumer<PartitionGenerator, Test> distributionTest(boolean sortByCount) {
  386. return (PartitionGenerator gen, Test test) -> {
  387. System.out.print("\n" + getName(gen) + "\n\n");
  388. Map<GeneratedVector, Integer> combos = new HashMap<>();
  389. // There's no point of checking permus for sorted generators
  390. // because they are the same as combos for them
  391. Map<GeneratedVector, Integer> permus = gen.isSorted() ? null : new HashMap<>();
  392. for (int i = 0; i < test.repeatCount; i++) {
  393. int[] v = gen.get();
  394. if (v == null && gen instanceof SequentialEnumerator)
  395. break;
  396. if (permus != null) {
  397. permus.merge(new GeneratedVector(v), 1, Integer::sum);
  398. v = v.clone();
  399. Arrays.sort(v);
  400. }
  401. combos.merge(new GeneratedVector(v), 1, Integer::sum);
  402. }
  403. Set<Map.Entry<GeneratedVector, Integer>> sortedEntries = new TreeSet<>(
  404. sortByCount ? byCount : lexicographical);
  405. System.out.println("Combos" + (gen.isSorted() ? ":" : " (don't have to be uniform):"));
  406. sortedEntries.addAll(combos.entrySet());
  407. for (Map.Entry<GeneratedVector, Integer> e : sortedEntries)
  408. System.out.println(e);
  409. checkMissingPartitions(combos, test.getGenerator(SequentialEnumerator.combinationFactory));
  410. if (permus != null) {
  411. System.out.println("\nPermus:");
  412. sortedEntries.clear();
  413. sortedEntries.addAll(permus.entrySet());
  414. for (Map.Entry<GeneratedVector, Integer> e : sortedEntries)
  415. System.out.println(e);
  416. checkMissingPartitions(permus, test.getGenerator(SequentialEnumerator.permutationFactory));
  417. }
  418. };
  419. }
  420.  
  421. public static final BiConsumer<PartitionGenerator, Test> correctnessTest =
  422. (PartitionGenerator gen, Test test) -> {
  423. String genName = getName(gen);
  424. for (int i = 0; i < test.repeatCount; i++) {
  425. int[] v = gen.get();
  426. if (v == null && gen instanceof SequentialEnumerator)
  427. v = gen.get();
  428. if (v.length != test.numberCount)
  429. throw new RuntimeException(genName + ": array of wrong length");
  430. int s = 0;
  431. if (gen.isSorted()) {
  432. if (v[0] < test.min || v[v.length - 1] > test.max)
  433. throw new RuntimeException(genName + ": generated number is out of range");
  434. int prev = test.min;
  435. for (int x : v) {
  436. if (x < prev)
  437. throw new RuntimeException(genName + ": unsorted array");
  438. s += x;
  439. prev = x;
  440. }
  441. } else
  442. for (int x : v) {
  443. if (x < test.min || x > test.max)
  444. throw new RuntimeException(genName + ": generated number is out of range");
  445. s += x;
  446. }
  447. if (s != test.sum)
  448. throw new RuntimeException(genName + ": wrong sum");
  449. }
  450. System.out.format("%30s : correctness test passed%n", genName);
  451. };
  452.  
  453. public static final BiConsumer<PartitionGenerator, Test> performanceTest =
  454. (PartitionGenerator gen, Test test) -> {
  455. long time = System.nanoTime();
  456. for (int i = 0; i < test.repeatCount; i++)
  457. gen.get();
  458. time = System.nanoTime() - time;
  459. System.out.format("%30s : %8.3f s %10.0f ns/test%n", getName(gen), time * 1e-9, time * 1.0 / test.repeatCount);
  460. };
  461.  
  462. public PartitionGenerator getGenerator(PartitionGenerator.GeneratorFactory factory) {
  463. return factory.create(numberCount, min, max, sum);
  464. }
  465.  
  466. public static String getName(PartitionGenerator gen) {
  467. String name = gen.getClass().getSimpleName();
  468. if (gen instanceof SequentialEnumerator)
  469. return (gen.isSorted() ? "Sorted " : "Unsorted ") + name;
  470. else
  471. return name;
  472. }
  473.  
  474. public static PartitionGenerator.GeneratorFactory[] factories = { SmithTromblePartitionGenerator.factory,
  475. PermutationPartitionGenerator.factory, CombinationPartitionGenerator.factory,
  476. SequentialEnumerator.permutationFactory, SequentialEnumerator.combinationFactory };
  477.  
  478. public static void main(String[] args) {
  479. Test[] tests = {
  480. new Test(3, 0, 3, 5, 3_000, distributionTest(false)),
  481. new Test(3, 0, 6, 12, 3_000, distributionTest(true)),
  482. new Test(50, -10, 20, 70, 1_000, correctnessTest),
  483. new Test(7, 3, 10, 42, 25_000, performanceTest),
  484. new Test(20, 3, 10, 120, 1_500, performanceTest)
  485. };
  486. for (Test t : tests) {
  487. System.out.println(t);
  488. for (PartitionGenerator.GeneratorFactory factory : factories) {
  489. PartitionGenerator candidate = t.getGenerator(factory);
  490. t.procedure.accept(candidate, t);
  491. }
  492. System.out.println();
  493. }
  494. }
  495. }
  496.  
Success #stdin #stdout 3.06s 126148KB
stdin
Standard input is empty
stdout
=== 3 numbers from [0, 3] with sum 5, 3000 iterations ===

SmithTromblePartitionGenerator

Combos (don't have to be uniform):
[0, 2, 3]=1517
[1, 1, 3]=744
[1, 2, 2]=739

Permus:
[0, 2, 3]=242
[0, 3, 2]=261
[1, 1, 3]=249
[1, 2, 2]=232
[1, 3, 1]=249
[2, 0, 3]=229
[2, 1, 2]=254
[2, 2, 1]=253
[2, 3, 0]=241
[3, 0, 2]=252
[3, 1, 1]=246
[3, 2, 0]=292

PermutationPartitionGenerator

Combos (don't have to be uniform):
[0, 2, 3]=1491
[1, 1, 3]=750
[1, 2, 2]=759

Permus:
[0, 2, 3]=248
[0, 3, 2]=267
[1, 1, 3]=252
[1, 2, 2]=262
[1, 3, 1]=239
[2, 0, 3]=246
[2, 1, 2]=260
[2, 2, 1]=237
[2, 3, 0]=247
[3, 0, 2]=219
[3, 1, 1]=259
[3, 2, 0]=264

CombinationPartitionGenerator

Combos:
[0, 2, 3]=985
[1, 1, 3]=991
[1, 2, 2]=1024

Unsorted SequentialEnumerator

Combos (don't have to be uniform):
[0, 2, 3]=6
[1, 1, 3]=3
[1, 2, 2]=3

Permus:
[0, 2, 3]=1
[0, 3, 2]=1
[1, 1, 3]=1
[1, 2, 2]=1
[1, 3, 1]=1
[2, 0, 3]=1
[2, 1, 2]=1
[2, 2, 1]=1
[2, 3, 0]=1
[3, 0, 2]=1
[3, 1, 1]=1
[3, 2, 0]=1

Sorted SequentialEnumerator

Combos:
[0, 2, 3]=1
[1, 1, 3]=1
[1, 2, 2]=1

=== 3 numbers from [0, 6] with sum 12, 3000 iterations ===

SmithTromblePartitionGenerator

Combos (don't have to be uniform):
[4, 4, 4]=113
[3, 3, 6]=297
[2, 5, 5]=301
[0, 6, 6]=315
[2, 4, 6]=638
[1, 5, 6]=654
[3, 4, 5]=682

Permus:
[6, 6, 0]=82
[3, 6, 3]=84
[6, 4, 2]=88
[6, 5, 1]=89
[5, 5, 2]=93
[2, 6, 4]=98
[4, 3, 5]=98
[5, 2, 5]=98
[3, 4, 5]=101
[6, 3, 3]=104
[3, 5, 4]=105
[5, 6, 1]=107
[6, 2, 4]=108
[1, 6, 5]=109
[3, 3, 6]=109
[2, 5, 5]=110
[6, 1, 5]=110
[4, 6, 2]=111
[4, 4, 4]=113
[4, 5, 3]=113
[6, 0, 6]=114
[2, 4, 6]=116
[4, 2, 6]=117
[0, 6, 6]=119
[1, 5, 6]=119
[5, 1, 6]=120
[5, 4, 3]=130
[5, 3, 4]=135

PermutationPartitionGenerator

Combos (don't have to be uniform):
[4, 4, 4]=104
[3, 3, 6]=313
[2, 5, 5]=322
[0, 6, 6]=347
[3, 4, 5]=610
[2, 4, 6]=647
[1, 5, 6]=657

Permus:
[5, 3, 4]=90
[2, 6, 4]=93
[6, 2, 4]=93
[4, 3, 5]=95
[4, 5, 3]=99
[5, 6, 1]=99
[2, 5, 5]=100
[5, 5, 2]=100
[3, 4, 5]=102
[3, 3, 6]=103
[4, 4, 4]=104
[3, 6, 3]=105
[6, 3, 3]=105
[6, 1, 5]=106
[5, 1, 6]=109
[1, 6, 5]=110
[5, 4, 3]=110
[6, 4, 2]=110
[6, 5, 1]=111
[6, 0, 6]=113
[3, 5, 4]=114
[0, 6, 6]=115
[4, 6, 2]=115
[4, 2, 6]=117
[2, 4, 6]=119
[6, 6, 0]=119
[1, 5, 6]=122
[5, 2, 5]=122

CombinationPartitionGenerator

Combos:
[2, 5, 5]=405
[1, 5, 6]=406
[3, 4, 5]=408
[0, 6, 6]=429
[2, 4, 6]=437
[4, 4, 4]=450
[3, 3, 6]=465

Unsorted SequentialEnumerator

Combos (don't have to be uniform):
[4, 4, 4]=1
[0, 6, 6]=3
[2, 5, 5]=3
[3, 3, 6]=3
[1, 5, 6]=6
[2, 4, 6]=6
[3, 4, 5]=6

Permus:
[0, 6, 6]=1
[1, 5, 6]=1
[1, 6, 5]=1
[2, 4, 6]=1
[2, 5, 5]=1
[2, 6, 4]=1
[3, 3, 6]=1
[3, 4, 5]=1
[3, 5, 4]=1
[3, 6, 3]=1
[4, 2, 6]=1
[4, 3, 5]=1
[4, 4, 4]=1
[4, 5, 3]=1
[4, 6, 2]=1
[5, 1, 6]=1
[5, 2, 5]=1
[5, 3, 4]=1
[5, 4, 3]=1
[5, 5, 2]=1
[5, 6, 1]=1
[6, 0, 6]=1
[6, 1, 5]=1
[6, 2, 4]=1
[6, 3, 3]=1
[6, 4, 2]=1
[6, 5, 1]=1
[6, 6, 0]=1

Sorted SequentialEnumerator

Combos:
[0, 6, 6]=1
[1, 5, 6]=1
[2, 4, 6]=1
[2, 5, 5]=1
[3, 3, 6]=1
[3, 4, 5]=1
[4, 4, 4]=1

=== 50 numbers from [-10, 20] with sum 70, 1000 iterations ===
SmithTromblePartitionGenerator :   correctness test passed
 PermutationPartitionGenerator :   correctness test passed
 CombinationPartitionGenerator :   correctness test passed
 Unsorted SequentialEnumerator :   correctness test passed
   Sorted SequentialEnumerator :   correctness test passed

=== 7 numbers from [3, 10] with sum 42, 25000 iterations ===
SmithTromblePartitionGenerator :    0.016 s        636 ns/test
 PermutationPartitionGenerator :    0.006 s        228 ns/test
 CombinationPartitionGenerator :    0.005 s        216 ns/test
 Unsorted SequentialEnumerator :    0.008 s        311 ns/test
   Sorted SequentialEnumerator :    0.045 s       1812 ns/test

=== 20 numbers from [3, 10] with sum 120, 1500 iterations ===
SmithTromblePartitionGenerator :    0.056 s      37466 ns/test
 PermutationPartitionGenerator :    0.001 s        644 ns/test
 CombinationPartitionGenerator :    0.001 s        470 ns/test
 Unsorted SequentialEnumerator :    0.000 s         30 ns/test
   Sorted SequentialEnumerator :    0.000 s         45 ns/test