独特组合的算法

时间:2014-06-23 18:31:35

标签: algorithm combinations

我一直试图找到一种从嵌套在容器中的对象列表中获取唯一组合列表的方法。无法组合同一组中的对象。对象在所有组中都是唯一的

示例:

Group 1: (1,2)
Group 2: (3,4)

结果

1
2
3
4
1,3
1,4
2,3
2,4

如果我们添加其他类似的组:

Group 1: (1,2)
Group 2: (3,4)
Group 3: (5,6,7)

结果将是

1
2
3
4
5
6
7
1,3
1,4
1,5
1,6
1,7
2,3
2,4
2,5
2,6
2,7
3,5
3,6
3,7
4,5
4,6
4,7
1,3,5
1,3,6
1,3,7
1,4,5
1,4,6
1,4,7
2,3,5
2,3,6
2,3,7
2,4,5
2,4,6
2,4,7

我可能错过了上面的组合,但提到的组合应该是足够的指示。

我有可能拥有7组,每个对象有20组。

我正在努力避免让代码知道它正在进行双打,三重奏,四联等组合,但是我在这个过程中遇到了很多逻辑障碍。

要清楚,我不是要求代码,更多的是方法,伪代码或指示会很好​​。

更新 这是我看到这两个答案后的所得。

来自@ Servy的回答:

public static IEnumerable<IEnumerable<T>> GetCombinations<T>(this IEnumerable<IEnumerable<T>> sequences)
    {
        var defaultArray = new[] { default(T) };

        return sequences.Select(sequence =>
                sequence.Select(item => item).Concat(defaultArray))
            .CartesianProduct()
            .Select(sequence =>
                sequence.Where(item => !item.Equals(default(T)))
                .Select(item => item));
    }

    public static IEnumerable<IEnumerable<T>> CartesianProduct<T>(this IEnumerable<IEnumerable<T>> sequences)
    {
        IEnumerable<IEnumerable<T>> emptyProduct = new[] { Enumerable.Empty<T>() };
        return sequences.Aggregate(
            emptyProduct,
            (accumulator, sequence) =>
                from accseq in accumulator
                from item in sequence
                select accseq.Concat(new[] { item })
            );
    }

来自@ AK_的回答

public static IEnumerable<IEnumerable<T>> GetCombinations<T>(this IEnumerable<IEnumerable<T>> groups)
    {

        if (groups.Count() == 0)
        {
            yield return new T[0];
        }

        if (groups.Count() == 1)
        {
            foreach (var t in groups.First())
            {
                yield return new T[] { t };
            }
        }

        else
        {
            var furtherResult = GetCombinations(groups.Where(x => x != groups.Last()));

            foreach (var result in furtherResult)
            {
                yield return result;
            }

            foreach (var t in groups.Last())
            {
                yield return new T[] { t };

                foreach (var result in furtherResult)
                {
                    yield return result.Concat(new T[] { t });
                }
            }
        }
    }

两者的用法

List<List<int>> groups = new List<List<int>>();

        groups.Add(new List<int>() { 1, 2 });
        groups.Add(new List<int>() { 3, 4, 5 });
        groups.Add(new List<int>() { 6, 7 });
        groups.Add(new List<int>() { 8, 9 });
        groups.Add(new List<int>() { 10, 11 });


        var x = groups.GetCombinations().Where(g => g.Count() > 0).ToList().OrderBy(y => y.Count());

什么是最佳解决方案?说实话,我能够更容易地阅读@ AK_解决方案正在发生的事情(不得不寻找如何获得笛卡尔积的解决方案)。

2 个答案:

答案 0 :(得分:1)

首先考虑N序列的笛卡尔积的问题。也就是说,来自每个序列的一个值的每个单独组合。 Here is a example of an implementation of that problem, with an amazing explanation

但是我们如何处理输出组合的大小小于序列数的情况?仅处理给定序列与序列数相同的大小的情况。好吧,想象一下,每一个输入序列都有一个“空”值。该null值与来自其他序列的每个值组合(包括所有空值)配对。然后我们可以在最后删除这些空值,瞧,我们有各种大小的组合。

要做到这一点,虽然仍允许输入序列实际使用C#文字null值,或者该类型的默认值(如果它不可为空),我们需要包装该类型。我们将创建一个包装真实值的包装器,同时还拥有自己定义的def ult / null值。从那里我们将每个序列映射到一个包装器序列,将实际默认值附加到末尾,计算笛卡尔积,然后将组合映射回“实际”值,在我们处于时,过滤掉默认值它

如果您不想查看实际代码,请在此处停止阅读。




























public class Wrapper<T>
{
    public Wrapper(T value) { Value = value; }
    public static Wrapper<T> Default = new Wrapper<T>(default(T));
    public T Value { get; private set; }
}

public static IEnumerable<IEnumerable<T>> Foo<T>
    (this IEnumerable<IEnumerable<T>> sequences)
{
    return sequences.Select(sequence =>
            sequence.Select(item => new Wrapper<T>(item))
            .Concat(new[] { Wrapper<T>.Default }))
        .CartesianProduct()
        .Select(sequence => 
            sequence.Where(wrapper => wrapper != Wrapper<T>.Default)
            .Select(wrapper => wrapper.Value));
}

答案 1 :(得分:1)

在C#中

这实际上是一个单子......我想......

IEnumerable<IEnumerable<int>> foo (IEnumerable<IEnumerable<int>> groups)
{

    if (groups.Count == 0)
    {
        return new List<List<int>>();
    }

    if (groups.Count == 1)
    {
        foreach(van num in groups.First())
        {
            return yield new List<int>(){num};
        }
    }

    else
    {
        var furtherResult = foo(groups.Where(x=> x != groups.First()));

        foreach (var result in furtherResult)
        {
            yield  return result;
        }

        foreach(van num in groups.First())
        {
            yield return new List<int>(){num};

            foreach (var result in furtherResult)
            {
                yield return result.Concat(num);
            }
        }
    }
}

更好的版本:

public static IEnumerable<IEnumerable<T>> foo<T> (IEnumerable<IEnumerable<T>> groups)
    {
        if (groups.Count() == 0)
        {
            return new List<List<T>>();
        }

        else
        {
            var firstGroup = groups.First();

            var furtherResult = foo(groups.Skip(1));

            IEnumerable<IEnumerable<T>> myResult =  from x in firstGroup
                select new [] {x};

            myResult = myResult.Concat(  from x      in firstGroup 
                                       from result in furtherResult
                                       select result.Concat(new T[]{x}));

            myResult = myResult.Concat(furtherResult);

            return myResult;
        }
    }