What is the best way to find all combinations of items in an array?
What is the best way to find all combinations of items in an array in c#?
Solution 1:
UPDATED
Here are a set of generic functions (require .net 3.5 or higher) for different scenarios. The outputs are for a list of {1, 2, 3, 4} and a length of 2.
Permutations with repetition
static IEnumerable<IEnumerable<T>>
GetPermutationsWithRept<T>(IEnumerable<T> list, int length)
{
if (length == 1) return list.Select(t => new T[] { t });
return GetPermutationsWithRept(list, length - 1)
.SelectMany(t => list,
(t1, t2) => t1.Concat(new T[] { t2 }));
}
Output:
{1,1} {1,2} {1,3} {1,4} {2,1} {2,2} {2,3} {2,4} {3,1} {3,2} {3,3} {3,4} {4,1} {4,2} {4,3} {4,4}
Permutations
static IEnumerable<IEnumerable<T>>
GetPermutations<T>(IEnumerable<T> list, int length)
{
if (length == 1) return list.Select(t => new T[] { t });
return GetPermutations(list, length - 1)
.SelectMany(t => list.Where(o => !t.Contains(o)),
(t1, t2) => t1.Concat(new T[] { t2 }));
}
Output:
{1,2} {1,3} {1,4} {2,1} {2,3} {2,4} {3,1} {3,2} {3,4} {4,1} {4,2} {4,3}
K-combinations with repetition
static IEnumerable<IEnumerable<T>>
GetKCombsWithRept<T>(IEnumerable<T> list, int length) where T : IComparable
{
if (length == 1) return list.Select(t => new T[] { t });
return GetKCombsWithRept(list, length - 1)
.SelectMany(t => list.Where(o => o.CompareTo(t.Last()) >= 0),
(t1, t2) => t1.Concat(new T[] { t2 }));
}
Output:
{1,1} {1,2} {1,3} {1,4} {2,2} {2,3} {2,4} {3,3} {3,4} {4,4}
K-combinations
static IEnumerable<IEnumerable<T>>
GetKCombs<T>(IEnumerable<T> list, int length) where T : IComparable
{
if (length == 1) return list.Select(t => new T[] { t });
return GetKCombs(list, length - 1)
.SelectMany(t => list.Where(o => o.CompareTo(t.Last()) > 0),
(t1, t2) => t1.Concat(new T[] { t2 }));
}
Output:
{1,2} {1,3} {1,4} {2,3} {2,4} {3,4}
Solution 2:
That's called permutations.
This can give you the permutations of any collection:
public class Permutation {
public static IEnumerable<T[]> GetPermutations<T>(T[] items) {
int[] work = new int[items.Length];
for (int i = 0; i < work.Length; i++) {
work[i] = i;
}
foreach (int[] index in GetIntPermutations(work, 0, work.Length)) {
T[] result = new T[index.Length];
for (int i = 0; i < index.Length; i++) result[i] = items[index[i]];
yield return result;
}
}
public static IEnumerable<int[]> GetIntPermutations(int[] index, int offset, int len) {
if (len == 1) {
yield return index;
} else if (len == 2) {
yield return index;
Swap(index, offset, offset + 1);
yield return index;
Swap(index, offset, offset + 1);
} else {
foreach (int[] result in GetIntPermutations(index, offset + 1, len - 1)) {
yield return result;
}
for (int i = 1; i < len; i++) {
Swap(index, offset, offset + i);
foreach (int[] result in GetIntPermutations(index, offset + 1, len - 1)) {
yield return result;
}
Swap(index, offset, offset + i);
}
}
}
private static void Swap(int[] index, int offset1, int offset2) {
int temp = index[offset1];
index[offset1] = index[offset2];
index[offset2] = temp;
}
}
Example:
string[] items = { "one", "two", "three" };
foreach (string[] permutation in Permutation.GetPermutations<string>(items)) {
Console.WriteLine(String.Join(", ", permutation));
}
Solution 3:
It is O(n!)
static List<List<int>> comb;
static bool[] used;
static void GetCombinationSample()
{
int[] arr = { 10, 50, 3, 1, 2 };
used = new bool[arr.Length];
used.Fill(false);
comb = new List<List<int>>();
List<int> c = new List<int>();
GetComb(arr, 0, c);
foreach (var item in comb)
{
foreach (var x in item)
{
Console.Write(x + ",");
}
Console.WriteLine("");
}
}
static void GetComb(int[] arr, int colindex, List<int> c)
{
if (colindex >= arr.Length)
{
comb.Add(new List<int>(c));
return;
}
for (int i = 0; i < arr.Length; i++)
{
if (!used[i])
{
used[i] = true;
c.Add(arr[i]);
GetComb(arr, colindex + 1, c);
c.RemoveAt(c.Count - 1);
used[i] = false;
}
}
}
Solution 4:
Regarding Pengyang answer: Here is my generic function which can return all the combinations from a list of T:
static IEnumerable<IEnumerable<T>>
GetCombinations<T>(IEnumerable<T> list, int length)
{
if (length == 1) return list.Select(t => new T[] { t });
return GetCombinations(list, length - 1)
.SelectMany(t => list, (t1, t2) => t1.Concat(new T[] { t2 }));
}
Example 1:n=3,k=2
IEnumerable<IEnumerable<int>> result =
GetCombinations(Enumerable.Range(1, 3), 2);
Output - a list of integer-lists:
{1, 1} {1, 2} {1, 3} {2, 1} {2, 2} {2, 3} {3, 1} {3, 2} {3, 3}
.............................................................................
I ran this example and I am not quite sure about the rightness of the results.
Example 2:n=3, k=3
IEnumerable<IEnumerable<int>> result =
GetCombinations(Enumerable.Range(1, 3), 3);
Output - a list of integer-lists:
{1, 1, 1} {1, 1, 2} {1, 1, 3}
{1, 2, 1} {1, 2, 2} {1, 2, 3}
{1, 3, 1} {1, 3, 2} {1, 3, 3}
{2, 1, 1} {2, 1, 2} {2, 1, 3}
{2, 2, 1} {2, 2, 2} {2, 2, 3}
{2, 3, 1} {2, 3, 2} {2, 3, 3}
{3, 1, 1} {3, 1, 2} {3, 1, 3}
{3, 2, 1} {3, 2, 2} {3, 2, 3}
{3, 3, 1} {3, 3, 2} {3, 3, 3}
This should not happen with combinations otherwise it should specify it is with repetition.See article http://en.wikipedia.org/wiki/Combinations