C# 查看锯齿阵列中的每个组合
在一个行数未知、列数未知的锯齿状数组中,循环遍历元素的每个可能组合的最快方法是什么 这个数组C# 查看锯齿阵列中的每个组合,c#,arrays,C#,Arrays,在一个行数未知、列数未知的锯齿状数组中,循环遍历元素的每个可能组合的最快方法是什么 这个数组 char[][] myArray = new char[][]{ new char[] {'A', 'B'}, new char[] {'C', 'D'}, new char[] {'E', 'F'} }; …将返回ACE、ACF、ADE、ADF、BCE、BCF、BDE和BDF的组合 使用C实现这一点的最快方法是什么?以下是一个具有最小分配的好算法,可以避免字符串连接: pub
char[][] myArray = new char[][]{
new char[] {'A', 'B'},
new char[] {'C', 'D'},
new char[] {'E', 'F'}
};
…将返回ACE、ACF、ADE、ADF、BCE、BCF、BDE和BDF的组合
使用C实现这一点的最快方法是什么?以下是一个具有最小分配的好算法,可以避免字符串连接:
public static class Algorithms
{
public static IEnumerable<string> GetCombinations(this char[][] input)
{
var result = new char[input.Length];
var indices = new int[input.Length];
for (int pos = 0, index = 0; ;)
{
for (; pos < input.Length; pos++, index = 0)
{
indices[pos] = index;
result[pos] = input[pos][index];
}
yield return new string(result);
do
{
if (pos == 0) yield break;
index = indices[--pos] + 1;
}
while (index >= input[pos].Length);
}
}
}
基本上,它是这样一个展开的实现
from c1 in input[0]
from c2 in input[1]
...
from cN in input[N]
select new string(new [] { c1, c2, ..., cN })
如果您确实需要char[]类型的结果,只需将签名更改为
public static IEnumerable<char[]> GetCombinations(this char[][] input)
以下是完整的测试代码,以防有人感兴趣:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Threading;
namespace Samples
{
public static class Algorithms
{
public static IEnumerable<string> GetCombinationsA(this char[][] input)
{
var result = new char[input.Length];
var indices = new int[input.Length];
for (int pos = 0, index = 0; ;)
{
for (; pos < input.Length; pos++, index = 0)
{
indices[pos] = index;
result[pos] = input[pos][index];
}
yield return new string(result);
do
{
if (pos == 0) yield break;
index = indices[--pos] + 1;
}
while (index >= input[pos].Length);
}
}
public static IEnumerable<string> GetCombinationsB(this char[][] input)
{
Func<IEnumerable<IEnumerable<char>>, IEnumerable<IEnumerable<char>>> combine = null;
combine = css =>
from c in css.First()
from cs in css.Skip(1).Any()
? combine(css.Skip(1))
: new[] { Enumerable.Empty<char>() }
select new[] { c }.Concat(cs);
return combine(input).Select(c => String.Join("", c));
}
}
class Program
{
class Algorithm
{
public string Name;
public Func<char[][], IEnumerable<string>> Func;
}
static void Main(string[] args)
{
Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
Algorithm[] algorithms =
{
new Algorithm { Name = "A", Func = Algorithms.GetCombinationsA },
new Algorithm { Name = "B", Func = Algorithms.GetCombinationsB },
};
char[][] myArray = { new char[] {'A', 'B'}, new char[] {'C', 'D'}, new char[] {'E', 'F'} };
foreach (var algo in algorithms)
algo.Func(myArray);
var chars = Enumerable.Range('A', 'Z' - 'A' + 1).Select(c => (char)c).ToArray();
for (int n = 2; n < 7; n++)
{
var input = Enumerable.Range(0, n).Select(_ => chars).ToArray();
foreach (var algo in algorithms)
Test(algo, input);
Console.WriteLine();
}
Console.WriteLine("Done.");
Console.ReadLine();
}
static void Test(Algorithm algo, char[][] input)
{
GC.Collect(); GC.WaitForPendingFinalizers();
GC.Collect(); GC.WaitForPendingFinalizers();
var totalMem = GC.GetTotalMemory(false);
var timer = Stopwatch.StartNew();
long count = 0;
foreach (var comb in algo.Func(input)) count++;
timer.Stop();
totalMem = GC.GetTotalMemory(false) - totalMem;
Console.WriteLine($"{algo.Name}: N={input.Length} Count={count,12:n0} Time={timer.Elapsed} Memory={totalMem / 1024,7:n0}K");
}
}
}
这很好用:
Func<IEnumerable<IEnumerable<char>>, IEnumerable<IEnumerable<char>>> combine = null;
combine = css =>
from c in css.First()
from cs in css.Skip(1).Any()
? combine(css.Skip(1))
: new [] { Enumerable.Empty<char>() }
select new [] { c }.Concat(cs);
…然后我得到这个结果:ACE,ACF,ADE,ADF,BCE,BCF,BDE,BDF
这计算速度非常快,但了解真实世界的输入是什么会很有趣,看看这是否足够快。请定义最快。e、 g.最快的性能,最快的a.k.a.快速和肮脏的方式,等等。最快的性能。预计算机是最快的。。。performance wisemyArray.AggregateEnumerable new[]{string.Empty},e,d=>e.SelectMany_u=>d,a,b=>a+b如果您正在查找有关此问题的详细信息,请说明此问题。此计算速度非常快,但是,了解真实世界的输入是很有趣的,看看这是否足够快,看看我答案末尾的基准:@IvanStoev-是的,这很公平。问题中提供的数据运行速度非常快。这就是我所评论的。看看真实世界中的输入是什么,看看它是否仍然有效,这将是一件有趣的事情。递归lambda是一种有趣的方法,但是当像我猜想的任何递归迭代器一样添加更多级别时,性能似乎开始显著下降。使用预链SelectMany测试另一个LINQ ish解决方案会很有趣。无论如何,这只是一个非常有趣的问题域:@IvanStoev-我发现做这种事情是明智的。ToArray可以提供相当好的性能提升或惩罚。值得尝试这些组合。
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Threading;
namespace Samples
{
public static class Algorithms
{
public static IEnumerable<string> GetCombinationsA(this char[][] input)
{
var result = new char[input.Length];
var indices = new int[input.Length];
for (int pos = 0, index = 0; ;)
{
for (; pos < input.Length; pos++, index = 0)
{
indices[pos] = index;
result[pos] = input[pos][index];
}
yield return new string(result);
do
{
if (pos == 0) yield break;
index = indices[--pos] + 1;
}
while (index >= input[pos].Length);
}
}
public static IEnumerable<string> GetCombinationsB(this char[][] input)
{
Func<IEnumerable<IEnumerable<char>>, IEnumerable<IEnumerable<char>>> combine = null;
combine = css =>
from c in css.First()
from cs in css.Skip(1).Any()
? combine(css.Skip(1))
: new[] { Enumerable.Empty<char>() }
select new[] { c }.Concat(cs);
return combine(input).Select(c => String.Join("", c));
}
}
class Program
{
class Algorithm
{
public string Name;
public Func<char[][], IEnumerable<string>> Func;
}
static void Main(string[] args)
{
Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
Algorithm[] algorithms =
{
new Algorithm { Name = "A", Func = Algorithms.GetCombinationsA },
new Algorithm { Name = "B", Func = Algorithms.GetCombinationsB },
};
char[][] myArray = { new char[] {'A', 'B'}, new char[] {'C', 'D'}, new char[] {'E', 'F'} };
foreach (var algo in algorithms)
algo.Func(myArray);
var chars = Enumerable.Range('A', 'Z' - 'A' + 1).Select(c => (char)c).ToArray();
for (int n = 2; n < 7; n++)
{
var input = Enumerable.Range(0, n).Select(_ => chars).ToArray();
foreach (var algo in algorithms)
Test(algo, input);
Console.WriteLine();
}
Console.WriteLine("Done.");
Console.ReadLine();
}
static void Test(Algorithm algo, char[][] input)
{
GC.Collect(); GC.WaitForPendingFinalizers();
GC.Collect(); GC.WaitForPendingFinalizers();
var totalMem = GC.GetTotalMemory(false);
var timer = Stopwatch.StartNew();
long count = 0;
foreach (var comb in algo.Func(input)) count++;
timer.Stop();
totalMem = GC.GetTotalMemory(false) - totalMem;
Console.WriteLine($"{algo.Name}: N={input.Length} Count={count,12:n0} Time={timer.Elapsed} Memory={totalMem / 1024,7:n0}K");
}
}
}
Func<IEnumerable<IEnumerable<char>>, IEnumerable<IEnumerable<char>>> combine = null;
combine = css =>
from c in css.First()
from cs in css.Skip(1).Any()
? combine(css.Skip(1))
: new [] { Enumerable.Empty<char>() }
select new [] { c }.Concat(cs);
var result =
String.Join(
", ",
combine(myArray)
.Select(c => String.Join("", c)));