Каков наиболее эффективный алгоритм вычисления LCM диапазона чисел?

Я огляделся и нашел другие вопросы, на которые были ответы, но ни один из них не затрагивает сферу этого конкретного вопроса., в том числе этот вопрос, а также этот.

Я должен вычислить LCM больших диапазонов чисел эффективным способом. Я не слишком углублялся в эти другие вопросы, потому что они не касаются диапазонов чисел, которые так велики, как те, которые должен обрабатывать этот алгоритм.

код, который у меня есть прямо сейчас можно рассчитать LCM каждого числа от 1 до 350000 примерно за 90 секунд. (Результирующее число составляет около 76000 десятичных цифр). Я надеюсь, что в конечном итоге смогу масштабировать его по диапазонам, которые составляют миллионы или даже миллиарды элементов.

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

вот это:

public static BigInteger getLCMOfRange(BigInteger lower, BigInteger upper)
{
    BigInteger M = BigInteger.ONE;
    BigInteger t;

    // long l = System.currentTimeMillis();
    // System.out.println("Calculating LCM of numbers up to " + upper + "...");
    for (; lower.compareTo(upper) != 1; lower = lower.add(BigInteger.ONE))
    {
        t = M.gcd(lower);
        if (t.compareTo(lower) == 0)
            continue;
        M = M.multiply(lower).divide(t);
    }
    // System.out.println("Done.  Took " + (System.currentTimeMillis() - l) + " milliseconds.  LCM is " + M.bitCount()+ " bits long.");
    return M;
}

обратите внимание, что в отличие от типичного цикла for эта функция работает над [lower, upper] вместо [lower, upper). Такое поведение преднамеренно.

немного поддерживающей математики является то, что LCM некоторого набора чисел произведение набора простых факторов, из которых любое из чисел может быть произведено без необходимости каких-либо вне пула. Если мой диапазон равен [1,20], я могу представить это в следующем путь:

1: 1         6:  3*2      11: 11       16: 2^4
2: 2         7:  7        12: 3*2^2    17: 17
3: 3         8:  2^3      13: 13       18: 3^2*2
4: 2^2       9:  3^2      14: 7*2      19: 19
5: 5         10: 5*2      15: 5*3      20: 5*2^2

LCM{[1,20]}: 2^4*3^2*5*7*11*13*17*19 = 232792560

существуют ли более эффективные способы вычисления LCM в таком большом диапазоне?

мне все равно, если алгоритм, который кто-то предлагает, очень тяжелый для памяти, производительность времени намного важнее (и также дороже), чем производительность памяти в этом случае.

это не вопрос домашнее задание.

вопрос

каков наиболее эффективный способ расчета LCM очень большого диапазона чисел? Этот алгоритм необходимо работать на запредельно широких диапазонах чисел и, таким образом, должны быть тщательно оптимизированы.

приложение 1

тесно связанный вопрос: каков наиболее эффективный способ вычисления логарифма одного BigInteger (база другого BigInteger)? Полученное значение может быть усечено до ближайшего целого числа.

2 ответов


Это макет алгоритма. Я предполагаю, что вы всегда начинаете с 1:

  1. найти простые числа в диапазоне. Вы можете использовать сито Эратосфена для 350000. Для большего диапазона чисел вам понадобится сегментированные сита.

  2. для каждого простого числа p используйте логарифмическую функцию, чтобы найти наибольший показатель E, который pe находится в пределах диапазона. Умножьте pe к LCM. (Детали оптимизации до вашей реализации)

Почему это правильно?

  • для чисел в виде pe где p является простым, а e > = 1, из-за шага 2, был включен в LCM, поэтому pe / LCM.
  • другие числа будут иметь вид N = p1e1p2e2...pnen (где pя - попарно различные простые числа и Ея > = 1), который больше или равен pяeя (для всех i от 1 до n). Так как pяeя | LCM, из-за предыдущего аргумента, N / LCM.

это обобщение ответа @nhahtdh

первый шаг, найти все простые числа, которые меньше или равны верхней границе.

затем возьмите каждое простое p и запишите как нижнюю, так и верхнюю границу в базовой нотации P. Самая высокая цифра, которая отличается в двух числах, - это показатель p, который вам нужно включить в LCM. Если нижняя граница равна 1, это тривиально то же самое, что и другой ответ.

обратите внимание, что сложность этого алгоритм не зависит от длины диапазона, только от величины верхней границы.