Какой самый быстрый способ в Java, чтобы получить сумму делителей числа
Я пытаюсь написать функцию на Java, которая вернет количество факторов конкретного числа.
необходимо учитывать следующие ограничения.
- это должно быть сделано с BigInteger
- хранить предыдущие произведенные номера не позволены, таким образом больше обрабатывать и меньше памяти.(Вы не можете использовать "сито Аткина", как в этой)
- отрицательные числа могут быть игнорируемый.
Это то, что у меня есть до сих пор, но это очень медленно.
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 ответов
я могу предложить более быстрое решение, хотя у меня есть чувство, что оно еще не будет достаточно быстрым. Ваше решение выполняется в O(n)
и мой будет работать в O(sqrt(n))
.
Я собираюсь использовать тот факт, что если n = xi1p1 * xИ2p2 * xi3p3 * ... xik pk премьер-факторизации n
(т. е. xя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
для перебора свыше.
Я полагаю, что этот код будет работать достаточно быстро для чисел не больше, чем 264.
редактировать я добавлю меры разумно быстро к ответу для полноты. Как видно из приведенных ниже комментариев, я сделал несколько замеров по производительности предложенного алгоритма для тестового случая 1000000072, который был предложен Betlista:
- если алгоритм используется как время на моей машине-57 секунд.
- если я считаю только нечетные числа, время сокращается до 28 секунд
- если я изменю чек для конечного состояния
while
для сравнения с квадратным корнемnumber
что я найти бинпоиском время снижает до 22 секунды. - наконец, когда я попытался переключить все
BigInteger
Сlong
время было сокращено до 2 секунд. Так как предложенный алгоритм не будет работать достаточно быстроnumber
больше, чем диапазонlong
возможно, имеет смысл переключиться на реализациюlong
некоторые улучшения:
- вам нужно только проверить до sqrt (n), не n/2. Это делает ваш алгоритм O (sqrt(n)) вместо O (n).
- вам нужно только проверить нечетные номера после проверки 2, которые должны удвоить скорость.
- хотя вы не можете использовать предыдущие числа, вы можете построить сито с известными простыми числами и немного хранения: 2, 3 являются простыми, поэтому нужно только проверить (например) 11,13,17,19,23, а не 12,14,15,16,18. Таким образом, вы можете сохранить шаблон дельты от 3: [+2,+4], повторяйте каждые 6:
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/3rds с использованием показанной дельты 2-значения), это stil O(sqrt (n)).
объединяя сито с привязкой sqrt, вы должны получить ускорение 4 / (3*sqrt (n)).
[Edit: добавлялся период к последнему значению, а не период-lastdelta. Спасибо @Betlista]
самое быстрое решение, предложенное Борисом Странджевым, имеет некоторую проблему, генерируя вывод больших чисел в 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);
}
}
этот код обычно принимает массив чисел в качестве входных данных и тем самым умножает, что приведет к большим числам. И, после этого берется основной код для подсчета числа делителей (включая число как делитель здесь) делается и дается вывод.
Я надеюсь, что это эффективный способ и предложить любые ошибки или добавление, если это необходимо.