【问题标题】:Combination Generator in LinqLinq 中的组合生成器
【发布时间】:2010-10-20 22:35:39
【问题描述】:

是否可以创建一些 Linq 来生成包含一系列数字的所有可能组合的 List??

如果您输入“21”,它将生成一个包含元素的列表:

list[0] = "21"
list[1] = "22"
list[2] = "11"
list[3] = "12"

(不一定按这个顺序)

我知道您可以使用 range 执行以下操作:

List<char> letterRange = Enumerable.Range('a', 'z' - 'a' + 1).Select(i => (Char)i).ToList(); //97 - 122 + 1 = 26 letters/iterations

从 a-z 生成字母表。但我似乎无法转移这些知识来制作组合生成器

我已经能够用下面的代码来解决它,但是它看起来太庞大了,我相信它可以用几行来完成。我确实觉得这确实是一个糟糕的解决方案。

想象一下,如果有帮助,我已经打电话给 GetAllCombinations("4321")

public static String[] GetAllCombinations(String s)
{
    var combinations = new string[PossibleCombinations(s.Length)];

    int n = PossibleCombinations(s.Length - 1);

    for (int i = 0; i < s.Length; i++)
    {
        String sub;
        String[] subs;

        if (i == 0)
        {
            sub = s.Substring(1); //Get the first number
        }
        else if (i == s.Length - 1)
        {
            sub = s.Substring(0, s.Length - 1);
        }
        else
        {
            sub = s.Substring(0, i) + s.Substring(i + 1); 
        }

        subs = GetAllCombinations(sub);

        for (int j = 0; j < subs.Length; j++)
        {
            combinations[i * n + j] = s[i] + subs[j];
        }
    }

    return combinations;
}
public static int PossibleCombinations(int n) //Combination possibilities. e.g 1-2-3-4 have 24 different combinations
{
    int result = 1;

    for (int i = 1; i <= n; i++)
        result *= i;

    return result;
}

【问题讨论】:

    标签: c# linq combinations


    【解决方案1】:

    为了它的价值,尝试这样的事情:

    public static IEnumerable<string> GetPermutations(string s)
    {
        if (s.Length > 1)
            return from ch in s
                   from permutation in GetPermutations(s.Remove(s.IndexOf(ch), 1))
                   select string.Format("{0}{1}", ch, permutation);
    
        else
            return new string[] { s };
    }
    

    【讨论】:

    • 请注意,这个给定的函数并不能满足问题的要求。 (它生成{ "12", "21" },缺少"11""22"。)我只能假设提问者确实设法将其改编成有用的东西。
    • 如果字符串中有重复字符,此代码也不起作用。如果字符串包含“banana”,则在 for 循环中对 IndexOf('a') 的第二次调用将再次返回第一个 'a'。
    【解决方案2】:

    记录在案:乔希的回答是通用的方式:

    public static IEnumerable<IEnumerable<T>> GetPermutations<T>(IEnumerable<T> items) {
            if (items.Count() > 1) {
                return items.SelectMany(item => GetPermutations(items.Where(i => !i.Equals(item))),
                                       (item, permutation) => new[] { item }.Concat(permutation));
            } else {
                return new[] {items};
            }
        }
    

    【讨论】:

    • 请注意:如果items 中的两个或多个元素相等,则此方法无效。
    【解决方案3】:

    这是我使用 Linq 的置换和组合函数

    public static IEnumerable<TSource> Prepend<TSource>(this IEnumerable<TSource> source, TSource item)
    {
        if (source == null)
            throw new ArgumentNullException("source");
    
        yield return item;
    
        foreach (var element in source)
            yield return element;
    }
    
    public static IEnumerable<IEnumerable<TSource>> Permutate<TSource>(this IEnumerable<TSource> source)
    {
        if (source == null)
            throw new ArgumentNullException("source");
    
        var list = source.ToList();
    
        if (list.Count > 1)
            return from s in list
                    from p in Permutate(list.Take(list.IndexOf(s)).Concat(list.Skip(list.IndexOf(s) + 1)))
                    select p.Prepend(s);
    
        return new[] { list };
    }
    
    public static IEnumerable<IEnumerable<TSource>> Combinate<TSource>(this IEnumerable<TSource> source, int k)
    {
        if (source == null)
            throw new ArgumentNullException("source");
    
        var list = source.ToList();
        if (k > list.Count)
            throw new ArgumentOutOfRangeException("k");
    
        if (k == 0)
            yield return Enumerable.Empty<TSource>();
    
        foreach (var l in list)
            foreach (var c in Combinate(list.Skip(list.Count - k - 2), k - 1))
                yield return c.Prepend(l);
    }
    

    对于 DNA 字母“A”、“C”、“G”、“T”:

    var dna = new[] {'A', 'C', 'G', 'T'};
    
    foreach (var p in dna.Permutate())
        Console.WriteLine(String.Concat(p));
    

    给予

    ACGT ACTG AGCT AGTC ATCG ATGC CAGT CATG CGAT CGTA CTAG CTGA GACT GATC GCAT GCTA GTAC GTCA TACG TAGC TCAG TCGA TGAC TGCA
    

    以及 DNA 字母表的组合 (k = 2)

    foreach (var c in dna.Combinate(2))
            Console.WriteLine(String.Concat(c));
    

    AA AC AG AT CA CC CG CT GA GC GG GT TA TC TG TT
    

    【讨论】:

    • 可爱,需要组合的数量部分:D
    • 您的组合部件无法正常工作,因为它会产生相同的组合集 - 在您的示例中,您可以看到“AC”和“CA”,或者另一个示例“AG”和“GA”——您制作的内容在combinate 实际上是其他东西,称为变体。 combinate 应该将 'AC' 和 'CA' 视为同一事物,因此只返回其中一个。除此之外,我真的很喜欢研究你的代码:)
    • 您的“组合”方法执行称为“部分排列”的操作
    【解决方案4】:

    您正在寻找的实际上是排列。简而言之,排列意味着顺序是相关的(即 12 与 21 不同),而组合意味着顺序不相关(12 和 21 是等价的)。欲了解更多信息,请参阅Wikipedia.

    this thread.

    至于做是在纯 LINQ 中,这听起来像是为了使用 LINQ 而使用 LINQ。

    【讨论】:

    • 好吧,我提到了 LINQ,因为它们通常只有 1 或 2 行 -> 我想存档,因为我讨厌我巨大的方法
    【解决方案5】:

    正如其他人指出的那样,如果任何元素相同,则此页面上的解决方案将生成重复项。 Distinct() 扩展将删除它们,但它的可扩展性不是很高,因为它通常会导致整个搜索树被遍历。您将通过在遍历期间调用它来显着减少搜索空间:

    private static IEnumerable<string> Permute(string str)
    {
        if (str.Length == 0)
            yield return "";
        else foreach (var index in str.Distinct().Select(c => str.IndexOf(c)))
            foreach (var p in Permute(str.Remove(index, 1)))
                yield return str[index] + p;
    }
    

    对于示例字符串“bananabana”,这将导致访问 8,294 个节点,而不是在不进行遍历剔除时访问 9,864,101 个节点。

    【讨论】:

      【解决方案6】:

      您可以使用这个 Permute LINQ 扩展:

      foreach (var value in Enumerable.Range(1,3).Permute())
        Console.WriteLine(String.Join(",", value));
      

      结果如下:

      1,1,1
      1,1,2
      1,1,3
      1,2,1
      1,2,2
      1,2,3
      1,3,1
      1,3,2
      1,3,3
      2,1,1
      2,1,2
      2,1,3
      2,2,1
      2,2,2
      2,2,3
      2,3,1
      ...
      

      您可以选择指定排列数

      foreach (var value in Enumerable.Range(1,2).Permute(4))
        Console.WriteLine(String.Join(",", value));
      

      结果:

      1,1,1,1
      1,1,1,2
      1,1,2,1
      1,1,2,2
      1,2,1,1
      1,2,1,2
      1,2,2,1
      1,2,2,2
      2,1,1,1
      2,1,1,2
      2,1,2,1
      2,1,2,2
      2,2,1,1
      2,2,1,2
      2,2,2,1
      2,2,2,2
      

      要添加的扩展类:

      public static class IEnumerableExtensions
      {
        public static IEnumerable<IEnumerable<T>> Permute<T>(this IEnumerable<T> values) => values.SelectMany(x => Permute(new[] { new[] { x } }, values, values.Count() - 1));
        public static IEnumerable<IEnumerable<T>> Permute<T>(this IEnumerable<T> values, int permutations) => values.SelectMany(x => Permute(new[] { new[] { x } }, values, permutations - 1));
        private static IEnumerable<IEnumerable<T>> Permute<T>(IEnumerable<IEnumerable<T>> current, IEnumerable<T> values, int count) => (count == 1) ? Permute(current, values) : Permute(Permute(current, values), values, --count);
        private static IEnumerable<IEnumerable<T>> Permute<T>(IEnumerable<IEnumerable<T>> current, IEnumerable<T> values) => current.SelectMany(x => values.Select(y => x.Concat(new[] { y })));
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-08-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-03-20
        • 2021-01-02
        相关资源
        最近更新 更多