发现的因素

Finding factors

本文关键字:发现      更新时间:2023-10-16

我正在做一个c++程序,在这个程序中,我需要从数组中找到具有公因数的数字。我已经在用幼稚的方式做这件事了。

int commonFactors(int p, int q){
    int count = 0;
    if(q > p){
        for(int i = 2;i < q;i++){
            if((q%i==0)&&(p%i==0)){
                count++;
                break;
            }
        }
    }
    else if(p > q){
        for(int i = 2;i < p;i++){
            if((p%i==0)&&(q%i==0)){
                count++;
                break;
            }
        }
    }
    else{
        count = 1;
    }
    return count;
}

那么对于较大的输入,我的代码会超时。对于数组中的任何元素,我的输入范围是从1到1000000。知道如何有效地计算它吗?

我有一个只检查质因数的想法,但我担心检查的范围

如果唯一的问题是"这两个是否有公约数(除了一个)",那么一个选项就是计算它们的最大公约数,并检查它是否为1。使用欧几里得算法可以相当有效地计算GCD(绝对比一直数到你的数字快):

gcd(a, 0) = a
gcd(a, b) = gcd(b, a % b)

你可以更有效地运行for循环直到"sqrt(p)"(或者是q,当然取决于更小的数)这应该已经加快了速度。

考虑两个数字:9240和16170。每个数都可以写成几个质数的乘积:

9240 = 2*2*3*5*7*11
16170 = 2*3*5*7*7*11
从上面的示例中可以明显看出,可能的公共因子的总数将是您可以用这些操作数创建的数字的总列表。在这种情况下,2, 3, 511的数字集合将产生15个总组合。

所以你的代码可以执行以下步骤(我不打算为你编写c++代码,因为你应该能够轻松地自己完成):

  1. 使用整数分解将每个数字分解为其质因数
  2. 查找每个列表中存在的素数的完整子集(不要忘记有些素数可能在两个列表中出现不止一次,应该作为单独的素数计算,即两次)
  3. 找到所有可能的数字,你可以通过组合一组给定的素数

对于本文的最后一部分,您可以查看动态规划,了解如何显著提高其性能naïve方法相比。

首先是一些数学问题:假设A和B是两个正非空整数,设C= gcd(A, B)是A和B的最大公约数,则如果M能整除A和B,则M能整除C。

所以如果你只想知道A和B是否有公因数你只需要检查C是否大于1,如果你想知道所有的公因数(或它们的个数)你必须找到C的所有因数

求两个数的GCD的欧几里得算法基于以下性质:A, A = P * Q + R是P除以Q的欧几里得除法,则如果R = 0, GCD(A,B) = B,否则GCD(A,B) = GCD(B,R) (ref wikipedia)

现在一些代码:

/* Euclidian algorythm to find Greatest Common Divisor
 Constraint (not controled here) p>0 and q>0 */
int gcd(int p, int q) {
    // ensures q < p
    if (p < q) {
        int temp = p;
        p = q;
        q = temp;
    }
    int r = p % q;
    // if q divises q, gcd is q, else gcd(p, q) is gcq(q, r)
    return (r == 0) ? q : gcd(q, r);
}
bool sharedivisors(int p, int q) {
    int d = gcd(p, q);
    return d > 1;
}
int divisors(int p, int q) {
    int d = gcd(p, q);
    if (d == 1) {
        return 1;
    }
    int count = 0;
    for(int i=2; i<d/2; i++) {
        if(d % i == 0) {
            int j = d/i;
            if (j > i) count += 2;
            else {
                if (j == i) count += 1;
                break;
            }
        }
    }
    return count + 2; // and 1 and d
}

从2到更大的输入是蛮力,即使其中一个输入很大,也会持续很长时间。公因数的个数可以由它们的质因数分解的指数得到。更容易先计算它们的最大公约数

gcd = gcd( p0, q0 )
/* .. */
int gcd( p0, q0 )
{
    while( q0 )
    {
        int swp = q0;
        q0 = p0 % q0;
        p0 = swp;
    }
    return p0;
}

,然后计算其除数

  • 以天真的方式(如问题)
  • 总是用找到的除数除gcd
  • 质因数分解

    p0^x0 * p1^x1 * .. * pN^xN = gcd
    count = (1+x0) * (1+x1) * .. * (1+xN)
    

质因数分解需要质数列表直到sqrt(gcd)