数组列表数组的C#排列?
Posted
技术标签:
【中文标题】数组列表数组的C#排列?【英文标题】:C# Permutation of an array of arraylists? 【发布时间】:2010-10-17 04:13:45 【问题描述】:我有一个 ArrayList[] myList,我正在尝试创建一个包含数组中所有值排列的列表。
示例:(所有值都是字符串)
myList[0] = "1", "5", "3", "9" ;
myList[1] = "2", "3" ;
myList[2] = "93" ;
myList 的计数可以变化,因此事先不知道其长度。
我希望能够生成类似于以下所有排列的列表(但带有一些额外的格式)。
1 2 93
1 3 93
5 2 93
5 3 93
3 2 93
3 3 93
9 2 93
9 3 93
这对我想要完成的事情有意义吗?我似乎想不出一个好的方法来做到这一点,(如果有的话)。
编辑: 我不确定递归是否会干扰我以自己的方式格式化输出的愿望。抱歉,我之前没有提到我的格式是什么。
我想最终构建一个 string[] 数组,其中包含以下格式的所有组合:
对于“1 2 93”排列
我希望输出为“val0=1;val1=2;val2=93;”
我现在将尝试递归。谢谢DrJokepu
【问题讨论】:
我现在没时间给出详细的答案,但是考虑用递归来做吧。 我在回答中做了一个补充,以符合您能够自行格式化的要求。 这与排列无关。你只想要所有的组合。 @Guffa":这是一种需要编辑的更正。但不是我,因为我对它没有足够的信心。 从技术上讲,您要求的是笛卡尔积,而不是排列或组合。 【参考方案1】:我很惊讶没有人发布 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)
【讨论】:
【参考方案2】:递归解
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();
请注意,通过将返回更改为字符串列表的列表并将 retval.add 调用更改为使用列表而不是使用连接,返回列表或数组而不是字符串将非常容易。
它是如何工作的:
这是一个经典的递归算法。基本情况是foo(myList.Count, myList)
,它返回一个包含一个元素的列表,即空字符串。 n 个字符串数组 s1, s2, ..., sN 的列表的排列等于 sA1 的每个成员都以 n-1 个字符串数组 s2, ..., sN 的排列为前缀。基本情况只是为要连接的 sN 的每个元素提供一些东西。
【讨论】:
是的,这段代码很难看。但它有效。在生产中使用它之前我会清理它。 你是怎么写得这么快的……当被问到问题时,你是从头开始写的吗? Wadih:是的,我是从零开始写的。这就是它如此丑陋的原因。 良好的递归思维技巧【参考方案3】:我最近在我的一个项目中遇到了类似的问题,偶然发现了这个问题。我需要一个可以处理任意对象列表的非递归解决方案。这就是我想出的。基本上我正在为每个子列表形成一个枚举器列表并迭代地递增它们。
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);
【讨论】:
这种方法非常适合我的目的(具有大量对象的众多列表的数百万到数万亿个排列)。我通过应用 Distinct() 和 Jon Skeet 的 DistinctBy() 检查了结果的有效性。我在此方法中添加了 this 关键字,使其成为扩展方法。【参考方案4】:您可以使用factoradics 来生成排列的枚举。尝试this article on MSDN 在 C# 中实现。
【讨论】:
【参考方案5】:无论您将多少个数组添加到 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;
请注意,它并不是真正的递归。可能是一个误导性的函数名称。
这是一个符合您的新要求的版本。请注意我输出到控制台的部分,您可以在此处进行自己的格式化:
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;
【讨论】:
我讨厌代码问题,因为你可以满足他们的要求,但仍然不能满足他们。 谢谢,这正是我想要的!【参考方案6】:您所要求的称为笛卡尔积。一旦你知道它叫什么,Stack Overflow 上有几个类似的问题。他们似乎都指向了一个最终写成博客文章的答案:
http://blogs.msdn.com/b/ericlippert/archive/2010/06/28/computing-a-cartesian-product-with-linq.aspx
【讨论】:
【参考方案7】:非递归解:
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
递归解法:
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;
【讨论】:
真的。我具体回答了这个问题,而不是笼统地回答。我想这会留下递归。 基于 TaRDy 的声明“myList 的计数可以变化,因此事先不知道它的长度。”,这并不能真正解决他的问题。但是,它适用于 count=3。 对,嵌套循环解决方案很简单。我正在尝试提出一个递归解决方案,因为该算法可以被视为一棵树.. 我看到你添加了一个递归版本:)【参考方案8】:这是我编写的一个通用递归函数(以及一个可能方便调用的重载):
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
和 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);
易于使用:
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
或在 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));
【讨论】:
【参考方案9】:这是一个使用很少代码的版本,完全是声明性的
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)));
【讨论】:
注意:这会生成不重复的排列。【参考方案10】: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;
【讨论】:
【参考方案11】:这是一个非递归、非 Linq 的解决方案。我不禁觉得我可以减少循环并用除法和模数计算位置,但不能完全理解这一点。
static void Main(string[] args)
//build test list
List<string[]> myList = new List<string[]>();
myList.Add(new string[0]);
myList.Add(new string[0]);
myList.Add(new string[0]);
myList[0] = new string[] "1", "2", "3";
myList[1] = new string[] "4", "5" ;
myList[2] = new string[] "7", "8", "9" ;
object[][] xProds = GetProducts(myList.ToArray());
foreach(object[] os in xProds)
foreach(object o in os)
Console.Write(o.ToString() + " ");
Console.WriteLine();
Console.ReadKey();
static object[][] GetProducts(object[][] jaggedArray)
int numLists = jaggedArray.Length;
int nProducts = 1;
foreach (object[] oArray in jaggedArray)
nProducts *= oArray.Length;
object[][] productAry = new object[nProducts][];//holds the results
int[] listIdxArray = new int[numLists];
listIdxArray.Initialize();
int listPtr = 0;//point to current list
for(int rowcounter = 0; rowcounter < nProducts; rowcounter++)
//create a result row
object[] prodRow = new object[numLists];
//get values for each column
for(int i=0;i<numLists;i++)
prodRow[i] = jaggedArray[i][listIdxArray[i]];
productAry[rowcounter] = prodRow;
//move the list pointer
//possible states
// 1) in a list, has room to move down
// 2) at bottom of list, can move to next list
// 3) at bottom of list, no more lists left
//in a list, can move down
if (listIdxArray[listPtr] < (jaggedArray[listPtr].Length - 1))
listIdxArray[listPtr]++;
else
//can move to next column?
//move the pointer over until we find a list, or run out of room
while (listPtr < numLists && listIdxArray[listPtr] >= (jaggedArray[listPtr].Length - 1))
listPtr++;
if (listPtr < listIdxArray.Length && listIdxArray[listPtr] < (jaggedArray[listPtr].Length - 1))
//zero out the previous stuff
for (int k = 0; k < listPtr; k++)
listIdxArray[k] = 0;
listIdxArray[listPtr]++;
listPtr = 0;
return productAry;
【讨论】:
【参考方案12】:当我为大量代码执行此操作时遇到的一个问题是,在给出的示例 brian 中,我实际上内存不足。为了解决这个问题,我使用了以下代码。
static void foo(string s, List<Array> x, int a)
if (a == x.Count)
// output here
Console.WriteLine(s);
else
foreach (object y in x[a])
foo(s + y.ToString(), x, a + 1);
static void Main(string[] args)
List<Array> a = new List<Array>();
a.Add(new string[0]);
a.Add(new string[0]);
a.Add(new string[0]);
a[0] = new string[] "T", "Z" ;
a[1] = new string[] "N", "Z" ;
a[2] = new string[] "3", "2", "Z" ;
foo("", a, 0);
Console.Read();
【讨论】:
【参考方案13】:private static void GetP(List<List<string>> conditions, List<List<string>> combinations, List<string> conditionCombo, List<string> previousT, int selectCnt)
for (int i = 0; i < conditions.Count(); i++)
List<string> oneField = conditions[i];
for (int k = 0; k < oneField.Count(); k++)
List<string> t = new List<string>(conditionCombo);
t.AddRange(previousT);
t.Add(oneField[k]);
if (selectCnt == t.Count )
combinations.Add(t);
continue;
GetP(conditions.GetRange(i + 1, conditions.Count - 1 - i), combinations, conditionCombo, t, selectCnt);
List<List<string>> a = new List<List<string>>();
a.Add(new List<string> "1", "5", "3", "9" );
a.Add(new List<string> "2", "3" );
a.Add(new List<string> "93" );
List<List<string>> result = new List<List<string>>();
GetP(a, result, new List<string>(), new List<string>(), a.Count);
另一个递归函数。
【讨论】:
以上是关于数组列表数组的C#排列?的主要内容,如果未能解决你的问题,请参考以下文章