一些数字之间最大的GCD

时间:2010-09-08 13:20:06

标签: algorithm math algebra number-theory

我们有一些非负数。我们希望找到最大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。

7 个答案:

答案 0 :(得分:5)

您可以使用欧几里德算法查找两个数字的GCD。

while (b != 0) 
{
    int m = a % b;
    a = b;
    b = m;
}
return a;

答案 1 :(得分:4)

如果你想要替代明显的算法,那么假设你的数字在一个有限的范围内,并且你有足够的内存,你可以超过O(N ^ 2)时间,N是值的数量:

  • 创建一个小整数类型的数组,将1索引到最大输入。 O(1)
  • 对于每个值,增加索引的每个元素的计数,这是数字的一个因子(确保不进行环绕)。 O(N)。
  • 从数组末尾开始,向后扫描,直到找到值> = 2. O(1)

告诉你最大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和/或预生成的素数列表。这给了我一个想法,如果你要记下因子分解,你不需要数组:

  • 创建一个空的int集合,以及最好的值1。
  • 对于每个输入整数:
    • 如果它小于或等于最佳 - 那么,继续。
    • 检查它是否在集合中。如果是这样,那么最好的那么远=最大(最好的,远远的,这个值),继续。如果不:
      • 将其添加到集合
      • 重复所有因素(大于迄今为止最好的因素)。

集合中的添加/查找可能是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中遇到它 - 在那里完成了两个数组。检查他们的社论。

  • 找到min(A)和max(A) - O(N)
    创建二进制掩码,标记A的哪些元素出现在给定范围内,用于O(1)查找; O(N)建造; O(MAX_RANGE)存储。
  • 对于范围内的每个数字a(min(A),max(A)):
    对于aa = a; aa< MAX(A); aa + = a:
    • 如果aa中的aa,则增加aa的计数器,并将其与当前的max_gcd进行比较,如果counter> = 2(即,你有两个可被aa整除的数字);
    • 为每位GCD候选人储存前两名候选人。
    • 也可以忽略小于当前max_gcd的元素;

上一个答案: 仍为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=25gcd(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_ix_i是一个整数。显而易见r_i <= g_i,因此在n gcd操作中,我们得到r_i i的上限。

上述说法不是很明显。让我们更深入地研究一下为什么它是真的:a_ia_j的gcd是a_ia_j中出现的所有素因子的乘积(根据定义)。现在,将a_j与另一个数字b相乘。 a_ib*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)中运行(如果我们没有过滤掉任何东西),但在随机数集上,我认为它会很快摆脱大块的候选人。