Algorithm 某些数字之间的最大GCD

Algorithm 某些数字之间的最大GCD,algorithm,math,algebra,number-theory,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。您可以使用欧几里德算法来找到两个数字的GCD while (b != 0) { int m = a % b; a = b; b = m; } return a; 伪码 function getGcdMax(a

我们有一些非负数。我们想找到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。

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

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

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
函数getGcdMax(数组[]) arrayUB=上限(数组) 如果(arrayUBarrayUB) 波因特拉++ pointerB=pointerA+1 直到(指针B>arrayUB) 返回gcdMax
我能想到的优化是

1) 从两个最大的数字开始,因为它们可能具有最多的基本因子,因此可能具有最多的共享基本因子(因此GCD最高)

2) 当计算其他对的GCD时,如果低于当前最大GCD,可以停止欧几里德算法循环

在我的脑海中,我想不出一种方法,你可以计算出一对中最好的GCD,而不试着单独计算出每一对(并像上面一样优化一点)

免责声明:我以前从未考虑过这个问题,上面的问题我都不知道。也许有更好的办法,但我可能错了。如果有人愿意,我很乐意更详细地讨论我的想法。:)

一般来说,这个问题没有
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时做不必要的工作(同时确保你不会错过任何东西)。

如果你想要一个明显算法的替代方案,那么假设你的数字在一个有界的范围内,如果你有足够的内存,你可以比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
我不知道对于你必须处理的输入来说,这是否真的更快。涉及的常量因素很大:值的边界以及在该边界内分解值的时间

您不必对每个值进行因式分解-您可以使用备忘录和/或预生成的素数列表。这让我想到,如果你在记忆因子分解,你不需要数组:

  • 创建一个int的空集,以及迄今为止的最佳值1
  • 对于每个输入整数:
    • 如果低于或等于目前为止的最佳值,请继续
    • 检查它是否在集合中。如果是,则至今最佳=最大值(至今最佳,此值),继续。如果没有:
      • 将其添加到集合中
      • 对其所有因子重复此操作(大于目前为止的最佳值)
集合中的添加/查找可以是O(logn),尽管它取决于您使用的数据结构。每个值都有O(f(k))因子,其中k是最大值,我记不起函数f是什么

当您在集合中遇到一个值时,您就可以使用该值了,这是因为您找到了一个数字,它是两个输入值的公因数。如果你继续因子分解,你只会发现更小的这样的数字,这并不有趣

对于更大的因素,我不太确定重复的最佳方法是什么。我认为在实践中,你可能需要找到一个平衡点:你不想按降序进行,因为生成有序因子很难,但你也不想找到所有的因子

即使在O(N^2)的领域中,您也可能比使用欧几里德算法更好:


完全分解每个数字,将其存储为素数指数序列(例如2为{1},4为{2},5为{0,0,1},15为{0,1,1})。然后,你可以计算gcd(a,b),方法是取每个指数的最小值,再乘以它们。不知道这是否比欧几里得平均速度快,但它可能是。显然,它会占用更多内存。

有一种解决方案需要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
。例如,对于集合
3,5,15,25
,您得到的答案是
25
,而不是
5

但是,您仍然可以使用它快速过滤出数字。例如,在上述情况下,一旦确定25,您可以首先使用
gcd(a_3,a_i)
a_3=25
进行彻底搜索,以找到真正的最大值,
5
,然后过滤掉
gcd(m/a_i,a_i),i=小于或等于
5
的3
(在上面的示例中,这会过滤掉所有其他)


增加
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