我们有一些非负数。我们希望找到最大gcd对。实际上这个最大值比这对更重要! 例如,如果我们有:
2 4 5 15
GCD(2,4)= 2
GCD(2,5)= 1
GCD(2,15)= 1
GCD(4,5)= 1
GCD(4,15)= 1
GCD(5,15)= 5
答案是5。
答案 0 :(得分:5)
您可以使用欧几里德算法查找两个数字的GCD。
while (b != 0)
{
int m = a % b;
a = b;
b = m;
}
return a;
答案 1 :(得分:4)
如果你想要替代明显的算法,那么假设你的数字在一个有限的范围内,并且你有足够的内存,你可以超过O(N ^ 2)时间,N是值的数量:
告诉你最大gcd,但不告诉你哪一对产生了它。对于您的示例输入,计算出的数组如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
4 2 1 1 2 0 0 0 0 0 0 0 0 0 1
我不知道这对你必须处理的输入来说是否真的更快。所涉及的常数因素很大:您的值的界限以及在该界限内分解值的时间。
您不必将每个值分解 - 您可以使用memoisation和/或预生成的素数列表。这给了我一个想法,如果你要记下因子分解,你不需要数组:
集合中的添加/查找可能是O(日志N),但它取决于您使用的数据结构。每个值都有O(f(k))因子,其中k是最大值,我不记得函数f是什么...
您在集合中遇到某个值后立即获得值的原因是您找到的数字是两个输入值的公因子。如果你保持因子分解,你只能找到较小的这样的数字,这些数字并不有趣。
我不太确定最重要的方法是重复哪些因素。我认为在实践中你可能需要取得平衡:你不想按降序顺序完成它们,因为生成有序因子很尴尬,但你也不想真正找到所有因素。
即使在O(N ^ 2)的领域,你也许能够击败欧几里得算法的使用:
将每个数字完全分解,将其存储为素数的指数序列(例如,2为{1},4为{2},5为{0,0,1},15为{0,1, 1})。然后你可以通过获取每个索引的最小值并将它们相乘来计算gcd(a,b)。不知道这是否比欧几里德平均快,但可能是。显然它使用了更多的内存。
答案 2 :(得分:3)
我能想到的优化是
1)从两个最大的数字开始,因为它们可能具有最多的素因子,因此可能具有最共享的素数因子(因此也是最高的GCD)。
2)当计算其他对的GCD时,如果低于当前最大GCD,则可以停止欧几里德算法循环。
在我的脑海中,我无法想到一种方法,你可以计算出一对中最大的GCD而不试图单独计算每一对(并如上所述进行优化)。
免责声明:我之前从未考虑过这个问题,而且上面的内容已经脱颖而出。可能有更好的方法,我可能是错的。如果有人想要,我很乐意更详细地讨论我的想法。 :)
答案 3 :(得分:1)
一般来说,这个问题没有O(n log n)
解决方案。事实上,最糟糕的情况是列表中的项目数为O(n^2)
。考虑以下一组数字:
2^20 3^13 5^9 7^2*11^4 7^4*11^3
只有前两个的GCD大于1,但从查看GCD知道这一点的唯一方法是尝试每一对并注意其中一个大于1。
因此,当你已经找到一个大的GCD(同时确保你没有)时,你可能会坚持使用无聊的暴力尝试每对方法,或者通过一些巧妙的优化来避免做不必要的工作什么都没有。)
答案 4 :(得分:1)
通过一些约束,例如数组中的数字在给定范围内,比如1-1e7,它在O(NlogN)/ O(MAX * logMAX)中是可行的,其中MAX是A中的最大可能值。
受筛选算法的启发,并在Hackerrank Challenge中遇到它 - 在那里完成了两个数组。检查他们的社论。
上一个答案: 仍为O(N ^ 2) - 对数组进行排序;应该消除一些不必要的比较;
max_gcd = 1
# assuming you want pairs of distinct elements.
sort(a) # assume in place
for ii = n - 1: -1 : 0 do
if a[ii] <= max_gcd
break
for jj = ii - 1 : -1 :0 do
if a[jj] <= max_gcd
break
current_gcd = GCD(a[ii], a[jj])
if current_gcd > max_gcd:
max_gcd = current_gcd
这应该可以节省一些不必要的计算。
答案 5 :(得分:0)
伪代码
function getGcdMax(array[])
arrayUB=upperbound(array)
if (arrayUB<1)
error
pointerA=0
pointerB=1
gcdMax=0
do
gcdMax=MAX(gcdMax,gcd(array[pointera],array[pointerb]))
pointerB++
if (pointerB>arrayUB)
pointerA++
pointerB=pointerA+1
until (pointerB>arrayUB)
return gcdMax
答案 6 :(得分:0)
有一个解决方案需要O(n):
让我们的数字为a_i
。首先,计算m=a_0*a_1*a_2*...
。对于每个数字a_i,计算gcd(m/a_i, a_i)
。您要查找的数字是这些值的最大值。
我没有证明这一直是真的,但在你的例子中,它有效:
m=2*4*5*15=600,
max(gcd(m/2,2), gcd(m/4,4), gcd(m/5,5), gcd(m/15,15))=max(2, 2, 5, 5)=5
注意:这不正确。如果数字a_i
的因子p_j
重复两次,而另外两个数字也包含此因素p_j
,那么您会得到错误的结果p_j^2
p_j
{} 1}}。例如,对于集合3, 5, 15, 25
,您将25
作为答案,而不是5
。
但是,您仍然可以使用此功能快速过滤掉数字。例如,在上述情况下,一旦确定25,您就可以首先使用a_3=25
对gcd(a_3, a_i)
进行详尽搜索,以找到实际最大值5
,然后过滤掉gcd(m/a_i, a_i), i!=3
1}}小于或等于5
(在上面的示例中,这会过滤掉所有其他内容)。
已添加以供澄清和说明:
要了解其原因,请注意gcd(a_i, a_j)
为所有gcd(m/a_i, a_i)
划分j!=i
。
我们将gcd(m/a_i, a_i)
称为g_i
,将max(gcd(a_i, a_j),j=1..n, j!=i)
称为r_i
。我上面说的是g_i=x_i*r_i
,x_i
是一个整数。显而易见r_i <= g_i
,因此在n
gcd操作中,我们得到r_i
i
的上限。
上述说法不是很明显。让我们更深入地研究一下为什么它是真的:a_i
和a_j
的gcd是a_i
和a_j
中出现的所有素因子的乘积(根据定义)。现在,将a_j
与另一个数字b
相乘。 a_i
和b*a_j
的gcd要么等于gcd(a_i, a_j)
,要么是它的倍数,因为b*a_j
包含a_j
的所有素因子,有些由b
贡献的更多素因子,也可能包含在a_i
的因子分解中。事实上,我认为gcd(a_i, b*a_j)=gcd(a_i/gcd(a_i, a_j), b)*gcd(a_i, a_j)
。但我看不出有办法利用这个。 :)
无论如何,在我们的构造中,m/a_i
只是计算所有a_j
的产品的捷径,其中j=1..1, j!=i
。因此,gcd(m/a_i, a_i)
包含所有gcd(a_i, a_j)
因素。因此,显然,这些单个gcd结果的最大值将除以g_i
。
现在,我们特别感兴趣的是最大的g_i
:它是最大的gcd本身(如果x_i
是1),或者是一个好的候选者。为此,我们执行另一个n-1
gcd操作,并明确计算r_i
。然后,我们将所有g_j
小于或等于r_i
作为候选人。如果我们没有剩下任何其他候选人,我们就完成了。如果没有,我们会选择下一个g_k
,并计算r_k
。如果r_k <= r_i
,我们会删除g_k
,并重复另一个g_k'
。如果r_k > r_i
,我们会过滤掉剩余的g_j <= r_k
,然后重复。
我认为有可能构造一个数字集,使这个算法在O(n ^ 2)中运行(如果我们没有过滤掉任何东西),但在随机数集上,我认为它会很快摆脱大块的候选人。