【问题标题】:Given a set of 64 integers, I must find all 8-integer subsets whose sum <= k给定一组 64 个整数,我必须找到总和 <= k 的所有 8 整数子集
【发布时间】:2015-03-26 05:48:39
【问题描述】:

有了非常基本的动态编程经验,当涉及到这个问题时,我只能求助于网络。做我的研究,似乎thisthis 似乎在我正在寻找的范围内,但是,鉴于要求,我仍然无法理解代码的外观所有子集都有八个整数。

例如:

2678
2258
2146
2067
2026
1986
1967
1928
1844
1809
987
981
928
790
752
739
470
400
393

给定这个集合,我必须找到总和小于 10000 的八个整数的每个组合。

我对在这些论坛上发帖很陌生,所以如果我越界了,请告诉我。任何信息都有帮助! 非常感谢,非常感谢。

【问题讨论】:

  • 雪佛兰到目前为止你尝试了什么?
  • 欢迎来到 SO。首先,SO 不是论坛。这是一个关于特定编程问题的问答网站。阅读How To AskOn-TopicOff-Topic
  • 一种可能性是遵循第二个链接答案中的代码/方法。仅当集合中已有 8 个值时才停止查找(查找集合的延续)。在找到所有这些 1..8 数字的集合后,丢弃所有没有 8 个元素的集合。无论如何,在引入一些代码或详细的大纲时,SO 通常会“更好”。
  • 你知道如何生成所有大小为 8 的子集吗?其中大约有 40 亿个,所以你可以全部计算出来。然后添加一些修剪以加快速度(如果说,4 个元素的总和已经超过 10000,则添加更多数字是没有用的)。

标签: algorithm set combinations


【解决方案1】:

如果您使用 c#,您可以使用 LINQ 作为一个非常强大的工具。使用 LINQ,您实际上可以在单行查询中实现您正在寻找的内容。使用您示例中的整数,有 8 个整数的 14441 种不同组合,其中总和小于 10.000:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TestConsole
{
    class Program
    {
        static void Main(string[] args)
        {
        //Set your input parameters.

            int[] input = { 2678, 2258, 2146, 2067, 2026, 1986, 1967, 1928, 1844, 1809, 987, 981, 928, 790, 752, 739, 470, 400, 393 };
            int SubsetCount = 8;
            int MaxSum = 10000;

        //Determine all subsets that are meeting your restrictions.
            var subsets = Enumerable.Range(0, 1 << input.Length).Select(a => Enumerable.Range(0, input.Length).Where(i => Program.NumberOfSetBits(a) == SubsetCount && (a & (1 << i)) != 0).Select(i2 => input[i2])).Where(sub => sub.Sum() < MaxSum && sub.Sum() >= 1);

        //Print the result to the Console.
            foreach (IEnumerable<int> sub in subsets)
            {
                foreach (int s in sub)
                {
                    Console.Write(s.ToString() + "; ");
                }
                Console.WriteLine();
            }

            Console.ReadKey();
        }

        public static int NumberOfSetBits(int i)
        {
            i = i - ((i >> 1) & 0x55555555);
            i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
            return (((i + (i >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;
        }
    }
}

使用 LINQ 的解决方案并不像我想象的那么简单,而且确实不容易阅读。作为编程的初学者,我建议从 cmets 中建议的更程序化的方法开始:

  • 确定输入的所有子集
  • 将该列表缩减为所有大小为 8 的子集
  • 在余数中搜索总和小于 10.000 的子集

另外,我没有使用完整的 64 个整数集来测试上面的代码。如果运行时对您来说是一个问题,那么如果您自己实现查询而不是使用 LINQ,则可以进行一些非常合理的优化。 (按大小对整数进行排序 - 并考虑到下一个最高数字的差异 - 将允许您构建一个算法,该算法可以完全生成所需的结果集,并在您达到超过 10.000 限制的组合时立即停止)

编辑:

由于我未能正确格式化 LINQ 查询以获得更好的可读性,我觉得有义务提供一些关于它在做什么的指示:

Enumerable.Range(0, 1&lt;&lt; input.Length) 是从02^input.length 的整数列表,因此对于input 的幂集中的每个元素都包含一个整数。我将此作为查询的基础,这样我们就不必自己生成幂集(请注意,您可以将这些整数的二进制表示一对一映射到幂集:input[i] 包含在子集当且仅当相应整数中位置 i 处的位被设置)。

因此,我们可以找到包含 8 个整数的所有子集,方法是在我们的 Range 中找到所有设置了 8 位的整数(我在 SO 的某个地方找到了用于此的包含方法 NumberOfSetBits)。

此外,当i 位置的位设置为整数a 时,语句a &amp; (1 &lt;&lt; i) 返回1(通过向左移动1 i 步并使用逻辑AND 操作来检查)。使用它,我们获得了几个精确的 8 索引列表,它们适合我们输入数组中的一些数字。

然后我们可以继续检查每个集合的总和并将其与我们的边界进行比较(并检查总和是否为 &gt;0 以从我们的查询结果中排除空集)。

【讨论】:

    【解决方案2】:

    你说做一些研究。由于解决方案非常简单,我将给出方法,尽管这似乎是家庭作业。 代码(故意)错过了尝试所有数字的捷径。

    64! / (64 - 8)! / 8! 的候选解的数量有点大。所以减产是应该的。由于您需要所有解决方案,请按递增顺序排列 64 个整数。然后在没有从下一个位置选出的候选人的情况下开始解决。

    int[] numbers = ... 64 numbers
    Arrays.sort(numbers);
    
    int[] result = new int[8];
    solve(result, 0, 0, 0);
    
    void solve(int[] result, int resultIndex, int total, int numberIndex) {
        if (resultIndex >= 8) {
            if (total <= sum) {
                System.out.println(Arrays.toString(result));
            }
            return;
        }
        if (numberIndex >= 64) {
            return;
        }
    
        // Solve without current number
        solve(result, resultIndex, total, numberIndex + 1);
    
        // Solve with current number
        int currentNumber = numbers[numberIndex];
        result[resultIndex] = currentNumber ;
        solve(result, resultIndex + 1, total + currentNumber, numberIndex + 1);
    }
    

    所采取的方法包括尝试新的候选人(或不尝试),然后继续其余的。

    【讨论】:

    • 组合数为64! / (64-8)! / 8!
    • 这太棒了!非常感谢你。这实际上是针对我的一个涉及梦幻团队运动的个人项目。你必须选择 8 名球员,每个球员都有一个根据他们的天赋给定的薪水 - 最高薪水为 k,所以这非常有帮助!我会自己研究一下缩短数字的方法:)。
    • 比较你的启发式(=额外的优化代码):在solve-with-current-number你可以检查if (total + (8 - resultIndex) * currentNumber &gt; sum) return;
    猜你喜欢
    • 2021-05-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-04
    相关资源
    最近更新 更多