Comment la bague intérieure est-elle choisie dans l'algorithme Schönhage – Strassen?


9

J'ai essayé d'implémenter l'algorithme de multiplication de Schönhage – Strassen, mais j'ai rencontré une pierre d'achoppement à l'étape récursive.

J'ai une valeur avec n bits et je veux calculer . Je pensais à l'origine que l'idée était de choisir un tel que , diviser en morceaux chacun avec bits, appliquer la convolution de SSA tout en travaillant modulo , un anneau avec bits de capacité par valeur, puis remontez les morceaux. Cependant, la sortie de la convolution a un peu plus de bits (c'est-à-direxnx2(mod2n+1)k4k2nx2k2k122k+12k2n>2kbits par valeur de sortie, ce qui est supérieur à la capacité de l'anneau, car chaque valeur de sortie étant une somme de plusieurs produits), cela ne fonctionne donc pas. J'ai dû ajouter un facteur supplémentaire de 2 de rembourrage.

Ce facteur supplémentaire de 2 dans le rembourrage ruine la complexité. Cela rend mon étape récursive trop chère. Au lieu d'un algorithme , je me retrouve avec un algorithme .F(n)=nlgn+nF(2n)=Θ(nlgnlglgn)F(n)=nlgn+nF(4n)=Θ(nlg2n)

J'ai lu quelques références liées à partir de wikipedia, mais elles semblent toutes masquer les détails de la façon dont ce problème est résolu. Par exemple, je pourrais éviter la surcharge de remplissage supplémentaire en travaillant modulo pour un qui n'est pas une puissance de 2 ... mais alors les choses se cassent plus tard, quand je n'ai que de la non-puissance- de-2 facteurs restants et ne peuvent pas appliquer Cooley-Tukey sans doubler le nombre de pièces. De plus, peut ne pas avoir un modulo inverse multiplicatif . Il y a donc toujours des facteurs forcés de 2 introduits.2p2k+1pp2p+1

Comment choisir la bague à utiliser lors de l'étape récursive, sans souffler la complexité asymptotique?

Ou, sous forme de pseudo code:

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

Veuillez ne pas poster la même question sur plusieurs sites . Chaque communauté devrait avoir une chance honnête de répondre sans perdre de temps. Je vous suggère de supprimer l'un des deux exemplaires.
DW

@DW Terminé. J'ai posté après que cs n'ait donné aucune réponse pendant une semaine, pensant que c'était trop difficile pour ce site. J'allais évidemment relier toutes les réponses.
Craig Gidney

Je comprends. S'il apparaît à l'avenir, vous pouvez toujours signaler votre message à l'attention du modérateur et demander à ce qu'il soit migré, et nous pouvons le déplacer pour vous vers CSTheory. Merci de votre compréhension!
DW

3
Il existe une version de l'algorithme qui fonctionne avec des nombres modulo de la forme : A. Schönhage. Algorithmes asymptotiquement rapides pour la multiplication et la division numériques de polynômes à coefficients complexes. In EUROCAM '82: European Computer Algebra Conference, Lect. Notes Comp. Sci. 144, 3-15. iai.uni-bonn.de/~schoe/publi39.dvi2ν2n
Markus Bläser

IIRC vous avez eu une auto-réponse partielle sur la question CS maintenant supprimée. Il semble dommage de perdre cela. Pourriez-vous l'inclure ici (dans la question, afin que la question ne soit pas marquée comme ayant déjà été répondue)?
Peter Taylor

Réponses:


4

Cette réponse est tirée de l'article «Algorithmes asymptotiquement rapides pour la multiplication numérique et la division de polynômes à coefficients complexes» que Markus a lié dans les commentaires.


Vous voulez mettre au carré un nombre à bits, modulo . Voici ce que vous faites:n2n+1

  • Trouvez et qui satisfont et .psn=(p1)2ssp2s

  • Choisissez le nombre de pièces pour diviser les bits et les paramètres correspondants pour les tailles de pièces:2mn

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

    Notez que et continuent de satisfaire l' . Notez également que est satisfait, donc l'entrée correspond à la place pour les reports.s2p2s2p22s22m2s2p22n+m+1

  • Effectuez la convolution négacyclique basée sur la FFT sur les pièces et le reste, comme d'habitude.

Voilà donc l'idée générale: un facteur de remplissage logarithmique . Maintenant pour l'analyse de complexité. La FFT nécessitera un travail de , et nous récursions sur des pièces de de taille , donc maintenant nous pouvons faire des calculs extrêmement approximatifs avec la relation de récurrence 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

Ce qui semble correct, même si j'ai beaucoup triché dans ces étapes.

Le «truc» semble être le fait que nous nous retrouvons avec au lieu de dans le coût de base. Il y a encore deux multiplications par deux par niveau récursif, comme je me plaignais de la question, mais maintenant la réduction de moitié de paie des doubles dividendes, donc tout fonctionne. Ensuite, à la fin, nous annulons le facteur supplémentaire de (qui est en fait un facteur de ) grâce à la taille de logarithmiquement grande par rapport à initialement.s2ssslognps

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.