【问题标题】:what is the fastest way to find the gcd of n numbers?找到n个数字的gcd的最快方法是什么?
【发布时间】:2011-02-03 11:26:34
【问题描述】:

计算 n 个数的最大公约数的最快方法是什么?

【问题讨论】:

  • 递归查找 GCD 是已知最快的方法。你想要某种特殊的优化吗?
  • @Gunner:问题是关于超过 2 个参数的 GCD。
  • 我能想到的所有方法都没有使用 gcd(a,b,c)=gcd(gcd(a,b),c) 较慢的事实。
  • 你为什么要问?使用gcd(a,b,c)=gcd(gcd(a,b),c) 是最好的方法,通常比使用例如分解要快得多。事实上,对于多项式,我们首先使用gcd 和导数来查找多次出现的因子。
  • 我可能会尝试通过计算常见的尾随零来找到 数字表示的基数的最大共同幂,然后从集合中第二小的数字中取余数按最小 - 等等,这只是从最小到最大的 GCD。嗯。寻找 Lehmer 和 为什么 矩阵乘法有帮助。

标签: algorithm math greatest-common-divisor


【解决方案1】:

没有递归:

int result = numbers[0];
for(int i = 1; i < numbers.length; i++){
    result = gcd(result, numbers[i]);
}
return result;

对于非常大的数组,使用 fork-join 模式可能会更快,您可以在其中拆分数组并并行计算 gcd。这是一些伪代码:

int calculateGCD(int[] numbers){
    if(numbers.length <= 2){
        return gcd(numbers);    
    }
    else {
        INVOKE-IN-PARALLEL {
            left = calculateGCD(extractLeftHalf(numbers));
            right = calculateGCD(extractRightHalf(numbers));
        }
        return gcd(left,right);
    }
}

【讨论】:

    【解决方案2】:

    您可能希望先对数字进行排序,然后从最小的两个数字开始递归地计算 gcd。

    【讨论】:

      【解决方案3】:

      你应该使用Lehmer's GCD algorithm

      【讨论】:

        【解决方案4】:

        C++17

        我已经编写了这个函数,通过使用 C++ 的内置 __gcd(int a, int b) 函数计算 n 个数字的 gcd。

        int gcd(vector<int> vec, int vsize)
        {
            int gcd = vec[0];
            for (int i = 1; i < vsize; i++)
            {
                gcd = __gcd(gcd, vec[i]);
            }
            return gcd;
        }
        

        要了解有关此功能的更多信息,请访问this link

        另请参阅以下链接中的Dijkstra's GCD algorithm。它可以在没有分裂的情况下工作。所以它可能会稍微快一点(如果我错了,请纠正我。)

        【讨论】:

        • (减法版本似乎是 Euclid 提出的 - 他这样做的时候已经了。与其余版本相比,速度应该取决于机器。)跨度>
        【解决方案5】:

        如何使用欧几里得算法减法:

        function getGCD(arr){
            let min = Math.min(...arr); 
            let max= Math.max(...arr);
            if(min==max){
                return min;
            }else{
                 for(let i in arr){
                    if(arr[i]>min){
                        arr[i]=arr[i]-min;
                    }
                }
                return getGCD(arr);
            }
           
        }
        
        console.log(getGCD([2,3,4,5,6]))

        上述实现需要 O(n^2) 时间。有可以实现的improvements,但我没有绕过 n 个数字尝试这些。

        【讨论】:

          【解决方案6】:

          如果您有很多 数,分解实际上可能更快。

          //Java
          int[] array = {60, 90, 45};
          int gcd = 1;
          outer: for (int d = 2; true; d += 1 + (d % 2)) {
              boolean any = false;
              do {
                  boolean all = true;
                  any = false;
                  boolean ready = true;
                  for (int i = 0; i < array.length; i++) {
                      ready &= (array[i] == 1);
                      if (array[i] % d == 0) {
                          any = true;
                          array[i] /= d;
                      } else all = false;
                  }
                  if (all) gcd *= d;
                  if (ready) break outer;
              } while (any);
          }
          System.out.println(gcd);
          

          (适用于某些示例,但未经真正测试)

          【讨论】:

            【解决方案7】:

            使用欧几里得算法

            function gcd(a, b)
            while b ≠ 0
               t := b; 
               b := a mod b; 
               a := t; 
            return a;
            

            你将它应用于前两个数字,然后是第三个数字的结果,等等......:

            read(a);
            read(b);
            
            result := gcd(a, b);
            i := 3;
            while(i <= n){
                read(a)
                result := gcd(result, a);
            }
            print(result);
            

            【讨论】:

            • 在循环中,如果结果为“1”,则可以停止循环
            【解决方案8】:

            下面是 C 程序的源代码,使用 Arrays 查找 N 个数的 HCF。

            #include<stdio.h>
            
            int main()
            {
                int n,i,gcd;
                printf("Enter how many no.s u want to find gcd : ");
                scanf("%d",&n);
                int arr[n];
                printf("\nEnter your numbers below :- \n ");
                for(i=0;i<n;i++)
                {
                    printf("\nEnter your %d number = ",i+1);
                    scanf("%d",&arr[i]);
                }
                gcd=arr[0];
                int j=1;
                while(j<n)
                {
                   if(arr[j]%gcd==0)
                   {
                       j++;
                   }
                   else
                   {
                       gcd=arr[j]%gcd;
                       i++;
                   }
                }
                printf("\nGCD of k no.s = %d ",gcd);
                return 0;
            }

            有关更多信息,请参阅此website 以获得进一步说明......

            【讨论】:

            • while 的 else 块中有错字,应该是 j++ 而不是 i++。甚至没有这条线。小心,第二个j++ 不会 100% 正常工作。例如,在数组 [3166277268, 14314056372, 3166277268, 1241634933, 11582623668, 3353406672, 4050665157, 11002134528, 14642726637, 14183632848]j++ 中,它将返回 7834365 而没有 - 74613
            【解决方案9】:

            您可以使用分而治之。要计算 gcdN([]),请将列表分为前半部分和后半部分。如果每个列表只有一个 num 。您使用 gcd2(n1, n2) 进行计算。

            我刚刚写了一个快速的示例代码。 (假设列表中的所有 num 都是正整数)

            def gcdN(nums):
                n = len(nums)
                if n == 0: return "ERROR"
                if n == 1: return nums[0]
                if n >= 2: return gcd2(gcdN(nums[:n//2]), gcdN(nums[n//2:]))
            
            def gcd2(n1, n2):
                for num in xrange(min(n1, n2), 0, -1):
                    if n1 % num == 0 and n2 % num == 0:
                        return num
            

            【讨论】:

              【解决方案10】:

              这是一个使用 gcd(a, b, c) = gcd(a, gcd(b, c)) 属性的 gcd 方法。
              它使用 BigInteger 的 gcd 方法,因为它已经过优化。

              public static BigInteger gcd(BigInteger[] parts){
                  BigInteger gcd = parts[0];
                  for(int i = 1; i < parts.length; i++)
                      gcd = parts[i].gcd(gcd);
                  return gcd;
              }
              

              【讨论】:

                【解决方案11】:
                //Recursive solution to get the GCD of Two Numbers
                
                long long int gcd(long long int a,long long int b)<br>
                {
                   return b==0 ? a : gcd(b,a%b);
                }
                int main(){
                  long long int a,b;
                  cin>>a>>b;
                  if(a>b) cout<<gcd(a,b);
                  else cout<<gcd(b,a);
                return 0;
                }
                

                【讨论】:

                  【解决方案12】:
                  import java.io.*;
                  import java.util.*;
                  import java.text.*;
                  import java.math.*;
                  import java.util.regex.*;
                  
                  class GCDArray{
                      public static int [] extractLeftHalf(int [] numbers)
                      {
                          int l =numbers.length/2;
                          int arr[] = Arrays.copyOf(numbers, l+1);
                          return arr;
                      }
                  
                      public static int [] extractRightHalf(int [] numbers)
                      {
                          int l =numbers.length/2;
                          int arr[] = Arrays.copyOfRange(numbers,l+1, numbers.length);
                          return arr;
                      }
                  
                      public static int gcd(int[] numbers)
                      {
                          if(numbers.length==1)
                              return numbers[0];
                          else {
                              int x = numbers[0];
                              int y = numbers[1];
                              while(y%x!=0)
                              {
                                  int rem = y%x;
                                  y = x;
                                  x = rem;
                              }
                              return x;
                          }
                      }
                      public static int gcd(int x,int y)
                      {
                              while(y%x!=0)
                              {
                                  int rem = y%x;
                                  y = x;
                                  x = rem;
                              }
                              return x;
                  
                      }
                      public static int calculateGCD(int[] numbers){
                          if(numbers.length <= 2){
                              return gcd(numbers);    
                          }
                          else {
                  
                                      int left = calculateGCD(extractLeftHalf(numbers));
                                      int right = calculateGCD(extractRightHalf(numbers));
                  
                              return gcd(left,right);
                          }
                      }
                      public static void main(String[] args) {
                          Scanner sc = new Scanner(System.in);
                          int n = sc.nextInt();
                          int arr[] = new int[n];
                          for(int i=0;i<n;i++){
                              arr[i]=sc.nextInt();
                          }
                          System.out.println(calculateGCD(arr));
                      }
                  }
                  

                  **

                  上面是java工作代码.....伪代码是 https://stackoverflow.com/users/7412/dogbane已经提到了

                  **

                  【讨论】:

                  • 这不是添加任何新信息,因为伪代码已经是他们的了。此外,这个问题是关于一个概念,而不是任何语言的实际实现。
                  • 你是对的......但我认为这对某人有帮助......但感谢你的评论:)
                  • 我在这里肯定看到了你的好意,但是有这样的答案意味着我们也必须对所有其他可能的语言采取类似的答案。这将很难在这里找到任何有用的信息。
                  【解决方案13】:

                  适用于任意位数的递归 JavaScript (ES6) 单行代码。

                  const gcd = (a, b, ...c) => b ? gcd(b, a % b, ...c) : c.length ? gcd(a, ...c) : Math.abs(a);
                  

                  【讨论】:

                    【解决方案14】:

                    这就是我在 Javascript 中想到的。

                    function calculateGCD(arrSize, arr) {
                        if(!arrSize)
                            return 0;
                        var n = Math.min(...arr);
                        for (let i = n; i > 0; i--) {
                            let j = 0;
                            while(j < arrSize) {
                                if(arr[j] % i === 0) {
                                    j++;
                                }else {
                                    break;
                                }
                                if(j === arrSize) {
                                    return i;
                                }
                            }
                        }
                    }
                    
                    console.log(generalizedGCD(4, [2, 6, 4, 8]));
                    // Output => 2
                    

                    【讨论】:

                    • 请添加对所需操作数量的评估。将其与预先存在的答案的 pairwise GCD 方法之一进行比较。
                    【解决方案15】:

                    这是我正在寻找的答案。 找到n个数字的gcd的最好方法确实是使用recursion.ie gcd(a,b,c)=gcd(gcd(a,b),c)。但是当我这样做时,我在某些程序中遇到了超时。

                    这里需要的优化是应该使用快速矩阵乘法算法来解决递归。

                    【讨论】:

                    • 一个接受的答案有两个反对票?这就是为什么,我有信任问题。
                    • 在 GNU MultiPrecision 中使用了一种“半 GCD”算法,据称它使用矩阵乘法:Subquadratic GCD,基于 Niels Möller,“关于 Schönhage 算法和次二次整数 GCD 计算”,在数学中计算,第 77 卷,2008 年 1 月,第 589-607 页。 (从刚才的眯眼看来,GMP似乎并没有直接支持超过两个数的GCD。)
                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2018-04-24
                    • 2011-01-14
                    • 1970-01-01
                    相关资源
                    最近更新 更多