获得K个元素列表中K和更少元素的所有组合以及大K

时间:2016-07-18 16:00:38

标签: c# algorithm permutation

我想在列表中包含所有元素的组合,如下所示:

列表:{1,2,3}

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

我的问题是我有180个元素,我希望所有组合最多包含5个元素。我用4个元素进行测试,花了很长时间(2分钟),但一切顺利。但是有了5个元素,我的内存异常就会耗尽。

我的代码目前是:

public IEnumerable<IEnumerable<Rondin>> getPossibilites(List<Rondin> rondins)
{
    var combin5 = rondins.Combinations(5);
    var combin4 = rondins.Combinations(4);
    var combin3 = rondins.Combinations(3);
    var combin2 = rondins.Combinations(2);
    var combin1 = rondins.Combinations(1);

    return combin5.Concat(combin4).Concat(combin3).Concat(combin2).Concat(combin1).ToList();
}

随着功能:(取自这个问题:Algorithm to return all combinations of k elements from n

public static IEnumerable<IEnumerable<T>> Combinations<T>(this IEnumerable<T> elements, int k)
{
    return k == 0 ? new[] { new T[0] } :
    elements.SelectMany((e, i) =>
    elements.Skip(i + 1).Combinations(k - 1).Select(c => (new[] { e }).Concat(c)));
}

我需要在列表中搜索一个组合,其中添加的每个元素接近(具有一定精度)一个值,这对于另一个列表中的每个元素。这部分有我的所有代码:

var possibilites = getPossibilites(opt.rondins);

possibilites = possibilites.Where(p => p.Sum(r => r.longueur + traitScie) < 144);

foreach(BilleOptimisee b in opt.billesOptimisees)
{
    var proches = possibilites.Where(p => p.Sum(r => (r.longueur + traitScie)) < b.chute && Math.Abs(b.chute - p.Sum(r => r.longueur)) - (p.Count() * 0.22) < 0.01).OrderByDescending(p => p.Sum(r => r.longueur)).ElementAt(0);

    if(proches != null)
    {
        foreach (Rondin r in proches)
        {
            opt.rondins.Remove(r);
            b.rondins.Add(r);
            possibilites = possibilites.Where(p => !p.Contains(r));
        }
    }
}

使用我的代码,如何限制列表占用的内存?或者是否有更好的解决方案来搜索非常大的组合?

如果我的问题不好,请告诉我为什么,我会尽力学习并在下次提出更好的问题;)

3 个答案:

答案 0 :(得分:2)

5个元素组合的输出列表将具有〜1.5*10^9(带有b的十亿个)大小为5的子列表。如果使用32位整数,甚至忽略列表开销并假设您有一个0b开销的完美列表 - 那将是〜200GB!

如果你真的需要像你一样生成列表,你应该重新考虑,一些替代方案可能是:流式传输元素列表 - 即动态生成它们。

这可以通过创建一个函数来完成,该函数将最后一个组合作为参数 - 然后输出下一个。 (想想它是如何完成的,考虑增加一个数字。你从最后到第一个,记住“结转”直到你完成)

用于选择4中的2的流式示例:

start: {4,3}
curr = start {4, 3}
curr = next(curr) {4, 2} // reduce last by one 
curr = next(curr) {4, 1} // reduce last by one
curr = next(curr) {3, 2} // cannot reduce more, reduce the first by one, and set the follower to maximal possible value
curr = next(curr) {3, 1} // reduce last by one
curr = next(curr) {2, 1} // similar to {3,2}
done.

现在,您需要确定如何为大小为2的列表执行此操作,然后将其概括为任意大小 - 并对流式组合生成器进行编程。

祝你好运!

答案 1 :(得分:0)

让您的精度在假想光谱中定义。

使用真实索引访问叶子,然后以所需的精度遍历叶子。

请参阅PrecisLise @ http://net7mma.codeplex.com/SourceControl/latest#Common/Collections/Generic/PrecicseList.cs

虽然实施不是100%完成链接,但你可以在这里找到我使用类似概念的地方:

http://net7mma.codeplex.com/SourceControl/latest#RtspServer/MediaTypes/RFC6184Media.cs

使用这个概念,我能够以我认为非常有趣的方式重新命令h.264访问单元及其底层网络访问层组件......除了有趣之外,它还有可能使用close更高效相同数量的记忆。

等,例如,0可以按0.1或0.01或0.001进行,具体取决于列表中的键的类型(double,float,Vector,除其他外),您可能有使用FPU的额外好处或如果你的处理器支持,甚至可能是Intrinsics,因此无论底层存储机制如何,排序和索引都比普通集合更快。

使用这个概念可以获得非常有趣的排序......特别是如果你提供了一种过滤精度的机制。

我还能够使用这种方法在众多知名媒体库的比特流解析器中找到几个错误......

答案 2 :(得分:0)

我找到了我的解决方案,我在这里写这篇文章,以便与我有类似问题的其他人可以有一些合作...

我做了一个递归函数,检查了适合条件的固定数量的可能性。当找到可能性的数量时,我返回可能性列表,对结果进行一些计算,然后我可以重新启动该过程。我添加了一个计时器,以便在花费太长时间后停止研究。由于我的条件是基于元素的总和,我使用不同的值做各种可能性,并且每次都搜索少量的可能性(如1)。

因此,函数以非常高的精度返回一个可能性,我做了我需要做的事情,我删除原始列表的元素,并以相同的精度调用fontion,直到没有返回,所以我可以继续其他精度。当完成很多精度时,我的列表中只有大约30个元素,所以我可以调用所有可能性(仍然适合最大总和),这部分比开始时容易得多。

有我的代码:

public List<IEnumerable<Rondin>> getPossibilites(IEnumerable<Rondin> rondins, int nbElements, double minimum, double maximum, int instance = 0, double longueur = 0)
{
    if(instance == 0)
        timer = DateTime.Now;

    List<IEnumerable<Rondin>> liste = new List<IEnumerable<Rondin>>();

    //Get all distinct rondins that can fit into the maximal length
    foreach (Rondin r in rondins.Where(r => r.longueur < (maximum - longueur)).DistinctBy(r => r.longueur).OrderBy(r => r.longueur))
    {
        //Check the current length
        double longueur2 = longueur + r.longueur + traitScie;

        //If the current length is under the maximal length
        if (longueur2 < maximum)
        {
            //Get all the possibilities with all rondins except the current one, and add them to the list
            foreach (IEnumerable<Rondin> poss in getPossibilites(rondins.Where(rondin => rondin.id != r.id), nbElements - liste.Count, minimum, maximum, instance + 1, longueur2).Select(possibilite => possibilite.Concat(new Rondin[] { r })))
            {
                liste.Add(poss);
                if (liste.Count >= nbElements && nbElements > 0)
                    break;
            }

            //If this the current length in higher than the minimum, add it to the list
            if (longueur2 >= minimum)
                liste.Add(new Rondin[] { r });
        }

        //If we have enough possibilities, we stop the research
        if (liste.Count >= nbElements && nbElements > 0)
            break;

        //If the research is taking too long, stop the research and return the list;
        if (DateTime.Now.Subtract(timer).TotalSeconds > 30)
            break;
    }
    return liste;
}