我需要从另一个包含每种可能组合的列表中创建一个列表。在研究可能的解决方案时,我发现了许多有趣的方法,但所有方法似乎都根据提供的记录数生成结果。我需要将组合增加到最大阈值。
即。考虑以下数组
1,2,3,4,5
我需要将结果看起来类似于(在此示例中阈值为3)
1
1,2
1,2,3
1,2,4
1,2,5
1,3,4
2,3,5... etc
实际上,数据将是IEnumerable。我用一个简单的int []来说明所需的结果。
答案 0 :(得分:5)
我的解决方案使用简单的递归算法来创建组合:
当我们遍历序列时,我们可以立即返回仅保存当前值的序列。我编写了一个简单的扩展方法来为单个项目创建一个IEnumerable。
接下来,我们递归生成剩余元素的所有组合,阈值减1,并将每个元素与当前值组合。
我认为不应重复元素(即不允许{1,1}或{1,2,1})。如果您想允许重复元素,可以删除remaining
变量,并在递归调用values
时将其替换为GetCombinations
。
请注意使用yield
关键字。这意味着代码使用延迟执行。在实际枚举结果之前,无需存储任何中间结果。
public static IEnumerable<IEnumerable<T>> GetCombinations<T>(IEnumerable<T> values, int threshold)
{
var remaining = values;
foreach (T value in values)
{
yield return value.Yield();
if (threshold < 2)
{
continue;
}
remaining = remaining.Skip(1);
foreach (var combination in GetCombinations(remaining, threshold - 1))
{
yield return value.Yield().Concat(combination);
}
}
}
public static IEnumerable<T> Yield<T>(this T item)
{
yield return item;
}
对于整数数组{1,2,3,4,5},输出为:
1
1, 2
1, 2, 3
1, 2, 4
1, 2, 5
1, 3
1, 3, 4
1, 3, 5
1, 4
1, 4, 5
1, 5
2
2, 3
2, 3, 4
2, 3, 5
2, 4
2, 4, 5
2, 5
3
3, 4
3, 4, 5
3, 5
4
4, 5
5
答案 1 :(得分:1)
假设您已经有一个解决方案来查找特定计数的组合(您已经说过了),请说明签名:
public static IEnumerable<IEnumerable<T>> Combinations<T>(
IList<T> source, int count)
然后你可以通过N次调用轻松获得所有计数的组合:
public static IEnumerable<IEnumerable<T>> Combinations<T>(IList<T> source)
{
return Enumerable.Range(0, source.Count)
.SelectMany(i => Combinations(source, i));
}
答案 2 :(得分:1)
这是一个解决方案:
public static IEnumerable<T[]> Combinations<T>(IEnumerable<T> items, int threshold)
{
var comboBuilder = new List<T>();
foreach (var combo in EnumerateCombos(items, comboBuilder, 0, threshold))
{
yield return combo;
}
}
private static IEnumerable<T[]> EnumerateCombos<T>(IEnumerable<T> items, List<T> currentCombo,
int startIndex, int threshold)
{
if (currentCombo.Count >= threshold) { yield break; }
for (int i = startIndex; i < items.Count(); i++)
{
//Skip past the items we've already gone through in the current combo:
var item = items.Skip(i).First();
//Create a new combination with the next available item:
currentCombo.Add(item);
yield return currentCombo.ToArray();
//Repeat the process with the rest of the remaining items:
foreach (var combo in EnumerateCombos(items, currentCombo, i + 1, threshold))
{
yield return combo;
}
//Recursion cleanup:
currentCombo.RemoveAt(currentCombo.Count - 1);
}
}