类似于子集和的算法的C/C++实现
Posted
技术标签:
【中文标题】类似于子集和的算法的C/C++实现【英文标题】:C/C++ implementation of an algorithm similar to subset sum 【发布时间】:2010-01-28 07:56:31 【问题描述】:这个问题比knapsack
(或它的一种类型,没有值,只有正权重)更简单。问题在于检查一个数字是否可以是其他数字的组合。该函数应返回true
或false
。
例如,
112 和带有 17, 100, 101
的列表应该返回false
,469
具有相同的列表应该返回true
,35
应该返回false
,119
应该返回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++实现的主要内容,如果未能解决你的问题,请参考以下文章