Временная сложность алгоритма Евклида

Я с трудом решаю, насколько сложна временная сложность алгоритма наибольшего общего знаменателя Евклида. Этот алгоритм в псевдокоде:

function gcd(a, b)
    while b ≠ 0
       t := b
       b := a mod b
       a := t
    return a

Кажется, что это зависит от a и b. Я считаю, что временная сложность O (a% b). Это верно? Есть ли лучший способ написать это?

Ответ 1

Один трюк для анализа временной сложности алгоритма Евклида - это то, что происходит в течение двух итераций:

a', b' := a % b, b % (a % b)

Теперь a и b уменьшатся, а не только один, что упростит анализ. Вы можете разделить его на случаи:

  • Tiny A: 2a <= b
  • Крошечный B: 2b <= a
  • Малый A: 2a > b, но a < b
  • Маленький B: 2b > a, но b < a
  • Равно: a == b

Теперь мы покажем, что каждый отдельный случай уменьшает общую сумму a+b по крайней мере на четверть:

  • Tiny A: b % (a % b) < a и 2a <= b, поэтому b уменьшается не менее чем на половину, поэтому a+b уменьшается не менее чем на 25%
  • Tiny B: a % b < b и 2b <= a, поэтому a уменьшается не менее чем на половину, поэтому a+b уменьшилось не менее чем на 25%
  • Малый A: b станет b-a, который меньше b/2, уменьшая a+b не менее чем на 25%.
  • Маленький B: a станет a-b, который меньше a/2, уменьшая a+b не менее чем на 25%.
  • Равно: a+b падает до 0, что, очевидно, уменьшается a+b не менее чем на 25%.

Следовательно, при анализе случая каждый двойной шаг уменьшается a+b по крайней мере на 25%. Максимальное количество раз это может произойти до того, как a+b будет опускаться ниже 1. Общее количество шагов (S), пока мы не достигнем 0, должно удовлетворять (4/3)^S <= A+B. Теперь просто выполните его:

(4/3)^S <= A+B
S <= lg[4/3](A+B)
S is O(lg[4/3](A+B))
S is O(lg(A+B))
S is O(lg(A*B)) //because A*B asymptotically greater than A+B
S is O(lg(A)+lg(B))
//Input size N is lg(A) + lg(B)
S is O(N)

Таким образом, число итераций является линейным по числу входных цифр. Для чисел, которые вписываются в регистры процессора, разумно моделировать итерации как постоянное время и делать вид, что общее время работы gcd является линейным.

Конечно, если вы имеете дело с большими целыми числами, вы должны учитывать тот факт, что операции модуля в каждой итерации не имеют постоянной стоимости. Грубо говоря, полное асимптотическое время выполнения будет в 2 раза больше полилогарифмического фактора. Что-то вроде n^2 lg(n) 2^O(log* n). Полилогарифмический фактор можно избежать, используя двоичный gcd ​​.

Ответ 2

Подходящим способом анализа алгоритма является определение его наихудших сценариев. Евклидский GCD худший случай возникает, когда участвуют пары Фибоначчи. void EGCD(fib[i], fib[i - 1]), где i > 0.

Например, позвольте выбрать случай, когда дивиденд равен 55, а делитель равен 34 (напомним, что мы все еще имеем дело с числами фибоначчи).

enter image description here

Как вы можете заметить, эта операция стоила 8 итераций (или рекурсивных вызовов).

Давайте попробуем увеличить числа Фибоначчи, а именно 121393 и 75025. Мы также можем заметить здесь, что для этого потребовалось 24 итерации (или рекурсивных вызовов).

enter image description here

Вы также можете заметить, что каждая итерация дает число Фибоначчи. Вот почему у нас так много операций. Мы не можем получить аналогичные результаты только с числами Фибоначчи.

Следовательно, временная сложность будет представлена ​​малым О (верхняя граница), на этот раз. Нижняя граница интуитивно омега (1): случай 500, разделенный на 2, например.

Решим рекуррентное соотношение:

enter image description here

Можно сказать, что евклидово GCD может сделать операцию log (xy) не более.

Ответ 3

Здесь вы можете посмотреть на статью статьи в википедии.

Он даже имеет приятный сюжет сложности для пар значений.

Это не O (a% b).

Известно (см. статью), что он никогда не предпримет больше шагов, чем в пять раз больше цифр меньшего числа. Таким образом, максимальное количество шагов растет как число цифр (ln b). Стоимость каждого шага также растет как число цифр, поэтому сложность связана O (ln ^ 2 b), где b - меньший нубмер. Это верхний предел, а фактическое время обычно меньше.

Ответ 4

Смотрите здесь.

В частности, эта часть:

Ламе показал, что количество шагов, необходимых для достижения наибольшего общего делителя для двух чисел, меньших n, равно

alt text

So O(log min(a, b)) - хорошая верхняя граница.

Ответ 5

Здесь интуитивное понимание сложности выполнения алгоритма Евклида. Формальные доказательства рассматриваются в различных текстах, таких как Введение в алгоритмы и TAOCP Vol 2.

Сначала подумайте, что если бы мы попытались взять gcd двух чисел Фибоначчи F (k + 1) и F (k). Вы можете быстро заметить, что алгоритм Евклида выполняет итерацию на F (k) и F (k-1). То есть, с каждой итерацией мы перемещаем одно число в ряд Фибоначчи. Поскольку числа Фибоначчи - это O (Phi ^ k), где Phi - золотое отношение, мы можем видеть, что время выполнения GCD было O (log n), где n = max (a, b), а log имеет базу Phi. Затем мы можем доказать, что это было бы наихудшим случаем, если бы заметить, что числа Фибоначчи последовательно производят пары, где остатки остаются достаточно большими на каждой итерации и никогда не становятся равными нулю, пока вы не достигнете начала серии.

Мы можем сделать O (log n), где n = max (a, b) связаны еще более жесткими. Предположим, что b >= a, поэтому мы можем записать в O (log b). Во-первых, заметим, что GCD (ka, kb) = GCD (a, b). Поскольку самые большие значения k являются gcd (a, c), мы можем заменить b на b/gcd (a, b) в нашей среде выполнения, что приведет к более жесткой привязке O (log b/gcd (a, b)).

Ответ 6

Худший вариант алгоритма Евклида - это когда остатки являются самыми большими на каждом шаге, т.е. для двух последовательных членов последовательности Фибоначчи.

Когда n и m - число цифр a и b, если n >= m, в алгоритме используются O (m) -разделения.

Обратите внимание, что сложности всегда задаются в терминах размеров входов, в этом случае количество цифр.

Ответ 7

Однако для итеративного алгоритма мы имеем:

int iterativeEGCD(long long n, long long m) {
    long long a;
    int numberOfIterations = 0;
    while ( n != 0 ) {
         a = m;
         m = n;
         n = a % n;
        numberOfIterations ++;
    }
    printf("\nIterative GCD iterated %d times.", numberOfIterations);
    return m;
}

С парами Фибоначчи нет разницы между iterativeEGCD() и iterativeEGCDForWorstCase(), где последнее выглядит следующим образом:

int iterativeEGCDForWorstCase(long long n, long long m) {
    long long a;
    int numberOfIterations = 0;
    while ( n != 0 ) {
         a = m;
         m = n;
         n = a - n;
        numberOfIterations ++;
    }
    printf("\nIterative GCD iterated %d times.", numberOfIterations);
    return m;
}

Да, с парами Фибоначчи, n = a % n и n = a - n, это точно то же самое.

Мы также знаем, что в более раннем ответе на тот же вопрос существует преобладающий убывающий фактор: factor = m / (n % m).

Поэтому, чтобы сформировать итеративную версию евклидова GCD в определенном виде, мы можем изобразить как "симулятор" следующим образом:

void iterativeGCDSimulator(long long x, long long y) {
    long long i;
    double factor = x / (double)(x % y);
    int numberOfIterations = 0;
    for ( i = x * y ; i >= 1 ; i = i / factor) {
        numberOfIterations ++;
    }
    printf("\nIterative GCD Simulator iterated %d times.", numberOfIterations);
}

На основе work (последний слайд) д-ра Jauhar Ali, цикл выше логарифмический.

enter image description here

Да, маленький О, потому что симулятор сообщает количество итераций не более. Партии, не относящиеся к Фибоначчи, занимали меньшее количество итераций, чем Фибоначчи, если они были исследованы на евклидовом GCD.

Ответ 8

Теорема Габриэля Ламе ограничивает число шагов по log (1/sqrt (5) * (a + 1/2)) - 2, где база журнала равна (1 + sqrt (5))/2. Это относится к наихудшему сценарию для алгоритма, и это происходит, когда входы являются последовательными числами Фибанокси.

Немного более либеральная граница: log a, где база журнала (sqrt (2)) подразумевается Koblitz.

Для криптографических целей мы обычно рассматриваем поразрядную сложность алгоритмов, принимая во внимание, что размер бит задается приблизительно на k = loga.

Вот подробный анализ поразрядной сложности алгоритма Евклида:

Хотя в большинстве ссылок побитовая сложность алгоритма Евклида дается выражением O (loga) ^ 3, существует более жесткая оценка, которая является O (loga) ^ 2.

Рассмотрим; r0 = a, r1 = b, r0 = q1.r1 + r2.,, ri-1 = qi.ri + ri + 1,.,, rm-2 = qm-1.rm-1 + rm rm-1 = qm.rm

заметим, что: a = r0 >= b = r1 > r2 > r3... > rm-1 > rm > 0.......... (1)

и rm - наибольший общий делитель a и b.

По заявлению в книге Коблица (курс теории чисел и криптографии) можно доказать, что: ri + 1 < (ri-1)/2................. (2)

Опять же в Коблице число бит-операций, необходимых для деления k-битового положительного целого на 1-битовое положительное целое число (при условии k >= l), задается как: (k-l + 1).l................... (3)

В силу (1) и (2) количество дивизий равно O (loga), и поэтому (3) общая сложность равна O (loga) ^ 3.

Теперь это может быть сведено к O (loga) ^ 2 замечанием в Коблице.

рассмотрим ki = logri +1

по (1) и (2) имеем: ki + 1 <= ki для я = 0,1,..., m-2, m-1 и ki + 2 <= (ki) -1 для = 0,1,..., т-2

и (3) общая стоимость m-дивизий ограничена: SUM [(ki-1) - ((ki) -1))] ki для я = 0,1,2,..., т

переставляя это: SUM [(ki-1) - ((ki) -1))] * ki <= 4 * k0 ^ 2

Таким образом, побитовая сложность алгоритма Евклида равна O (loga) ^ 2.