我正在尝试用Java编写一个函数,它将返回特定数字所具有的因子数。
应考虑以下限制。
这是我到目前为止所做的,但它非常慢。
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;
}
答案 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 的算法的性能进行了几次测量:
while
的结束条件的检查更改为与使用二分查找找到的number
的平方根进行比较,则所花费的时间减少到22秒。BigInteger
切换所有long
时,时间减少到2秒。由于建议的算法运行速度不够快number
大于long
的范围,因此将实现切换为long
答案 1 :(得分:1)
一些改进:
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);
}
}
这段代码通常将数字数组作为输入,从而乘以产生大数字的数字。 并且,在那之后计算除数的主要代码(包括数字在这里被视为除数)完成并给出输出。
我希望这是有效的方式,并在需要时建议任何错误或添加。