这个不应该太难,但我的思想似乎是堆栈溢出(huehue)。我有一系列的列表,我想找到他们可以订购的所有排列。所有列表都有不同的长度。
例如:
列表1:1
清单2:1,2
所有排列都是:
1,1
1,2
在我的情况下,我不会切换数字。 (例如2,1) 写这个最简单的方法是什么?
答案 0 :(得分:1)
我不能说以下是最简单的方法,但IMO是最有效的方式。它基本上是我对Looking at each combination in jagged array的回答的一般化版本:
public static class Algorithms
{
public static IEnumerable<T[]> GenerateCombinations<T>(this IReadOnlyList<IReadOnlyList<T>> input)
{
var result = new T[input.Count];
var indices = new int[input.Count];
for (int pos = 0, index = 0; ;)
{
for (; pos < result.Length; pos++, index = 0)
{
indices[pos] = index;
result[pos] = input[pos][index];
}
yield return result;
do
{
if (pos == 0) yield break;
index = indices[--pos] + 1;
}
while (index >= input[pos].Count);
}
}
}
您可以在链接的答案中看到解释(很快就会模拟嵌套循环)。此外,由于性能原因,它产生内部缓冲区而不克隆它,如果你想存储结果以供以后处理,你需要克隆它。
样本用法:
var list1 = new List<int> { 1 };
var list2 = new List<int> { 1, 2 };
var lists = new[] { list1, list2 };
// Non caching usage
foreach (var combination in lists.GenerateCombinations())
{
// do something with the combination
}
// Caching usage
var combinations = lists.GenerateCombinations().Select(c => c.ToList()).ToList();
更新 GenerateCombinations
是标准的C#iterator方法,实现基本上模仿N
嵌套循环(其中N
是input.Count
}像这样(伪代码):
for(int i 0 = 0; i 0 &lt; input [0] .Count; i 0 ++)
for(int i 1 = 0; i 1 &lt; input [1] .Count; i 1 ++)
for(int i 2 = 0; i 2 &lt; input [2] .Count; i 2 ++)
...
for(int i N-1 = 0; i N-1 &lt; input [N-1] .Count; i N-1 ++)
yield {input [0] [i 0 ],输入[1] [i 1 ],输入[2] [i 2 ], ...,输入[N-1] [i N-1 ]}
或以不同方式显示:
for (indices[0] = 0; indices[0] < input[0].Count; indices[0]++)
{
result[0] = input[0][indices[0]];
for (indices[1] = 0; indices[1] < input[1].Count; indices[1]++)
{
result[1] = input[1][indices[1]];
// ...
for (indices[N-1] = 0; indices[N-1] < input[N-1].Count; indices[N-1]++)
{
result[N-1] = input[N-1][indices[N-1]];
yield return result;
}
}
}
答案 1 :(得分:0)
嵌套循环:
List<int> listA = (whatever), listB = (whatever);
var answers = new List<Tuple<int,int>>;
for(int a in listA)
for(int b in listB)
answers.add(Tuple.create(a,b));
// do whatever with answers
答案 2 :(得分:0)
试试这个:
Func<IEnumerable<string>, IEnumerable<string>> combine = null;
combine = xs =>
xs.Skip(1).Any()
? xs.First().SelectMany(x => combine(xs.Skip(1)), (x, y) => String.Format("{0}{1}", x, y))
: xs.First().Select(x => x.ToString());
var strings = new [] { "AB", "12", "$%" };
foreach (var x in combine(strings))
{
Console.WriteLine(x);
}
这让我:
A1$ A1% A2$ A2% B1$ B1% B2$ B2%
答案 3 :(得分:0)
我创建了以下 IEnumerable<IEnumerable<TValue>>
类来解决这个问题,它允许使用通用 IEnumerable 并且其枚举器返回值的所有排列,每个内部列表中的一个。它可以方便地直接在 foreach 循环中使用。
这是 Michael Liu 对 IEnumerable and Recursion using yield return 的回答的变体
我已经修改了它以返回带有排列而不是单个值的列表。
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace Permutation
{
public class ListOfListsPermuter<TValue> : IEnumerable<IEnumerable<TValue>>
{
private int count;
private IEnumerable<TValue>[] listOfLists;
public ListOfListsPermuter(IEnumerable<IEnumerable<TValue>> listOfLists_)
{
if (object.ReferenceEquals(listOfLists_, null))
{
throw new ArgumentNullException(nameof(listOfLists_));
}
listOfLists =listOfLists_.ToArray();
count = listOfLists.Count();
for (int i = 0; i < count; i++)
{
if (object.ReferenceEquals(listOfLists[i], null))
{
throw new NullReferenceException(string.Format("{0}[{1}] is null.", nameof(listOfLists_), i));
}
}
}
// A variant of Michael Liu's answer in StackOverflow
// https://stackoverflow.com/questions/2055927/ienumerable-and-recursion-using-yield-return
public IEnumerator<IEnumerable<TValue>> GetEnumerator()
{
TValue[] currentList = new TValue[count];
int level = 0;
var enumerators = new Stack<IEnumerator<TValue>>();
IEnumerator<TValue> enumerator = listOfLists[level].GetEnumerator();
try
{
while (true)
{
if (enumerator.MoveNext())
{
currentList[level] = enumerator.Current;
level++;
if (level >= count)
{
level--;
yield return currentList;
}
else
{
enumerators.Push(enumerator);
enumerator = listOfLists[level].GetEnumerator();
}
}
else
{
if (level == 0)
{
yield break;
}
else
{
enumerator.Dispose();
enumerator = enumerators.Pop();
level--;
}
}
}
}
finally
{
// Clean up in case of an exception.
enumerator?.Dispose();
while (enumerators.Count > 0)
{
enumerator = enumerators.Pop();
enumerator.Dispose();
}
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}
你可以像这样直接在 foreach 中使用它:
public static void Main(string[] args)
{
var listOfLists = new List<List<string>>()
{
{ new List<string>() { "A", "B" } },
{ new List<string>() { "C", "D" } }
};
var permuter = new ListOfListsPermuter<string>(listOfLists);
foreach (IEnumerable<string> item in permuter)
{
Console.WriteLine("{ \"" + string.Join("\", \"", item) + "\" }");
}
}
输出:
{ "A", "C" }
{ "A", "D" }
{ "B", "C" }
{ "B", "D" }