我的C#程序中有一个整数列表。但是,我只在运行时知道列表中的项目数。
让我们说,为了简单起见,我的清单是{1,2,3} 现在我需要生成所有可能的组合,如下所示。 {1,2,3} {1,2} {1,3} {2,3} {1} {2} {3}
有人可以帮忙吗?
答案 0 :(得分:65)
试试这个:
static void Main(string[] args)
{
GetCombination(new List<int> { 1, 2, 3 });
}
static void GetCombination(List<int> list)
{
double count = Math.Pow(2, list.Count);
for (int i = 1; i <= count - 1; i++)
{
string str = Convert.ToString(i, 2).PadLeft(list.Count, '0');
for (int j = 0; j < str.Length; j++)
{
if (str[j] == '1')
{
Console.Write(list[j]);
}
}
Console.WriteLine();
}
}
答案 1 :(得分:12)
以下是强类型列表的两个通用解决方案,它们将返回列表成员的所有唯一组合(如果您可以使用更简单的代码解决此问题,我向您致敬):
T-SQL
答案 2 :(得分:10)
这是使用递归的通用解决方案
public static ICollection<ICollection<T>> Permutations<T>(ICollection<T> list) {
var result = new List<ICollection<T>>();
if (list.Count == 1) { // If only one possible permutation
result.Add(list); // Add it and return it
return result;
}
foreach (var element in list) { // For each element in that list
var remainingList = new List<T>(list);
remainingList.Remove(element); // Get a list containing everything except of chosen element
foreach (var permutation in Permutations<T>(remainingList)) { // Get all possible sub-permutations
permutation.Add(element); // Add that element
result.Add(permutation);
}
}
return result;
}
我知道这是一篇旧帖子,但有人可能会觉得这很有帮助。
答案 3 :(得分:7)
这个答案使用与ojlovecd相同的算法和(对于他的迭代解决方案)jaolho。我要添加的唯一选项是过滤组合中最少数量项目的结果。这可能很有用,例如,如果您只对包含至少两个项目的组合感兴趣。
编辑:根据@ user3610374的要求,添加了最大项目数的过滤器。
编辑2:正如@stannius所建议的那样,算法已被更改,以使其在不需要所有组合的情况下更有效。
/// <summary>
/// Method to create lists containing possible combinations of an input list of items. This is
/// basically copied from code by user "jaolho" on this thread:
/// http://stackoverflow.com/questions/7802822/all-possible-combinations-of-a-list-of-values
/// </summary>
/// <typeparam name="T">type of the items on the input list</typeparam>
/// <param name="inputList">list of items</param>
/// <param name="minimumItems">minimum number of items wanted in the generated combinations,
/// if zero the empty combination is included,
/// default is one</param>
/// <param name="maximumItems">maximum number of items wanted in the generated combinations,
/// default is no maximum limit</param>
/// <returns>list of lists for possible combinations of the input items</returns>
public static List<List<T>> ItemCombinations<T>(List<T> inputList, int minimumItems = 1,
int maximumItems = int.MaxValue)
{
int nonEmptyCombinations = (int)Math.Pow(2, inputList.Count) - 1;
List<List<T>> listOfLists = new List<List<T>>(nonEmptyCombinations + 1);
// Optimize generation of empty combination, if empty combination is wanted
if (minimumItems == 0)
listOfLists.Add(new List<T>());
if (minimumItems <= 1 && maximumItems >= inputList.Count)
{
// Simple case, generate all possible non-empty combinations
for (int bitPattern = 1; bitPattern <= nonEmptyCombinations; bitPattern++)
listOfLists.Add(GenerateCombination(inputList, bitPattern));
}
else
{
// Not-so-simple case, avoid generating the unwanted combinations
for (int bitPattern = 1; bitPattern <= nonEmptyCombinations; bitPattern++)
{
int bitCount = CountBits(bitPattern);
if (bitCount >= minimumItems && bitCount <= maximumItems)
listOfLists.Add(GenerateCombination(inputList, bitPattern));
}
}
return listOfLists;
}
/// <summary>
/// Sub-method of ItemCombinations() method to generate a combination based on a bit pattern.
/// </summary>
private static List<T> GenerateCombination<T>(List<T> inputList, int bitPattern)
{
List<T> thisCombination = new List<T>(inputList.Count);
for (int j = 0; j < inputList.Count; j++)
{
if ((bitPattern >> j & 1) == 1)
thisCombination.Add(inputList[j]);
}
return thisCombination;
}
/// <summary>
/// Sub-method of ItemCombinations() method to count the bits in a bit pattern. Based on this:
/// https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetKernighan
/// </summary>
private static int CountBits(int bitPattern)
{
int numberBits = 0;
while (bitPattern != 0)
{
numberBits++;
bitPattern &= bitPattern - 1;
}
return numberBits;
}
答案 4 :(得分:3)
使用Linq和递归的另一种解决方案......
static void Main(string[] args)
{
List<List<long>> result = new List<List<long>>();
List<long> set = new List<long>() { 1, 2, 3, 4 };
GetCombination<long>(set, result);
result.Add(set);
IOrderedEnumerable<List<long>> sorted = result.OrderByDescending(s => s.Count);
sorted.ToList().ForEach(l => { l.ForEach(l1 => Console.Write(l1 + " ")); Console.WriteLine(); });
}
private static void GetCombination<T>(List<T> set, List<List<T>> result)
{
for (int i = 0; i < set.Count; i++)
{
List<T> temp = new List<T>(set.Where((s, index) => index != i));
if (temp.Count > 0 && !result.Where(l => l.Count == temp.Count).Any(l => l.SequenceEqual(temp)))
{
result.Add(temp);
GetCombination<T>(temp, result);
}
}
}
答案 5 :(得分:2)
首先,给定一组n个元素,可以计算出k个元素的所有组合(nCk)。您必须将k的值从1更改为n才能满足您的要求。
有关生成组合的C#代码,请参阅此codeproject article。
如果您有兴趣自行开发组合算法,请查看SO question,其中包含大量相关材料的链接。
答案 6 :(得分:2)
这是@ojlovecd答案的改进,无需使用字符串。
static void Main(string[] args)
{
GetCombination(new List<int> { 1, 2, 3 });
}
private static void GetCombination(List<int> list)
{
double count = Math.Pow(2, list.Count);
for (int i = 1; i <= count - 1; i++)
{
for (int j = 0; j < list.Count; j++)
{
int b = i & (1 << j);
if (b > 0)
{
Console.Write(list[j]);
}
}
Console.WriteLine();
}
}
答案 7 :(得分:1)
protected List<List<T>> AllCombos<T>(Func<List<T>, List<T>, bool> comparer, params T[] items)
{
List<List<T>> results = new List<List<T>>();
List<T> workingWith = items.ToList();
results.Add(workingWith);
items.ToList().ForEach((x) =>
{
results.Add(new List<T>() { x });
});
for (int i = 0; i < workingWith.Count(); i++)
{
T removed = workingWith[i];
workingWith.RemoveAt(i);
List<List<T>> nextResults = AllCombos2(comparer, workingWith.ToArray());
results.AddRange(nextResults);
workingWith.Insert(i, removed);
}
results = results.Where(x => x.Count > 0).ToList();
for (int i = 0; i < results.Count; i++)
{
List<T> list = results[i];
if (results.Where(x => comparer(x, list)).Count() > 1)
{
results.RemoveAt(i);
}
}
return results;
}
protected List<List<T>> AllCombos2<T>(Func<List<T>, List<T>, bool> comparer, params T[] items)
{
List<List<T>> results = new List<List<T>>();
List<T> workingWith = items.ToList();
if (workingWith.Count > 1)
{
results.Add(workingWith);
}
for (int i = 0; i < workingWith.Count(); i++)
{
T removed = workingWith[i];
workingWith.RemoveAt(i);
List<List<T>> nextResults = AllCombos2(comparer, workingWith.ToArray());
results.AddRange(nextResults);
workingWith.Insert(i, removed);
}
results = results.Where(x => x.Count > 0).ToList();
for (int i = 0; i < results.Count; i++)
{
List<T> list = results[i];
if (results.Where(x => comparer(x, list)).Count() > 1)
{
results.RemoveAt(i);
}
}
return results;
}
这对我有用,它稍微复杂一点,实际上需要一个比较器回调功能,它实际上有2个功能,不同之处在于AllCombos明确地添加了单个项目列表。这是非常原始的,绝对可以减少,但它完成了工作。欢迎任何重构建议。谢谢,
答案 8 :(得分:1)
public class CombinationGenerator{
private readonly Dictionary<int, int> currentIndexesWithLevels = new Dictionary<int, int>();
private readonly LinkedList<List<int>> _combinationsList = new LinkedList<List<int>>();
private readonly int _combinationLength;
public CombinationGenerator(int combinationLength)
{
_combinationLength = combinationLength;
}
private void InitializeLevelIndexes(List<int> list)
{
for (int i = 0; i < _combinationLength; i++)
{
currentIndexesWithLevels.Add(i+1, i);
}
}
private void UpdateCurrentIndexesForLevels(int level)
{
int index;
if (level == 1)
{
index = currentIndexesWithLevels[level];
for (int i = level; i < _combinationLength + 1; i++)
{
index = index + 1;
currentIndexesWithLevels[i] = index;
}
}
else
{
int previousLevelIndex;
for (int i = level; i < _combinationLength + 1; i++)
{
if (i > level)
{
previousLevelIndex = currentIndexesWithLevels[i - 1];
currentIndexesWithLevels[i] = previousLevelIndex + 1;
}
else
{
index = currentIndexesWithLevels[level];
currentIndexesWithLevels[i] = index + 1;
}
}
}
}
public void FindCombinations(List<int> list, int level, Stack<int> stack)
{
int currentIndex;
InitializeLevelIndexes(list);
while (true)
{
currentIndex = currentIndexesWithLevels[level];
bool levelUp = false;
for (int i = currentIndex; i < list.Count; i++)
{
if (level < _combinationLength)
{
currentIndex = currentIndexesWithLevels[level];
MoveToUpperLevel(ref level, stack, list, currentIndex);
levelUp = true;
break;
}
levelUp = false;
stack.Push(list[i]);
if (stack.Count == _combinationLength)
{
AddCombination(stack);
stack.Pop();
}
}
if (!levelUp)
{
MoveToLowerLevel(ref level, stack, list, ref currentIndex);
while (currentIndex >= list.Count - 1)
{
if (level == 1)
{
AdjustStackCountToCurrentLevel(stack, level);
currentIndex = currentIndexesWithLevels[level];
if (currentIndex >= list.Count - 1)
{
return;
}
UpdateCurrentIndexesForLevels(level);
}
else
{
MoveToLowerLevel(ref level, stack, list, ref currentIndex);
}
}
}
}
}
private void AddCombination(Stack<int> stack)
{
List<int> listNew = new List<int>();
listNew.AddRange(stack);
_combinationsList.AddLast(listNew);
}
private void MoveToUpperLevel(ref int level, Stack<int> stack, List<int> list, int index)
{
stack.Push(list[index]);
level++;
}
private void MoveToLowerLevel(ref int level, Stack<int> stack, List<int> list, ref int currentIndex)
{
if (level != 1)
{
level--;
}
AdjustStackCountToCurrentLevel(stack, level);
UpdateCurrentIndexesForLevels(level);
currentIndex = currentIndexesWithLevels[level];
}
private void AdjustStackCountToCurrentLevel(Stack<int> stack, int currentLevel)
{
while (stack.Count >= currentLevel)
{
if (stack.Count != 0)
stack.Pop();
}
}
public void PrintPermutations()
{
int count = _combinationsList.Where(perm => perm.Count() == _combinationLength).Count();
Console.WriteLine("The number of combinations is " + count);
}
}
答案 9 :(得分:0)
我们可以使用递归来处理涉及字符串或整数的组合/置换问题。
public static void Main(string[] args)
{
IntegerList = new List<int> { 1, 2, 3, 4 };
PrintAllCombination(default(int), default(int));
}
public static List<int> IntegerList { get; set; }
public static int Length { get { return IntegerList.Count; } }
public static void PrintAllCombination(int position, int prefix)
{
for (int i = position; i < Length; i++)
{
Console.WriteLine(prefix * 10 + IntegerList[i]);
PrintAllCombination(i + 1, prefix * 10 + IntegerList[i]);
}
}
答案 10 :(得分:0)
怎么样?
data['consomation_day'] = data.groupby(['day','site'])['consomation'].transform(lambda v: v.mean())
答案 11 :(得分:0)
使用C#7为Linq稍微更通用的版本。这里使用具有两个元素的项目进行过滤。
library(caret) # V6.0-79
library(recipes) # V0.1.2
library(MASS) # V7.3-47
# transform variables using recipes
rec_box <- recipe(~ ., data = as.data.frame(state.x77)) %>%
step_BoxCox(., everything()) %>%
prep(., training = as.data.frame(state.x77)) %>%
bake(., as.data.frame(state.x77))
> head(rec_box)
# A tibble: 6 x 8
Population Income Illiteracy `Life Exp` Murder `HS Grad` Frost Area
<dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 8.19 138. 0.647 60171653. 6.89 651. 20. 56.0
2 5.90 185. 0.376 61218586. 5.52 1632. 152. 106.
3 7.70 155. 0.527 66409311. 4.08 1253. 15. 69.4
4 7.65 133. 0.570 66885876. 5.05 609. 65. 56.4
5 9.96 165. 0.0936 71570875. 5.13 1445. 20. 75.5
6 7.84 161. -0.382 73188251. 3.62 1503. 166. 67.7
# transform variables using preProcess
pre_box <- preProcess(x = as.data.frame(state.x77), method = c('BoxCox')) %>%
predict(. ,newdata = as.data.frame(state.x77))
> head(pre_box)
# A tibble: 6 x 8
Population Income Illiteracy `Life Exp` Murder `HS Grad` Frost Area
<dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 8.19 118. 0.642 2383. 6.83 618. 20. 38.7
2 5.90 157. 0.374 2401. 5.47 1538. 152. 65.7
3 7.70 133. 0.524 2488. 4.05 1183. 15. 46.3
4 7.65 114. 0.566 2496. 5.01 579. 65. 38.9
5 9.96 141. 0.0935 2571. 5.09 1363. 20. 49.7
6 7.84 138. -0.383 2596. 3.60 1418. 166. 45.4
## Subtract recipe transformations from MARS::boxcox via caret::preProcess
colMeans(rec_box - pre_box)
> colMeans(rec_box - pre_box)
Population Income Illiteracy Life Exp Murder HS Grad Frost Area
0.000000e+00 2.215800e+01 2.515464e-03 6.803437e+07 2.638715e-02 5.883549e+01 0.000000e+00 1.745788e+01
答案 12 :(得分:0)
假设initail集合中的所有项目都是不同,我们可以尝试使用 Linq 进行查询;让我们概括一下解决方案:
代码:
public static IEnumerable<T[]> Permutations<T>(IEnumerable<T> source) {
if (null == source)
throw new ArgumentNullException(nameof(source));
T[] data = source.ToArray();
return Enumerable
.Range(0, 1 << (data.Length))
.Select(index => data
.Where((v, i) => (index & (1 << i)) != 0)
.ToArray());
}
演示:
var data = new char[] { 'A', 'B', 'C' };
var result = Permutations(data);
foreach (var item in result)
Console.WriteLine($"[{string.Join(", ", item)}]);
结果:
[]
[A]
[B]
[A, B]
[C]
[A, C]
[B, C]
[A, B, C]
如果您要排除初始的空数组,请放置.Range(1, (1 << (data.Length)) - 1)
而不是.Range(0, 1 << (data.Length))
答案 13 :(得分:0)
请找到一种非常简单的解决方案,无需递归并且不占用RAM。
答案 14 :(得分:0)
这就是我的做法。
public static List<List<int>> GetCombination(List<int> lst, int index, int count)
{
List<List<int>> combinations = new List<List<int>>();
List<int> comb;
if (count == 0 || index == lst.Count)
{
return null;
}
for (int i = index; i < lst.Count; i++)
{
comb = new List<int>();
comb.Add(lst.ElementAt(i));
combinations.Add(comb);
var rest = GetCombination(lst,i + 1, count - 1);
if (rest != null)
{
foreach (var item in rest)
{
combinations.Add(comb.Union(item).ToList());
}
}
}
return combinations;
}
您将其称为:
List<int> lst= new List<int>(new int[]{ 1, 2, 3, 4 });
var combinations = GetCombination(lst, 0, lst.Length)
答案 15 :(得分:0)
我只是遇到需要这样做的情况,这就是我想到的:
private static List<string> GetCombinations(List<string> elements)
{
List<string> combinations = new List<string>();
combinations.AddRange(elements);
for (int i = 0; i < elements.Count - 1; i++)
{
combinations = (from combination in combinations
join element in elements on 1 equals 1
let value = string.Join(string.Empty, $"{combination}{element}".OrderBy(c => c).Distinct())
select value).Distinct().ToList();
}
return combinations;
}
它可能不太有效,并且确实有改进的空间,但是可以完成工作!
List<string> elements = new List<string> { "1", "2", "3" };
List<string> combinations = GetCombinations(elements);
foreach (string combination in combinations)
{
System.Console.Write(combination);
}