如果是易处理 9 个元素(或最多 25-30 个)和 5 个子集的集合,代码可以基于递归函数
static void Main(string[] args)
{
foreach (var item in ListPerm())
{
Console.WriteLine(String.Join(",", item));
}
Console.Read();
}
private static List<List<int>> ListPerm(HashSet<int> mySet = null, int deep = 5)
{
if (mySet == null)
{
mySet = initSet(8);
}
if (deep <= 0)
{
return Enumerable.Empty<List<int>>().ToList();
}
List<List<int>> all = new List<List<int>>();
for (int i = 0; i < mySet.Count - deep + 1; i++)
{
if (deep == 1)
{
var list = new List<int>() { mySet.ElementAt(i) };
all.Add(list);
}
foreach (var item in ListPerm(new HashSet<int>(mySet.Skip(i+1)), deep - 1))
{
var list = new List<int>() { mySet.ElementAt(i) };
list.AddRange(item);
all.Add(list);
}
}
return all;
}
private static HashSet<int> initSet(int lenght)
{
HashSet<int> ret = new HashSet<int>();
for (int i = 0; i < lenght; i++)
{
ret.Add(i * 1 + 1); // just an example...
};
return ret;
}
再造
现在,让我将上面的代码优化成一个更性能 的函数,在我的标准笔记本电脑上,它需要 3.2 秒来获得 30 个整数中的 8 个的组合。
private static int[][] ListPerm(int[] mySet, int deep)
{
var all = new List<int[]>();
if (deep == 1)
{
return mySet.Select(x => new int[] { x }).ToArray();
}
else
{
var mySubSet = new int[mySet.Length - 1];
Array.Copy(mySet, 1, mySubSet, 0, mySubSet.Length);
var perm1st = ListPerm(mySubSet, deep - 1);
for (int i = 0; i < mySet.Length - deep + 1; i++)
{
var permn = perm1st.Select(x =>
{
var z = new int[x.Length + 1];
z[0] = mySet[i];
x.CopyTo(z, 1);
return z;
}
);
all.AddRange(permn);
int start = Array.FindIndex(perm1st, item => item[0] != mySet[i + 1]);
if (start > 0)
{
var temp_cpy = new int[perm1st.Length - start][];
Array.Copy(perm1st, start, temp_cpy, 0, temp_cpy.Length);
perm1st = temp_cpy;
}
}
}
return all.ToArray();
}
基准测试
这里是 Ivan、my 和社区wiki 算法对 20 中 5 个 int 组合的比较。
结果
wiki 权限:00:00:00.0950055
编写 wiki 权限:00:00:00.0460026
伊万烫发:00:00:00.0400023
写伊万烫发:00:00:00.0260015
我的烫发:00:00:00.0110006
写我的烫发:00:00:00.0300017
测试代码
var input = Enumerable.Range(1, 20);
int deep = 5;
var start = DateTime.Now;
var wiki = Algorithms.Combinations(input, deep).ToArray();
Console.WriteLine("wiki perm: {0}", DateTime.Now - start);
start = DateTime.Now;
StreamWriter sw0 = new StreamWriter(@"C:\dev\SO\Algo\perm0.txt", false);
foreach (var item in wiki)
{
sw0.WriteLine(String.Join(",", item));
}
sw0.Close();
Console.WriteLine("writing wiki perm: {0}", DateTime.Now - start);
start = DateTime.Now;
start = DateTime.Now;
var result = input
.Distinct()
.ToArray()
.GetCombinations(deep)
.Select(c => (int[])c.Clone())
.ToList();
Console.WriteLine("Ivan perm: {0}", DateTime.Now - start);
start = DateTime.Now;
StreamWriter sw1 = new StreamWriter(@"C:\dev\SO\Algo\perm1.txt", false);
foreach (var item in result)
{
sw1.WriteLine(String.Join(",", item));
}
sw1.Close();
Console.WriteLine("writing Ivan perm: {0}", DateTime.Now - start);
start = DateTime.Now;
var myPerm = ListPermSO(input.ToArray(), deep);
Console.WriteLine("my perm: {0}", DateTime.Now - start);
start = DateTime.Now;
StreamWriter sw2 = new StreamWriter(@"C:\dev\SO\Algo\perm2.txt", false);
foreach (var item in myPerm)
{
sw2.WriteLine(String.Join(",", item));
}
sw2.Close();
Console.WriteLine("writing my perm: {0}", DateTime.Now - start);
Console.Read();