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)));