给定一个长度为N的非零整数数组。写一个返回数组最大元素的函数,该元素是同一数组中某个其他元素的除数。如果此号码不存在,则返回0
。我知道如何在O(n^2)
中解决。是否可以更快地完成它?
答案 0 :(得分:2)
首先,请注意您假设测试整数A除以整数B可以在O(1)中完成。我猜您也假设不允许预先计算(例如构建divisibility graph)。
由于integer factorization(不知道多项式算法)不是一个选项,因此你不能比O(n ^ 2)(最差情况)更快。
例如,给定输入{11,127,16139}(所有整数都是素数,每个整数的平方小于下一个),你就不能检查所有对。
答案 1 :(得分:1)
我一直在玩你的问题一段时间,发现了一个比蛮力更强的解决方案。
它基于创意:
我们可以按顺序执行搜索,以便首先测试更大的除数候选。这样我们就可以在找到除数后立即终止搜索。
测试某个候选divw
是否为w
的除数的一种方法是计算r = floor(w / divw)
,然后检查r * divw == w
。有趣的是,当它失败时,我们可以将w
的下一个除数候选者的上限计算为topw = floor(w / (r + 1))
。因此,我们可以放弃divw
和topw
之间的任何内容。
第二点的示例:想象一下,我们正在测试divw = 10
是w = 12
的除数,我们计算r = floor(12 / 10) = 1
和topw = floor(w / 2) = 6
。因此,我们无需检查7
和9
之间的集合中的数字是否为12
的除数。
为了实现这个算法,我使用了一个堆来保存集合中的数字,使用必须进行测试的下一个除数候选作为关键字。
因此...
初始化堆,将其前身的每个元素作为其较大的潜在除数。
弹出堆中的第一个元素(w
)并检查潜在的除数候选(divw
)是否实际上是除数。
如果是,请将其作为最大的除数
为topw
,w
计算divw
;搜索集合divw'
中等于或小于topw
的下一个元素(使用二进制搜索);如果找到,请再次在队列中推送w
,divw'
。
除非队列为空,请转到2
。
Common Lisp中的实现可用here!
我想计算这个算法的理论计算成本会很有挑战性,特别是对于普通情况,所以我不打算这样做!
在运行十几次之后,当N为高并且数字分散时(这意味着一个数字是另一个数的除数的概率很低),它似乎比蛮力方法表现得更好。另一方面,当N为低或数字密集分布在一个小范围内时,蛮力似乎更快(这意味着数字的概率是另一个的除数很高)。
答案 2 :(得分:0)
我这样做了
int f(int* a, int size)
{
int max = 0;
for (int i = 0; i < size; i++)
for (int j = 0; j < size; j++)
if (a[i] > a[j] && a[i] % a[j] == 0 && a[j] > max)
max = a[j];
return max;
}