2015-03-30 3 views
3

Я хотел бы сделать этот фрагмент кода быстрее. Он возвращает все факторы (простые числа) длинного числа. Очевидно, что минуты должны быть выполнены, если longNumber является конкретным.Улучшите код, который факторизуется быстрее?

int div = 2; 
String factors = ""; 

while (longNumer != 1) 
{ 
    if (longNumer % div == 0) 
    { 
     longNumer /= div; 
     factors += div + " "; 
    } 
    else { div++; } 
} 

//longNumber = 10, gives: 2 5. 
//longNumber = 150, gives: 3 5 7. 
//longNumber = 10523, gives: 17 619. 

Это занимает слишком много времени с номерами таких «7544222046562688368», и это не очень хорошо, что вы могли бы предложить?

+4

Используйте memoization для простых чисел, и код будет иметь большую производительность. –

+1

Если ваш алгоритм действительно дает '3,5,7' в качестве факторов' 150', я предлагаю вам сделать это правильно, прежде чем тратить время на ускорение. –

ответ

2

Вы можете использовать следующие шаги вместо -

1. Найти все простые числа < = SQRT (longNumber). И сохраните их в массиве - primes.
2. Теперь постепенно используйте элемент массива - primes в качестве делителя, чтобы найти коэффициент.

+0

не перехватывает массив довольно плохо с точки зрения производительности? – OPK

+1

@HashMap это не так. Это зависит от того, имеет ли массив 10 или 1 м элементов, что определяет хорошую/плохую производительность. –

+2

Я бы сказал, что вы должны искать простые числа тогда и только тогда, когда они не хранятся в массиве. Вы можете быстро просмотреть, используя двоичный поиск (при условии, что вы сохраните простые числа отсортированным способом в массиве). –

4

Для больших чисел можно использовать Sieve of Eratosthenes algorithm сначала найти простые числа до SQRT (п), а затем вы должны проверить, что ли эти простые числа факторов

+0

Это не поможет для 64-битных чисел, все еще много простых символов ниже '2^32' для проверки, и их генерация в любом случае добавит уменьшающихся возвратов. – IVlad

+0

Использование сита Эратосфена для получения первого sqrt (n) простых чисел принимает sqrt (n) loglog n операций. Судебное деление принимает операции sqrt (n). Что вы получаете, просеивая? –

+0

@ DouglasZare Да, сито выполняет больше операций, чем пробное подразделение. Но эти операции очень разные. Для CPU больше 10 раз медленнее, чем добавление, а 'log (log (n))' фактически является константой в диапазоне значений, которые вы можете вписать в длинный. (И указанная константа значительно меньше 10.) Ваш выбор языка может добавить достаточное количество служебных данных для маскировки этой разницы в производительности. Но на эффективном языке хорошо выполненное сито, вероятно, превзойдет пробное разделение над чем-либо, что может быть поставлено за долгое время. – btilly

2

Ответы предполагающие решета Эратосфена не делайте много для чисел, как вы описываете. Для 64 бит номеров sqrt(2^64) = 2^32, которого все еще много.

Для тех, ваши лучшие ставки Pollard's Rho algorithm или более сложные методы целочисленной факторизации перечислены here:

алгоритмы факторизации Алгебраическая-группы, среди которых р Полларда - 1 алгоритм, Williams' р + 1 алгоритм, и факторизация с помощью эллиптических кривых

метод факторизации Ферма

метод факторизации Эйлера

Специальное поле номер сито

1

Хороший способ к фактору 64-разрядных целых чисел, это как просто программировать и достаточно эффективно на практике, сочетает в себе пробное деление и алгоритм ро Полларда. Вот псевдокод:

function factors(n) 
    wheel := [1,2,2,4,2,4,2,4,6,2,6] 
    w, f, fs := 0, 2, [] 
    while f*f <= n and f < 10000 
     while n % f == 0 
      fs, n := f :: fs, n/f 
     f, w := f + wheel[w], w+1 
     if w = 11 then w = 3 
    if n == 1 return fs 
    h, t, g, c := 1, 1, 1, 1 
    while not isPrime(n) 
     repeat 
      h := (h*h+c) % n # the hare runs 
      h := (h*h+c) % n # twice as fast 
      t := (t*t+c) % n # as the tortoise 
      g := gcd(t-h, n) 
     while g == 1 
     if isPrime(g) 
      while n % g == 0 
       fs, n := g :: fs, n/g 
     h, t, g, c := 1, 1, 1, c+1 
    return n :: fs 

Это использует 2,3,5-колесо для пробного деления до 10000 с последующим простой реализацией алгоритма ро; он должен учитывать ваш номер образца как 7544222046562688368 = 2 * 2 * 2 * 2 * 7 * 7 * 14618561 * 658254407 через несколько миллисекунд. Усовершенствования возможны, но этого должно быть достаточно, чтобы вы начали.

2

Перед тем, как внедрить один из алгоритмов факторинга быстрее, чем пробное деление, одна легко исправленная ошибка состоит в том, чтобы избежать выполнения пробного деления за sqrt последней части.

while (longNumber != 1) { 
    if (longNumber % div == 0) { 
     longNumber /= div; 
     factors += div + " "; 
    } 
    else { 
     if (div*div>longNumber) { 
      if (longNumber > 1) 
       factors += longNumber + " "; 
      break; // leave the while loop. 
     } 
     div++; 
    } 
} 

Пусть два самых больших простых фактора - P1 и P2. В вашей версии вы выполняете операции c P1. В модифицированной версии вы делаете о c Max (sqrt (P1), P2). На 7544222046562688368 улучшение должно быть в 45 раз.

Другим усовершенствованием является изменение строки div ++. Вам не нужно делать пробное деление четными числами больше 2 или цифрами, делящимися на 3 больше, чем 3.Избегая этих ускорений вычислений более чем на 2%, и вы можете сделать несколько лучше, избегая тестирования кратных других небольших простых чисел. Тем не менее, вы не хотите тратить время на пробные подразделения div на небольшие простые числа. Вместо этого вы отслеживаете текущие и допустимые остатки mod 2 * 3 * 5 * 7, скажем. Это называется использованием колеса для небольших простых чисел.

Некоторые другие ответы говорят о том, чтобы использовать сито, чтобы найти все мелкие простые числа, а затем использовать пробное деление только этими. Это не поможет, если вы факторизуете только одно число, так как слишком долго приходится просеивать простые числа. Получение списка простых чисел до sqrt (n) занимает около c sqrt (n) loglog n операций, а пробное деление на все до sqrt (n) занимает около c sqrt (n) операций. Выполнение сита один раз и сохранение результатов может помочь, если вам нужно учитывать множество больших чисел.

+0

Я не думаю, что для 'div * div> longNumber' существует некоторая дорога, так как в этом случае вы уже нашли все основные факторы, а' longNumber' был уменьшен до 1. –

+0

@Anonymous: div * div может быть больше longNumber, когда у вас остался только один простой фактор. Предположим, что longNumber начинается с 7. Вы тестируете div = 2, затем div = 3 и 3 * 3> 7, а затем вы знаете, что 7 является простым, поэтому вам не нужно тестировать более высокие значения div. Если longNumber начинается с 14, вы находите коэффициент 2, уменьшая его до 7, тест 2 снова, затем в 3 вы можете остановиться. –

+0

Ах да, умный. –

Смежные вопросы