Как выбирается внутреннее кольцо в алгоритме Шёнхаге-Штрассена?


9

Я пытался реализовать алгоритм умножения целых чисел Шёнхаге-Штрассена, но натолкнулся на камень преткновения на рекурсивном этапе.

У меня есть значение с битами, и я хочу вычислить . Первоначально я думал, что идея состоит в том, чтобы выбрать , чтобы 4 ^ k \ geq 2n , разбить x на 2 ^ k частей, каждый с 2 ^ {k-1} битами, применить свертку SSA во время работы по модулю 2 ^ {2 ^ k} +1 , кольцо с 2 ^ k битами емкости на значение, затем соедините части вместе. Однако вывод свертки имеет чуть более 2n бит (т.е. > 2 ^ kxnx2(mod2n+1)k4k2nx2k2k122k+12k2n>2kбит на выходное значение, которое больше емкости кольца, поскольку каждое выходное значение является суммой нескольких продуктов), поэтому это не работает. Я должен был добавить дополнительный коэффициент 2 дополнения.

Этот дополнительный фактор 2 в заполнении разрушает сложность. Это делает мой рекурсивный шаг слишком дорогим. Вместо алгоритма F(n)=nlgn+nF(2n)=Θ(nlgnlglgn) я получаю с алгоритмом F(n)=nlgn+nF(4n)=Θ(nlg2n) .

Я прочитал несколько ссылок, связанных с Википедией, но все они, кажется, затушевывают детали того, как эта проблема решается. Например, я мог бы избежать дополнительных накладных расходов, работая по модулю 2p2k+1 для p который не является степенью 2 ... но тогда все просто сломается позже, когда у меня только не-power- из 2 факторов осталось и не может применить Кули-Тьюки без удвоения количества фигур. Кроме того, p может не иметь мультипликативного обратного по модулю 2p+1 . Таким образом, все еще существуют вынужденные факторы 2.

Как выбрать кольцо для использования во время рекурсивного шага, не увеличивая асимптотическую сложность?

Или в форме псевдокода:

multiply_in_ring(a, b, n):
  ...
  // vvv                          vvv //
  // vvv HOW DOES THIS PART WORK? vvv //
  // vvv                          vvv //
  let inner_ring = convolution_ring_for_values_of_size(n);
  // ^^^                          ^^^ //
  // ^^^ HOW DOES THIS PART WORK? ^^^ //
  // ^^^                          ^^^ //

  let input_bits_per_piece = ceil(n / inner_ring.order);
  let piecesA = a.splitIntoNPiecesOfSize(inner_ring.order, input_bits_per_piece);
  let piecesB = b.splitIntoNPiecesOfSize(inner_ring.order, input_bits_per_piece);

  let piecesC = inner_ring.negacyclic_convolution(piecesA, piecesB);
  ...

Пожалуйста , не размещайте один и тот же вопрос на нескольких сайтах . Каждое сообщество должно иметь честный ответ, не теряя никого времени. Я предлагаю вам удалить одну из двух копий.
DW

@DW Готово. Я кросс-постил после того, как cs не давал никаких ответов в течение недели, полагая, что это было слишком сложно для этого сайта. Собирался ссылаться на любые ответы, очевидно.
Крейг Гидни

Я понимаю. Если это произойдет в будущем, вы всегда можете пометить свое сообщение для модератора и попросить перенести его, и мы можем переместить его для вас в CSTheory. Спасибо за понимание!
DW

3
Существует версия алгоритма, которая работает по модулю чисел вида : А. Шенхаге. Асимптотически быстрые алгоритмы численного умножения и деления полиномов с комплексными коэффициентами. На EUROCAM '82: Европейская конференция по компьютерной алгебре, лекция Примечания Comp. Sci. 144, 3-15. iai.uni-bonn.de/~schoe/publi39.dvi2ν2n
Маркус Блезер

IIRC у вас был частичный ответ на удаленный вопрос CS. Кажется, стыдно терять это. Не могли бы вы включить его здесь (в вопросе, чтобы вопрос не был помечен как уже отвеченный)?
Питер Тейлор

Ответы:


4

Этот ответ взят из статьи «Асимптотически быстрые алгоритмы числового умножения и деления многочленов с комплексными коэффициентами», которую Маркус связал в комментариях.


Вы хотите возвести в квадрат разрядное число по модулю . Вот что вы делаете:n2n+1

  • Найдите и которые удовлетворяют и .psn=(p1)2ssp2s

  • Выберите количество частей чтобы разделить бит, и соответствующие параметры для размеров частей:2mn

    m=s/2+1s2=s/2+1p2=p/2+1

    Обратите внимание, что и продолжают удовлетворять инварианту . Также обратите внимание, что удовлетворяется, поэтому вход соответствует месту для переносов.s2p2s2p22s22m2s2p22n+m+1

  • Выполните основанную на БПФ отрицательную циклическую свертку на кусках, а остальное, как обычно.

Так что это всеобъемлющая идея: логарифмический коэффициент заполнения . Теперь для анализа сложности. Для выполнения БПФ потребуется работа, и мы вернемся к кусочкам , так что теперь мы можем сделать очень грубую математику с рекуррентным соотношением относительно :pnm2m(p21)2s2s

F(s)()(p1)2sm+2mF(s/2+1)()2s2s(s/2+1)+2s/2+1F(s/2+1)()s22s+22s/2F(s/2+1)()s22s+4(s/2)22s+16(s/4)22s+...()2ss2lg(s)()nlgn(lgnlgn)2lglgnlgn()nlgn(lg2n)lglgn()n(lgn)lglgn

Что кажется правильным, хотя я довольно много обманывал на этих шагах.

«Уловка», кажется, в том, что мы получаем вместо в базовой стоимости. По-прежнему есть два умножения на два на рекурсивный уровень, как я жаловался в этом вопросе, но теперь, когда половина в платит двойные дивиденды, все работает. Затем, в конце, мы отменяем дополнительный коэффициент (который на самом деле является коэффициентом ) благодаря тому, что изначально мы делали логарифмически большим относительно .s2ssslognps

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.