【问题标题】:Permutations using same letters使用相同字母的排列
【发布时间】:2012-03-09 13:48:10
【问题描述】:

我目前正在从事一个项目,我需要从给定的字符集生成所有可能的排列。我目前正在使用此代码:

public static IEnumerable<string> AllPermutations(this IEnumerable<char> s)
{
    return s.SelectMany(x =>
    {
        var index = Array.IndexOf(s.ToArray(), x);
        return s.Where((y, i) => i != index).AllPermutations().Select(y => new string(new[] { x }.Concat(y).ToArray())).Union(new[] { new string(new[] { x }) });
    }).Distinct();
}

来自this 的回答。

我遇到的问题是它不会生成多次使用相同字母的排列。

例如,如果我使用abcde 作为输入,我需要它来生成aaaaadcc 等组合。

我对 LINQ 的经验不足,无法理解代码在何处停止重复字母。非常感谢任何帮助。

【问题讨论】:

  • 是否有理由在 LINQ 中这样做?
  • 我没有写这个,所以只是在寻找真正能完成这项工作的代码。
  • 'aaaaa' 不是 'abcde' 的排列。如果您的项目需要排列,请不要包含“aaaaa”,如果您包含“aaaaa”,请不要将其称为排列(或就此而言的组合)。你只会混淆阅读你问题的每个人,包括你自己。
  • 也许你需要的是不断重复的变化。
  • 我还需要任何较短的版本,从 1 个字符到字符串长度,以任意顺序使用所有输入字母。

标签: c# algorithm permutation


【解决方案1】:

可能有效,但我确信它可以更有效地完成(从 PeskyGnat 获取计数提示):

    static IEnumerable<string> GetVariations(string s)
    {
        int[] indexes = new int[s.Length];
        StringBuilder sb = new StringBuilder();

        while (IncrementIndexes(indexes, s.Length))
        {
            sb.Clear();
            for (int i = 0; i < indexes.Length; i++)
            {
                if (indexes[i] != 0)
                {
                    sb.Append(s[indexes[i]-1]);
                }
            }
            yield return sb.ToString();
        }
    }

    static bool IncrementIndexes(int[] indexes, int limit)
    {
        for (int i = 0; i < indexes.Length; i++)
        {
            indexes[i]++;
            if (indexes[i] > limit)
            {
                indexes[i] = 1;
            }
            else
            {
                return true;
            }
        }
        return false;
    }

编辑:根据 Rawlings 的建议更改为使用收益回报。如果您不需要保留所有结果,并且可以在结果全部生成之前开始使用,那么内存使用会更好。

【讨论】:

  • 这真是太棒了!我不能感谢你。 +1 并接受,谢谢!
  • 无论这是否可以更有效地完成,我确信它的效率会低很多。例如。我的回答:D
  • @Rawling:实际上我没有做的一件事是你做的是处理重复。例如,如果字符串是“cca”,我的会吐出两次“caa”,对于它认为不同的每个“c”一次。你的不这样做。
  • @Matt 哦,我的不是有意处理重复的。如果您只是切换到使用yield return 而不是List,您的实际上是我正在考虑的O(1)空间解决方案。编辑:实际上重复的事情是微不足道的,只需检查输入字符串的长度,然后在将其用作输入之前将其去除重复字符...
  • 是的,yield 可以让这个处理更大的值而没有任何内存开销,并且更接近返回 Enumerable 的原始接口
【解决方案2】:

我很惊讶这有效。它基本上是“从字符中创建一个字符串列表。然后对于从列表中取出的每个字符串,再次添加每个字符,并将生成的字符串添加到列表中。重复直到你得到正确的长度。”

public static IEnumerable<string> BuildStrings(this IEnumerable<char> alphabet)
{
    var strings = alphabet.Select(c => c.ToString());
    for (int i = 1; i < alphabet.Count(); i++)
    {
        strings = strings.Union(strings.SelectMany(s => alphabet.Select(c => s + c.ToString())));
    }
    return strings;
}

【讨论】:

  • 啊哈,它只是因为我在做Union而不是Concat,否则我会有很多很多重复...
【解决方案3】:

一个有趣的,通过一个固定点运算符仅使用递归 lambdas(感谢@Rawling for the SelectMany

// Fix point operator   
public static Func<T, TResult> Fix<T, TResult>(Func<Func<T, TResult>, Func<T, TResult>> f)
    {
        return t => f(Fix<T, TResult>(f))(t);
    }

然后

var chars = new[] {'a','b','c','d','e'}.Select(c=>c.ToString()) ;

var result = Fix<int,IEnumerable<string>>(
    f => 
      x => 
       x == 1
         ? chars
         : chars.Union(f(x - 1).SelectMany(s => chars.Select(c => s + c))))(chars.Count());

【讨论】:

    猜你喜欢
    • 2017-04-25
    • 2014-10-28
    • 2017-09-13
    • 1970-01-01
    • 2022-01-24
    • 1970-01-01
    • 2023-02-10
    • 1970-01-01
    相关资源
    最近更新 更多