From e1f1eda4705921c089d4b9649101635f5aa87445 Mon Sep 17 00:00:00 2001 From: Daniel Sword Date: Mon, 19 Nov 2018 20:10:50 -0800 Subject: [PATCH] Combinatorics: add a method for lexicographic generation of all possible subsets of a set. --- src/Numerics/Combinatorics.cs | 72 +++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/src/Numerics/Combinatorics.cs b/src/Numerics/Combinatorics.cs index ff62df10..ffc12d43 100644 --- a/src/Numerics/Combinatorics.cs +++ b/src/Numerics/Combinatorics.cs @@ -197,6 +197,78 @@ namespace MathNet.Numerics } } + /// + /// Lazily generates all combinations lexicographically. + /// Note that the same array is yielded on each iteration, + /// so care is needed when converting to a collection. + /// + /// Number of items in the set. + /// Number of items to choose, without replacement. + /// A lazy enumeration of all unique subsets of size k. + public static IEnumerable GenerateAllCombinations(int n, int k) + { + // This method follows Algorithm T found in + // Knuth, "Combinatorial Algorithms," The Art of Computer Science vol. 4A, 7.2.1.3 + + if (n < 0) throw new ArgumentOutOfRangeException(nameof(n), Resources.ArgumentNotNegative); + if (k < 0) throw new ArgumentOutOfRangeException(nameof(k), Resources.ArgumentNotNegative); + + // We'll repeatedly yield the following array as we generate the permutations. + // The first entry is just the items in lexicographical order. + var a = new int[k]; + + var c = Enumerable.Range(0, k + 2).ToArray(); + c[k] = n; + c[k + 1] = 0; + + int x; + int j = k; + if (k <= n) // Returns empty enumerable if k > n; + { + if (k == n) + yield return c.Take(k).ToArray(); + else + while (true) + { + for (int i = 0; i < k; i++) + a[i] = c[i]; + yield return a; + + if (j > 0) + { + x = j; + } + else + { + // Easy case? + if (c[0] + 1 < c[1]) + { + c[0] = c[0] + 1; + continue; + } + + // We differ slightly here from Knuth. + // Instead of j=2, we set to 1. + // But we put j++ at the beginning of the do loop that immediately follows. + j = 1; + + do + { + j++; + c[j - 2] = j - 2; + x = c[j - 1] + 1; + } while (x == c[j]); + + if (j > k) break; + } + + + c[j - 1] = x; + j--; + } + } + } + /// /// Select a random permutation, without repetition, from a data array by reordering the provided array in-place. /// Implemented using Fisher-Yates Shuffling. The provided data array will be modified.