类似于子集和的算法的C/C++实现

Posted

技术标签:

【中文标题】类似于子集和的算法的C/C++实现【英文标题】:C/C++ implementation of an algorithm similar to subset sum 【发布时间】:2010-01-28 07:56:31 【问题描述】:

这个问题比knapsack(或它的一种类型,没有值,只有正权重)更简单。问题在于检查一个数字是否可以是其他数字的组合。该函数应返回truefalse

例如,

112 和带有 17, 100, 101 的列表应该返回false469 具有相同的列表应该返回true35 应该返回false119 应该返回true,等等。 ..

编辑:子集和问题比背包更准确。

【问题讨论】:

我很确定 119 应该返回 false..! 天哪……现在还很早。 7 不在列表中。如果你可以乘以不在列表中的数字,为什么不乘以 1.12 从 100 得到 112? @Pete: 7 不在列表中,但 17 是,7 乘以 17 是 119,所以 119 应该返回 true !!!!!! grrr(一巴掌) 【参考方案1】:

这是子集和问题的一个特例,集合只包含一个负数(即将 112 和 17, 100, 101 表示为 -112, 17, 100, 101 )。***页面上有一些算法,http://en.wikipedia.org/wiki/Subset_sum_problem。

【讨论】:

这仍然没有考虑到集合中数字的倍数。尽管具有倍数的子集和是适用的,因为负数可以相乘。【参考方案2】:

一个对您有帮助的观察是,如果您的列表是 a, b, c... 并且您要测试的数字是 x,那么只有当 x或者 x-a 可以写成子列表 b, c, ... 的总和。这让您可以编写一个非常简单的递归算法来解决问题。

编辑:这里有一些代码,考虑到下面的 cmets。没有经过测试,所以可能有问题;不一定是最快的。但是对于一个小数据集,它会很好地完成工作。

bool is_subset_sum(int x, std::list::const_iterator start, std::list::const_iterator end)

  // for a 1-element list a we just need to test a|x
  if (start == end) return (x % *start == 0); 

  // if x is small enough  we don't need to bother testing x - a
  if (x<a) return is_subset_sum (x, start+1, end);

  // the default case. Note that the shortcut properties of || means the process ends as soon as we get a positive.
  return (is_subset_sum (x, start+1, end) || is_subset_sum (x-a, start, end));

【讨论】:

好吧,如果它必须对 x 求和并且没有负整数,也许我可以使用带有子集的常见(零和)实现: -x, a, b, c 。 .. 啊,好的。我将这个问题解释为简单地取列表子集的总和。如果您允许正倍数,那么您需要测试 x、x-a、x-2a 等。如果您允许负倍数,那么测试当然归结为 gcd(a,b,c...) 是否进入 x。 递归步骤会稍微复杂一些:a 是解决方案的一部分,然后 x-a 可以根据 a, b, c 计算,或者 a 不是解决方案的一部分解决方案和x 可以写成 b, c 。在任何情况下,您都在减少问题的大小(较小的数字或较小的集合)。现在搜索空间可能比你想用天真的实现做的更大......【参考方案3】:

请注意,随着查询的数量变大,阳性结果会变得越来越密集。例如,所有大于 100^2 的数字都可以由 17, 100, 101 生成。所以最优算法可能取决于查询的数量是否远大于集合的成员。你可以研究场论。

至少,如果集合的最大公约数不在查询中,您至少知道结果总是错误的,并且可以在可忽略不计的时间内进行检查。

【讨论】:

【参考方案4】:

如果要到达的数字不是太大,您可能可以从集合中生成所有在 [1,N] 范围内的可到达数字。

问题:使用列表L 中的元素到达N,其中N 足够小,不必担心大小为N 元素大小的向量。

算法:

生成大小为N 的向量V 对于列表L 中的每个元素l 对于v 中的每个可达元素V 将 V 中的所有元素 v + n*l 标记为可访问

【讨论】:

我真的很喜欢这种方法,因为列表没有变化,而且最大数量也不是很大。 又称筛子。这是我想到的第一件事,只是在 CS 中“这个算法适用于小数”是不受欢迎的。不要忘记使用bitset 将上限提高一个数量级。

以上是关于类似于子集和的算法的C/C++实现的主要内容,如果未能解决你的问题,请参考以下文章

C++初识

Objective-C基础教程 与 Objective-C2.0程序设计 看那一本好?

不能被 K 整除的两个和的最大子集

在 C++ 中比较两个大型数据列表的有效算法是啥?

用C++完成贝叶斯分类算法的设计与实现

bzoj3687简单题