【问题标题】:Finding common prime divisors in two sets of numbers quickly快速找到两组数字的公约数
【发布时间】:2015-12-13 13:37:49
【问题描述】:

我一直在尝试破解这个问题:https://codility.com/programmers/task/common_prime_divisors/

我让它在返回正确答案方面发挥作用,但对于更大的数字来说它非常慢,我想看看是否有人更好地更快地完成它或解释我可以优化它的方法。

bool IsPrime(int number)
{
    for (int i = 2; i < number; i++)
    {
        if (number % i == 0)
        {
            return false;
        }
    }

    return true;    
}

bool GetPrimeFactors(int valueA, int valueB)
{
    if(valueA < 0 || valueB < 0)
        return false;

    int max = sqrt(std::max(valueA, valueB)) + 1;//sqrt(std::max(valueA, valueB));
    std::vector<int> factors;
    bool oneSuccess = false;
    for(int i = 2; i <= max; i++)
    {
        bool remainderA = valueA % i == 0;
        bool remainderB = valueB % i == 0;
        if(remainderA != remainderB)
            return false;
        if(IsPrime(i))
        {
            //bool remainderA = valueA % i == 0;
           // bool remainderB = valueB % i == 0;

            if(remainderA != remainderB )
            {
                return false;
            }
            else if(!oneSuccess && remainderA && remainderB)
            {
                oneSuccess = true;
            }
        }
    }

    return true;
}

int solution(vector<int> &A, vector<int> &B) {
    int count = 0;
    for(size_t i = 0; i < A.size(); i++)
    {
        int valA = A[i];
        int valB = B[i];

        if(GetPrimeFactors(valA, valB))
            ++count;
    }

    return count;
}

【问题讨论】:

  • IsPrime 函数可以使用常见素数的表查找。如果数字大于最大的素数,则开始筛子(并将新找到的素数附加到表中)。通常,查表比筛子快。

标签: c++ math optimization primes


【解决方案1】:

您实际上不必找到数字的质因数来确定它们是否具有相同的质因数。

这是我想出的一个通用算法,用于检查 ab 是否具有相同的质因数。这将比素数分解ab 快得多。

  1. 如果a == b,则答案为true
  2. 如果a == 1 || b == 1,则答案为false
  3. 使用Euclid's Algorithm 查找2 个号码的GCD。如果是GCD == 1,答案是false
  4. 请注意,GCD 需要包含两个数字的所有质因数才能使答案为真,因此请检查 newa = a/GCDnewb = b/GCD 是否可以通过重复将它们除以 @987654335 来减少到 1 @ 和 Euclid(newb, GCD) 直到 newanewb 到达 1 表示成功,或者 Euclid(newa, GCD)Euclid(newb, GCD) 返回 1 表示失败。
让我们看看这对于 a = 75, b = 15 是如何工作的: 1) GCD = 欧几里得(75, 15) = 15 2) newa = 75/15 = 5,newb = 15/15 = 1,用 newb 完成 3) newa = 5/Euclid(5, 15) = 5/5 = 1 成功! a = 6, b = 4 怎么样: 1) GCD = 欧几里得(6, 4) = 2 2) newa = 6/2 = 3,newb = 4/2 = 2 3) Euclid(newa, 2) = Euclid(3, 2) = 1 失败! a = 2, b = 16 怎么样: 1) GCD = 欧几里得(2, 16) = 2 2) newa = 2/2 = 1(很好),newb = 16/2 = 8 3) newb = 8/Euclid(8, 2) = 8/2 = 4 4) newb = 4/Euclid(4, 2) = 4/2 = 2 5) newb = 2/Euclid(2, 2) = 2/2 = 1 成功!

【讨论】:

  • 这是正确答案,即使对于任意大的整数也应该很快。
【解决方案2】:

一个(非常简单的)优化(已更新):

bool IsPrime(int number)
{
    if (number % 2 == 0) 
    {
        return (number == 2);
    }
    int limit = sqrt(number);
    for (int i = 3; i <= limit; i += 2)
    {
        if (number % i == 0)
        {
            return false;
        }
    }
    return true;    
}

【讨论】:

  • if (number % 2 == 0) { return number == 2; } 二是素数;其他偶数不是。
  • @rossum,我的代码(在编辑之前)为2 返回true,这是应该的。但是,我包含了您的建议,因为它更有效(它不会为 2 计算 sqrt)。谢谢!
  • @WillNess,没有机会测试它,for 循环条件中有错误。谢谢!我已经修复了代码。
【解决方案3】:

Java 实现基于vacawama 的回答:

  class Solution {
    public int solution(int[] A, int[] B) {
        int count = 0;
        for(int i = 0; i < A.length; i++){
            if(A[i] == B[i]) count++;
            else if(A[i] == 1 || B[i] == 1) continue;
            else{
                int GCD = gcd(A[i], B[i]);
                
                if(GCD == 1) continue;
                
                int newA = A[i]/GCD;
                int newB = B[i]/GCD;
                
                if(checkDiv(newA, GCD) && checkDiv(newB, GCD)) count++;
            } 
        }
        
        return count;
    }
    
    public boolean checkDiv(int num, int gcd){
        
        if(num == 1) return true;
        else if(gcd == 1) return false;
        
        else {
            gcd = gcd(gcd, num);
            num = num/gcd;
        
            return checkDiv(num, gcd);
        }
    }
    public int gcd(int a, int b){
        if(b == 0) return a;
        else return gcd(b, a % b);
    }
}

【讨论】:

    【解决方案4】:

    找到了很好很详细的解释here

    假设两个数NM,用质数分解它们,然后将NM的GCD表示为P1 * P2 * P3 * P4 * ... Px(每个都是gcd(N,M)的质数除数) .然后,将N / gcd(N,M)M / gcd(N,M)分别表示为N1 * N2 * N3 * ... NyM1 * M2 * M3 * ... Mz,分别由它们的素数除数;那么NM可以表示如下。

    N = (P1 * P2 * P3 ... Px) * N1 * N2 * N3 * ... Ny
    M = (P1 * P2 * P3 ... Px) * M1 * M2 * M3 * ... Mz
    

    由于(P1 * P2 * P3 ... Px)gcd(N,M)NM 共有的任何素除数总是在(P1, P2, P3, ... Px) 中至少出现一次。

    换句话说,如果在(P1, P2, P3, ...Px) 中找不到任何'N/ gcd(N,M)'(N1, N2, N3 ... Ny) 的素因数,它就不是M 的素因数。因此,可以说NM的素因数集合并不完全相同。

    同理,如果在(P1, P2. P3, ... Px)中找不到任何'M / gcd(A,B)'(M1, M2, L3 ... Ly)的素因数,它就不是N的素因数,可以说NM 并不完全相同。

    所以问题只是检查N1-NyM1-Mz 是否从未出现在P1-Px 中。

    现在让我们想想这个。让X = N / gcd(N,M) 考虑gcd(gcd(N, M), X)

    暂时如下。

    gcd(N,M): P1 * P2 * P3 ... Px
    X       : N1 * N2 * N3 ... Ny
    

    如果gcd(N,M) % X == 0,则X的所有质数除数都包含在gcd(N,M)中。

    如果不是,那么我们计算gcd(gcd(N,M), X)。如果这两个值的gcd只有1,那意味着N1-Ny没有出现在P1-Px中;这意味着值N 有一个不与M 共享的主要除数。

    如果 gcd 大于 1。那么我们计算 X / gcd(gcd(N,M), X),并在下一轮更新 X。这意味着我们取出了X的一些素数,构成gcd(gcd(N,M), X),并将其用于下一轮

    如果此时gcd(N, M) % X == 0,则意味着X 的所有主要除数都包含在gcd(N, M) 中。如果不是,我们再次执行上述操作。

    【讨论】:

      【解决方案5】:

      上述@vacawama 解决方案的python 实现。

      def gcd_division(a, b):
          if not a%b:
              return b
          return gcd_division(b, a%b)
      
      def prime_reduce(n, gcd):
          na = n // gcd
          ngcd = gcd_division(na, gcd)
          if na == 1:
              return True # success base case
          elif ngcd == 1:
              return False
          return prime_reduce(na, ngcd)
      
      def solution(A, B):
          Z = len(A)
          result = 0
          for i in range(0, Z):
              a, b = A[i], B[i]
              if a == b:
                  result += 1
              else:
                  gcd = gcd_division(a, b)
                  result += (prime_reduce(a, gcd) and prime_reduce(b, gcd))
          return result
      

      我使用以下测试用例运行它。

      if __name__ == '__main__':
          test_cases = (
              (1, ([15, 10, 9], [75, 30, 5]) ),
              (2, ([7, 17, 5, 3], [7, 11, 5, 2]) ),
              (2, ([3, 9, 20, 11], [9, 81, 5, 13]) ),
          )
          for expected, args in test_cases:
              got = solution(*args)
              print('result', expected, got)
              assert(expected == got)
      

      它的 100% https://app.codility.com/demo/results/training7KRXR3-FE5/

      【讨论】:

        【解决方案6】:

        使用@vacawama 答案的Javascript 解决方案。 100% 上代码

        
        function solution(A, B) {
        
            function getGcd(a,b, res = 1) {
                if (a === b) return res * a;
                if (a % 2 === 0 && b % 2 === 0) return getGcd(a/2, b/2, 2 * res);
                if (a % 2 === 0) return getGcd(a/2, b, res);
                if (b % 2 === 0) return getGcd(a, b/2, res);
                if (a > b) return getGcd(a-b, b, res);
                else return getGcd(a, b-a, res);
            }
        
            const hasCommonPrimeDivisors = (a, b) => {
                if (a === b) return true;
                if (a === 1 || b === 1) return false;
                let gcd = getGcd(a, b);
                if (gcd === 1) return false;
                while (a !== 1 || b !== 1) {
                    let newGcd;
                    if (a !== 1) {
                        newGcd = getGcd(a, gcd);
                        if (newGcd === 1) {
                            return false;
                        }
                        a = a / newGcd;
                    }
        
                    if (b !== 1) {
                        newGcd = getGcd(b, gcd);
                        if (newGcd === 1) {
                            return false;
                        }
                        b = b/newGcd;
                    }
                }
                return true;
            }
        
            let count = 0
            A.forEach((a, index) => {
                const b = B[index];
                if (hasCommonPrimeDivisors(a, b)) {
                    count++;
                }
            })
            return count;
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2012-02-14
          • 1970-01-01
          • 1970-01-01
          • 2017-01-02
          • 1970-01-01
          • 2020-04-17
          • 1970-01-01
          相关资源
          最近更新 更多