如果您使用 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<< input.Length) 是从0 到2^input.length 的整数列表,因此对于input 的幂集中的每个元素都包含一个整数。我将此作为查询的基础,这样我们就不必自己生成幂集(请注意,您可以将这些整数的二进制表示一对一映射到幂集:input[i] 包含在子集当且仅当相应整数中位置 i 处的位被设置)。
因此,我们可以找到包含 8 个整数的所有子集,方法是在我们的 Range 中找到所有设置了 8 位的整数(我在 SO 的某个地方找到了用于此的包含方法 NumberOfSetBits)。
此外,当i 位置的位设置为整数a 时,语句a & (1 << i) 返回1(通过向左移动1 i 步并使用逻辑AND 操作来检查)。使用它,我们获得了几个精确的 8 索引列表,它们适合我们输入数组中的一些数字。
然后我们可以继续检查每个集合的总和并将其与我们的边界进行比较(并检查总和是否为 >0 以从我们的查询结果中排除空集)。