数组列表的 C# 排列?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/710670/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
C# Permutation of an array of arraylists?
提问by
I have an ArrayList[] myList and I am trying to create a list of all the permutations of the values in the arrays.
我有一个 ArrayList[] myList 并且我正在尝试创建一个包含数组中值的所有排列的列表。
EXAMPLE: (all values are strings)
示例:(所有值都是字符串)
myList[0] = { "1", "5", "3", "9" };
myList[1] = { "2", "3" };
myList[2] = { "93" };
The count of myList can be varied so its length is not known beforehand.
myList 的计数可以变化,因此它的长度事先是未知的。
I would like to be able to generate a list of all the permutations similar to the following (but with some additional formatting).
我希望能够生成与以下类似的所有排列的列表(但有一些额外的格式)。
1 2 93
1 3 93
5 2 93
5 3 93
3 2 93
3 3 93
9 2 93
9 3 93
Does this make sense of what I am trying to accomplish? I can't seem to come up with a good method for doing this, (if any).
这对我想要完成的事情有意义吗?我似乎无法想出一个好的方法来做到这一点(如果有的话)。
Edit:
I am not sure if recursion would interfere with my desire to format the output in my own manner. Sorry I did not mention before what my formatting was.
编辑:
我不确定递归是否会干扰我以自己的方式格式化输出的愿望。对不起,我之前没有提到我的格式是什么。
I want to end up building a string[] array of all the combinations that follows the format like below:
我想最终构建一个 string[] 数组,其中包含以下格式的所有组合:
for the "1 2 93" permutation
对于“1 2 93”排列
I want the output to be "val0=1;val1=2;val2=93;"
我希望输出为“val0=1;val1=2;val2=93;”
I will experiment with recursion for now. Thank you DrJokepu
我现在将尝试递归。谢谢Jokepu博士
回答by Elie
Non-recursive solution:
非递归解决方案:
foreach (String s1 in array1) {
foreach (String s2 in array2) {
foreach (String s3 in array3) {
String result = s1 + " " + s2 + " " + s3;
//do something with the result
}
}
}
Recursive solution:
递归解决方案:
private ArrayList<String> permute(ArrayList<ArrayList<String>> ar, int startIndex) {
if (ar.Count == 1) {
foreach(String s in ar.Value(0)) {
ar.Value(0) = "val" + startIndex + "=" + ar.Value(0);
return ar.Value(0);
}
ArrayList<String> ret = new ArrayList<String>();
ArrayList<String> tmp1 ar.Value(0);
ar.remove(0);
ArrayList<String> tmp2 = permute(ar, startIndex+1);
foreach (String s in tmp1) {
foreach (String s2 in tmp2) {
ret.Add("val" + startIndex + "=" + s + " " + s2);
}
}
return ret;
}
回答by Brian
Recursive solution
递归解
static List<string> foo(int a, List<Array> x)
{
List<string> retval= new List<string>();
if (a == x.Count)
{
retval.Add("");
return retval;
}
foreach (Object y in x[a])
{
foreach (string x2 in foo(a + 1, x))
{
retval.Add(y.ToString() + " " + x2.ToString());
}
}
return retval;
}
static void Main(string[] args)
{
List<Array> myList = new List<Array>();
myList.Add(new string[0]);
myList.Add(new string[0]);
myList.Add(new string[0]);
myList[0] = new string[]{ "1", "5", "3", "9" };
myList[1] = new string[] { "2", "3" };
myList[2] = new string[] { "93" };
foreach (string x in foo(0, myList))
{
Console.WriteLine(x);
}
Console.ReadKey();
}
Note that it would be pretty easy to return a list or array instead of a string by changing the return to be a list of lists of strings and changing the retval.add call to work with a list instead of using concatenation.
请注意,通过将返回值更改为字符串列表的列表并将 retval.add 调用更改为使用列表而不是使用串联,返回列表或数组而不是字符串将非常容易。
How it works:
这个怎么运作:
This is a classic recursive algorithm. The base case is foo(myList.Count, myList)
, which returns a List containing one element, the empty string. The permutation of a list of n string arrays s1, s2, ..., sN is equal to every member of sA1 prefixed to the permutation of n-1 string arrays, s2, ..., sN. The base case is just there to provide something for each element of sN to be concatenated to.
这是一个经典的递归算法。基本情况是foo(myList.Count, myList)
,它返回一个包含一个元素的列表,即空字符串。一个包含 n 个字符串数组 s1, s2, ..., sN 的列表的排列等于 sA1 的每个成员,前缀为 n-1 个字符串数组,s2, ..., sN 的排列。基本情况只是为要连接到的 sN 的每个元素提供一些东西。
回答by AaronLS
This will work no matter how many arrays you add to your myList:
无论您向 myList 添加多少数组,这都将起作用:
static void Main(string[] args)
{
string[][] myList = new string[3][];
myList[0] = new string[] { "1", "5", "3", "9" };
myList[1] = new string[] { "2", "3" };
myList[2] = new string[] { "93" };
List<string> permutations = new List<string>(myList[0]);
for (int i = 1; i < myList.Length; ++i)
{
permutations = RecursiveAppend(permutations, myList[i]);
}
//at this point the permutations variable contains all permutations
}
static List<string> RecursiveAppend(List<string> priorPermutations, string[] additions)
{
List<string> newPermutationsResult = new List<string>();
foreach (string priorPermutation in priorPermutations)
{
foreach (string addition in additions)
{
newPermutationsResult.Add(priorPermutation + ":" + addition);
}
}
return newPermutationsResult;
}
Note that it's not really recursive. Probably a misleading function name.
请注意,它并不是真正的递归。可能是一个误导性的函数名称。
Here is a version that adheres to your new requirements. Note the section where I output to console, this is where you can do your own formatting:
这是一个符合您的新要求的版本。请注意我输出到控制台的部分,您可以在此处进行自己的格式化:
static void Main(string[] args)
{
string[][] myList = new string[3][];
myList[0] = new string[] { "1", "5", "3", "9" };
myList[1] = new string[] { "2", "3" };
myList[2] = new string[] { "93" };
List<List<string>> permutations = new List<List<string>>();
foreach (string init in myList[0])
{
List<string> temp = new List<string>();
temp.Add(init);
permutations.Add(temp);
}
for (int i = 1; i < myList.Length; ++i)
{
permutations = RecursiveAppend(permutations, myList[i]);
}
//at this point the permutations variable contains all permutations
foreach (List<string> list in permutations)
{
foreach (string item in list)
{
Console.Write(item + ":");
}
Console.WriteLine();
}
}
static List<List<string>> RecursiveAppend(List<List<string>> priorPermutations, string[] additions)
{
List<List<string>> newPermutationsResult = new List<List<string>>();
foreach (List<string> priorPermutation in priorPermutations)
{
foreach (string addition in additions)
{
List<string> priorWithAddition = new List<string>(priorPermutation);
priorWithAddition.Add(addition);
newPermutationsResult.Add(priorWithAddition);
}
}
return newPermutationsResult;
}
回答by Neil Williams
You could use factoradicsto generate the enumeration of permutations. Try this article on MSDNfor an implementation in C#.
您可以使用factoradics来生成排列的枚举。试试MSDN上的这篇文章,以在 C# 中实现。
回答by Josh
I'm surprised nobody posted the LINQ solution.
我很惊讶没有人发布 LINQ 解决方案。
from val0 in new []{ "1", "5", "3", "9" }
from val1 in new []{ "2", "3" }
from val2 in new []{ "93" }
select String.Format("val0={0};val1={1};val2={2}", val0, val1, val2)
回答by Erwin Mayer
Here is a generic recursive function that I wrote (and an overload that may be convenient to call):
这是我编写的通用递归函数(以及可能方便调用的重载):
Public Shared Function GetCombinationsFromIEnumerables(ByRef chain() As Object, ByRef IEnumerables As IEnumerable(Of IEnumerable(Of Object))) As List(Of Object())
Dim Combinations As New List(Of Object())
If IEnumerables.Any Then
For Each v In IEnumerables.First
Combinations.AddRange(GetCombinationsFromIEnumerables(chain.Concat(New Object() {v}).ToArray, IEnumerables.Skip(1)).ToArray)
Next
Else
Combinations.Add(chain)
End If
Return Combinations
End Function
Public Shared Function GetCombinationsFromIEnumerables(ByVal ParamArray IEnumerables() As IEnumerable(Of Object)) As List(Of Object())
Return GetCombinationsFromIEnumerables(chain:=New Object() {}, IEnumerables:=IEnumerables.AsEnumerable)
End Function
And the equivalent in C#:
和 C# 中的等价物:
public static List<object[]> GetCombinationsFromIEnumerables(ref object[] chain, ref IEnumerable<IEnumerable<object>> IEnumerables)
{
List<object[]> Combinations = new List<object[]>();
if (IEnumerables.Any) {
foreach ( v in IEnumerables.First) {
Combinations.AddRange(GetCombinationsFromIEnumerables(chain.Concat(new object[] { v }).ToArray, IEnumerables.Skip(1)).ToArray);
}
} else {
Combinations.Add(chain);
}
return Combinations;
}
public static List<object[]> GetCombinationsFromIEnumerables(params IEnumerable<object>[] IEnumerables)
{
return GetCombinationsFromIEnumerables(chain = new object[], IEnumerables = IEnumerables.AsEnumerable);
}
Easy to use:
便于使用:
Dim list1 = New String() {"hello", "bonjour", "hallo", "hola"}
Dim list2 = New String() {"Erwin", "Larry", "Bill"}
Dim list3 = New String() {"!", ".."}
Dim result = MyLib.GetCombinationsFromIEnumerables(list1, list2, list3)
For Each r In result
Debug.Print(String.Join(" "c, r))
Next
or in C#:
或在 C# 中:
object list1 = new string[] {"hello","bonjour","hallo","hola"};
object list2 = new string[] {"Erwin", "Larry", "Bill"};
object list3 = new string[] {"!",".."};
object result = MyLib.GetCombinationsFromIEnumerables(list1, list2, list3);
foreach (r in result) {
Debug.Print(string.Join(' ', r));
}
回答by Chadwick
I recently ran across a similar problem in a project of mine and stumbled on this question. I needed a non-recursive solution that could work with lists of arbitrary objects. Here's what I came up with. Basically I'm forming a list of enumerators for each of the sub-lists and incrementing them iteratively.
我最近在我的一个项目中遇到了类似的问题,并偶然发现了这个问题。我需要一个可以处理任意对象列表的非递归解决方案。这是我想出的。基本上,我正在为每个子列表形成一个枚举器列表,并以迭代方式递增它们。
public static IEnumerable<IEnumerable<T>> GetPermutations<T>(IEnumerable<IEnumerable<T>> lists)
{
// Check against an empty list.
if (!lists.Any())
{
yield break;
}
// Create a list of iterators into each of the sub-lists.
List<IEnumerator<T>> iterators = new List<IEnumerator<T>>();
foreach (var list in lists)
{
var it = list.GetEnumerator();
// Ensure empty sub-lists are excluded.
if (!it.MoveNext())
{
continue;
}
iterators.Add(it);
}
bool done = false;
while (!done)
{
// Return the current state of all the iterator, this permutation.
yield return from it in iterators select it.Current;
// Move to the next permutation.
bool recurse = false;
var mainIt = iterators.GetEnumerator();
mainIt.MoveNext(); // Move to the first, succeeds; the main list is not empty.
do
{
recurse = false;
var subIt = mainIt.Current;
if (!subIt.MoveNext())
{
subIt.Reset(); // Note the sub-list must be a reset-able IEnumerable!
subIt.MoveNext(); // Move to the first, succeeds; each sub-list is not empty.
if (!mainIt.MoveNext())
{
done = true;
}
else
{
recurse = true;
}
}
}
while (recurse);
}
}
回答by J Pullar
Here is a version which uses very little code, and is entirely declarative
这是一个使用很少代码的版本,并且完全是声明性的
public static IEnumerable<IEnumerable<T>> GetPermutations<T>(IEnumerable<T> collection) where T : IComparable
{
if (!collection.Any())
{
return new List<IEnumerable<T>>() {Enumerable.Empty<T>() };
}
var sequence = collection.OrderBy(s => s).ToArray();
return sequence.SelectMany(s => GetPermutations(sequence.Where(s2 => !s2.Equals(s))).Select(sq => (new T[] {s}).Concat(sq)));
}
回答by Chris van de Steeg
class Program
{
static void Main(string[] args)
{
var listofInts = new List<List<int>>(3);
listofInts.Add(new List<int>{1, 2, 3});
listofInts.Add(new List<int> { 4,5,6 });
listofInts.Add(new List<int> { 7,8,9,10 });
var temp = CrossJoinLists(listofInts);
foreach (var l in temp)
{
foreach (var i in l)
Console.Write(i + ",");
Console.WriteLine();
}
}
private static IEnumerable<List<T>> CrossJoinLists<T>(IEnumerable<List<T>> listofObjects)
{
var result = from obj in listofObjects.First()
select new List<T> {obj};
for (var i = 1; i < listofObjects.Count(); i++)
{
var iLocal = i;
result = from obj in result
from obj2 in listofObjects.ElementAt(iLocal)
select new List<T>(obj){ obj2 };
}
return result;
}
}
回答by jamie
What you are asking for is called the Cartesian Product. Once you know what its called, there are several similar questions on Stack Overflow. They all seem to end up pointing to an answer which ended up written as a blog post:
您所要求的称为笛卡尔积。一旦你知道它叫什么,在 Stack Overflow 上有几个类似的问题。他们似乎都指向了一个最终写成博客文章的答案:
http://blogs.msdn.com/b/ericlippert/archive/2010/06/28/computing-a-cartesian-product-with-linq.aspx
http://blogs.msdn.com/b/ericlippert/archive/2010/06/28/computing-a-cartesian-product-with-linq.aspx