Come viene scelto l'anello interno nell'algoritmo di Schönhage – Strassen?


9

Ho cercato di implementare l'algoritmo di moltiplicazione di interi Schönhage-Strassen, ma ho colpito un ostacolo nel passaggio ricorsivo.

Ho un valore con bit e voglio calcolare x ^ 2 \ pmod {2 ^ n + 1} . Inizialmente pensavo che l'idea fosse quella di scegliere un k tale che 4 ^ k \ geq 2n , dividere x in 2 ^ k pezzi ciascuno con 2 ^ {k-1} bit, applicare la convoluzione di SSA mentre si lavora modulo 2 ^ {2 ^ k} +1 , un anello con 2 ^ k bit di capacità per valore, quindi rimettere insieme i pezzi. Tuttavia, l'output della convoluzione ha poco più di 2n bit (ovvero > 2 ^ knxnx2(mod2n+1)k4k2nx2k2k122k+12k2n>2kbit per valore di uscita, che è superiore alla capacità dell'anello, poiché ciascun valore di uscita è una somma di più prodotti), quindi non funziona. Ho dovuto aggiungere un ulteriore fattore di 2 di imbottitura.

Quel fattore aggiuntivo di 2 nell'imbottitura rovina la complessità. Rende il mio passaggio ricorsivo troppo costoso. Invece di un algoritmo F(n)=nlgn+nF(2n)=Θ(nlgnlglgn) , finisco con un algoritmo F(n)=nlgn+nF(4n)=Θ(nlg2n) .

Ho letto alcuni riferimenti collegati da Wikipedia, ma tutti sembrano sorvolare i dettagli di come questo problema è stato risolto. Ad esempio, potrei evitare il sovraccarico di imbottitura extra lavorando modulo 2p2k+1 per una p che non è una potenza di 2 ... ma poi le cose si rompono più tardi, quando ho solo non-power- di 2 fattori rimanenti e non è possibile applicare Cooley-Tukey senza raddoppiare il numero di pezzi. Inoltre, p potrebbe non avere un modulo moltiplicativo inverso 2p+1 . Quindi ci sono ancora fattori forzati per l'introduzione di 2.

Come scelgo l'anello da usare durante la fase ricorsiva, senza soffiare sulla complessità asintotica?

Oppure, in forma di pseudo codice:

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);
  ...

Si prega di non pubblicare la stessa domanda su più siti . Ogni comunità dovrebbe avere una buona dose di risposta senza che il tempo di nessuno venga perso. Ti suggerisco di eliminare una delle due copie.
DW,

@DW Fatto. Ho inviato un post incrociato dopo che cs non ha dato alcuna risposta per una settimana, immaginando che fosse troppo difficile per quel sito. Stavo per ricollegare tutte le risposte ovviamente.
Craig Gidney,

Capisco. Se viene in futuro, puoi sempre contrassegnare il tuo post per l'attenzione del moderatore e chiedere di farlo migrare, e possiamo spostarlo per te su CSTheory. Grazie per la vostra comprensione!
DW,

3
Esiste una versione dell'algoritmo che funziona con i numeri modulo del modulo : A. Schönhage. Algoritmi asintoticamente veloci per la moltiplicazione e la divisione numerica di polinomi con coefficienti complessi. In EUROCAM '82: European Computer Algebra Conference, Lect. Note Comp. Sci. 144, 3-15. iai.uni-bonn.de/~schoe/publi39.dvi2ν2n
Markus Bläser

IIRC hai avuto una risposta parziale alla domanda CS ora cancellata. Sembra un peccato perderlo. Potresti includerlo qui (nella domanda, in modo che la domanda non sia contrassegnata come già risposta)?
Peter Taylor,

Risposte:


4

Questa risposta è tratta dall'articolo "Algoritmi asintoticamente veloci per la muitiplicazione numerica e la divisione di polinomi con coefficienti complessi" che Markus ha collegato nei commenti.


Vuoi quadrare un numero bit, modulo . Ecco cosa fai:n2n+1

  • Trova e che Satisfy e .psn=(p1)2ssp2s

  • Scegli il numero di pezzi in cui dividere gli bit e i parametri corrispondenti per le dimensioni del pezzo:2mn

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

    Si noti che e continuano a soddisfare l' invariante . Si noti inoltre che è soddisfatto, quindi l'ingresso si adatta con spazio per carry.s2p2s2p22s22m2s2p22n+m+1

  • Esegui la convoluzione negaciclica basata su FFT sui pezzi e il resto, come al solito.

Quindi questa è l'idea generale: un fattore di imbottitura logaritmica . Ora per l'analisi della complessità. La FFT impiegherà lavoro da fare e stiamo ricorrendo a pezzi di dimensioni , quindi ora possiamo fare matematica estremamente approssimativa con la relazione di ricorrenza wrt :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

Il che sembra giusto, anche se ho barato parecchio in quei passaggi.

Il "trucco" sembra essere il fatto che finiamo con invece di nel costo base. Ci sono ancora due moltiplicazioni per due per livello ricorsivo, come mi stavo lamentando nella domanda, ma ora la metà di sta pagando i doppi dividendi, quindi tutto funziona. Quindi, alla fine, cancelliamo il fattore aggiuntivo di (che in realtà è un fattore di ) grazie a rendere inizialmente logaritmicamente grande rispetto a inizialmente.s2ssslognps

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.