Java中获取数量的最快因素的最快方法是什么

时间:2012-04-13 10:14:32

标签: java algorithm math

我正在尝试用Java编写一个函数,它将返回特定数字所具有的因子数。

应考虑以下限制。

  1. 应该使用BigInteger
  2. 不允许存储以前生成的数字,因此处理更多,内存更少。(您不能像this中那样使用“Atve的筛选”)
  3. 可以忽略负数。
  4. 这是我到目前为止所做的,但它非常慢。

    public static int getNumberOfFactors(BigInteger number) {
        // If the number is 1
        int numberOfFactors = 1;
    
        if (number.compareTo(BigInteger.ONE) <= 0)  {
            return numberOfFactors;
        }
    
        BigInteger boundry = number.divide(new BigInteger("2"));
        BigInteger counter = new BigInteger("2");
    
        while (counter.compareTo(boundry) <= 0) {
            if (number.mod(counter).compareTo(BigInteger.ZERO) == 0) {
                numberOfFactors++;
            }
    
            counter = counter.add(BigInteger.ONE);
        }
    
        // For the number it self
        numberOfFactors++;
    
        return numberOfFactors;
    }
    

3 个答案:

答案 0 :(得分:16)

我可以提出更快的解决方案,但我觉得它还不够快。您的解决方案在O(n)中运行,我的解决方案将在O(sqrt(n))中运行。

我将使用以下事实:如果n = x i1 p1 * x i2 p2 * x i3 p3 * ... x ik pk n的主要分解(即x i j 都是不同的素数)然后n总共具有(p1 + 1)*(p2 + 1)* ... *(pk + 1)个因子。

现在解决方案:

BigInteger x = new BigInteger("2");
long totalFactors = 1;
while (x.multiply(x).compareTo(number) <= 0) {
    int power = 0;
    while (number.mod(x).equals(BigInteger.ZERO)) {
        power++;
        number = number.divide(x);
    }
    totalFactors *= (power + 1);
    x = x.add(BigInteger.ONE);
}
if (!number.equals(BigInteger.ONE)) {
    totalFactors *= 2;
}
System.out.println("The total number of factors is: " + totalFactors);

如果您单独考虑2的情况,然后将x的步骤等于2而不是1(仅迭代奇数),则可以进一步优化。

另请注意,在我的代码中,我修改了number,您可能会发现它更适合保留number并使另一个变量等于number来迭代。

我认为对于不大于2 64 的数字,此代码运行速度相当快。

编辑为了完整性,我会将合理快速的措施添加到答案中。正如在下面的评论中可以看到的,我对Betlista提出的测试用例100000007 2 的算法的性能进行了几次测量:

  • 如果按原样使用算法,我机器上的时间是57秒。
  • 如果我只考虑奇数,则时间减少到28秒
  • 如果我将while的结束条件的检查更改为与使用二分查找找到的number的平方根进行比较,则所花费的时间减少到22秒。
  • 最后,当我尝试用BigInteger切换所有long时,时间减少到2秒。由于建议的算法运行速度不够快number大于long的范围,因此将实现切换为long
  • 可能是有意义的

答案 1 :(得分:1)

一些改进:

  1. 您只需要检查sqrt(n),而不是n / 2。这使得您的算法O(sqrt(n))而不是O(n)。
  2. 你只需要在检查2之后检查奇数,这应该加倍速度。
  3. 虽然你不能使用以前的数字,你可以构造一个已知素数和一点存储的筛子:2,3是素数,所以只需要检查(例如)11,13,17,19,23和不是12,14,15,16,18。因此,您可以存储3的增量模式:[+ 2,+ 4],每6重复一次:
  4. var deltas = [2,4];
    var period = 6;
    var val = 3;
    var i=0;
    while(val<sqrt(n)) {
        var idx = i%deltas.length; // i modulo num deltas
        val += deltas[idx];
        count += isFactor(n,val);
        // if reached end of deltas, add period
        if(idx == deltas.length-1) {
            val += period - deltas[idx];
        }
        ++i;
    }
    

    一旦得到这个结果,如果它们是因素,你显然必须加2和/或3。

    当我在学校感到无聊时,我就把上述模式搞得一团糟。你可以计算任何素数列表的模式,但有一个收益递减规律;你添加的每个素数都会增加周期,并大大增加增量列表的长度。因此,对于一长串已知素数,您将得到一个极长的增量列表,并且速度只有很小的改进。但是,请测试加速是否值得。

    因为它只是敲出了已知部分的值(使用显示的2值delta的2 / 3rds),所以这是stil O(sqrt(n))。

    将筛子与sqrt界限相结合,你应该获得4 /(3 * sqrt(n))的加速。

    [编辑:将句点添加到最后一个值,而不是句点 - lastdelta。谢谢@Betlista]

答案 2 :(得分:0)

Boris Strandjev提出的最快解决方案通过在Java中生成大量输出来解决问题。 它是在Java中查找非常大的整数除数的最快算法。

以下是我的代码将成功运行:

import java.math.BigInteger;
import java.util.Scanner;

class ProductDivisors {

    public static BigInteger modulo=new BigInteger("1000000007");
    public static BigInteger solve=new BigInteger("1");
    public static BigInteger two=new BigInteger("2");
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Scanner sc=new Scanner(System.in);
        int N=sc.nextInt();
        BigInteger prod=new BigInteger("1");
        while(N-->0){
            prod=sc.nextBigInteger();
            solve=solve.multiply(prod);
        }
        BigInteger x = new BigInteger("2");
        BigInteger total = new BigInteger("0");
        BigInteger totalFactors =new BigInteger("1");
        while (x.multiply(x).compareTo(solve) <= 0) {
            int power = 0;
            while (solve.mod(x).equals(BigInteger.ZERO)) {
                power++;
                solve = solve.divide(x);
            }
            total = new BigInteger(""+(power + 1));
            totalFactors=totalFactors.multiply(total);
            x = x.add(BigInteger.ONE);
        }
        if (!(solve.equals(BigInteger.ONE))) {
            totalFactors =totalFactors.multiply(two);
        }
        totalFactors=totalFactors.mod(modulo);
        System.out.println(totalFactors);
    }

}

这段代码通常将数字数组作为输入,从而乘以产生大数字的数字。 并且,在那之后计算除数的主要代码(包括数字在这里被视为除数)完成并给出输出。

我希望这是有效的方式,并在需要时建议任何错误或添加。