从列表中获取总和为值的元素数组[重复]

Posted

技术标签:

【中文标题】从列表中获取总和为值的元素数组[重复]【英文标题】:Get array of elements from list that sum to value [duplicate] 【发布时间】:2021-11-20 01:02:50 【问题描述】:

下面是我的问题的简化版本,下面的背景摘要提供了更大的背景。

问题:

创建一个函数,列出列表中所有元素的总和为数组中的给定值。

鉴于:

List<int> list = new List<int>()  1, 2, 3, 4, 5, 9 ;

如果提供的值为 10,则该函数应返回一个包含列表 1、2、3、4 和 1、4、5 和 2、3、5 和 1、9 的数组。


背景:

我正在尝试将一款名为 Pastra(或 Bastra)的旧纸牌游戏数字化,目标是从棋盘上收集纸牌。您只能收集与卡片的数值相匹配的卡片或任何与所打卡片数量相加的数字卡片。 A 是 1。

我已经有了领取等值卡的代码。

我需要的是从原始列表中创建一个值数组,其中的元素总和为给定值。

我需要知道哪个列表更大,以及哪个列表包含哪些卡片,这样就不会多次收集同一张卡片。 (注意:这超出了这个问题的范围。我想自己发现这一点,但是在这里提供关于为什么我需要这种信息的上下文)。

示例:

同上,如果棋盘有 A、2、3、4、5 和 9,如果我要打 10,我可以收集 A、2、3、4 或 A、4、5 , 或 2, 3, 5, 或 Ace, 9。


感谢您的帮助,非常感谢。

【问题讨论】:

【参考方案1】:

您首先需要创建组合,然后根据总和过滤列表

给定

这基本上是一个通用方法,它使用 位掩码 来确定访问过的组合,即。一个 30 元素的数组将占用一个 30 位的数字,它会增加要生成的数字这些位的组合...对于每个位模式,它将返回原始数组的组合

注意:如果需要,这可以与longBigInteger 一起使用

public static IEnumerable<T[]> GetCombinations<T>(T[] source)

   for (var i = 0; i < (1 << source.Length); i++)
      yield return source
         .Where((t, j) => (i & (1 << j)) != 0)
         .ToArray();

过滤器

这里不多说,根据Sum过滤组合

public static IEnumerable<int[]> GetItems(IEnumerable<int> source, int target) 
   => GetCombinations(source.ToArray())
      .Where(items => items.Sum() == target);

用法

List<int> list = new List<int>()  1, 2, 3, 4, 5, 9 ;

foreach (var found in GetItems(list,10))
   Console.WriteLine(string.Join(", ", found));

输出

1, 2, 3, 4
2, 3, 5
1, 4, 5
1, 9

【讨论】:

有更有效的方法不需要预先创建所有可能的组合,因为一旦达到或超过总和,您就不需要考虑包含更多的组合数字。如果您在一组所有卡片上运行此程序,则需要查看 2^52 个组合,如果每个卡片需要 1 纳秒,则需要 52 天。 @juharr 所以 52 天是不可接受的吗? :) 无论如何,你所说的一切似乎都是合乎逻辑的,而且很容易解决。把它写成一个答案。【参考方案2】:

这是一个递归解决方案,可以找到所有正数的组合。如果一组数字包含重复的数字,它不会删除重复的组合。

IEnumerable<IReadOnlyList<int>> FindCombosThatAddToValue(IReadOnlyList<int> numbers, int value)

    var indices = new BitArray(numbers.Count);
    var combos = new List<IReadOnlyList<int>>();
    FindCombos(0, 0);
    return combos;

    void FindCombos(int index, int total)
    
        if (index >= numbers.Count)
            return;

        var n = numbers[index];
        var newTotal = total + n;

        if (newTotal == value)
        
            // this is a matching combo so lets return it
            var combo = new List<int>();

            for (int i = 0; i < index; i++)
            
                if (indices[i])
                
                    combo.Add(numbers[i]);
                
            
            
            combo.Add(n);
            combos.Add(combo);
        
        else
        
            if (newTotal < value)
            
                // try for more including this number/index
                indices.Set(index, true); // index included in total
                FindCombos(index + 1, newTotal);
            

            // try for more not including this number/index
            indices.Set(index, false); // index not included in total
            FindCombos(index + 1, total);
        
    

【讨论】:

如果newTotal &gt; value,你会想停止递归。

以上是关于从列表中获取总和为值的元素数组[重复]的主要内容,如果未能解决你的问题,请参考以下文章

问题列表

获取总和为X的数组的数字列表[重复]

如何从对象数组中获取具有属性的列表,除非它包含具有特定值的另一个项目?

根据具有不同对象的匹配字段从数组列表中删除重复元素

C语言 删除所有相同值的元素

LINQ - 从嵌套列表中获取值的总数并保存在父列表中[重复]