2009-07-02 8 views
4

multiplication de deux nombres à n bits A et B peut être compris comme une somme des déplacements:OU-multiplication sur les grands entiers

(A << i1) + (A << i2) + ... 

où i1, i2, ... sont des nombres de bits qui sont établis à 1 B.

permet maintenant remplacer PLUS avec ou pour obtenir une nouvelle opération j'ai réellement besoin:

(A << i1) | (A << i2) | ... 

Cette opération est tout à fait similaire à la multiplication régulière pour lesquels il existe de nombreux algorithmes plus rapides (Schönhage-Strassen par exemple). Un algorithme similaire est-il présenté ici?

La taille des nombres est de 6000 bits.

modifier: (quelle idée pourquoi) Pour une raison que je n'ai pas de lien/bouton pour écrire des commentaires alors je vais modifier ma INSEAD question. Je cherche en effet plus vite que l'algorithme O (n^2) pour l'opération définie ci-dessus. Et oui, je suis conscient que ce n'est pas une multiplication ordinaire.

+0

Pouvez-vous remplacer plus avec ou? Cela ne donne pas de résultats incorrects, si vous avez le même bit dans n'importe quelle position sur deux des chaînes, vous obtiendrez 1 et non 0. – JSchlather

+3

Il ne dit pas que vous obtiendrez les mêmes résultats. Il se demande s'il existe des algorithmes d'accélération pour la multiplication "OU" de la même manière qu'il existe des algorithmes d'accélération pour une multiplication régulière. –

+0

Quelle est la taille de vos numéros biginteger? (combien de bits) –

Répondre

1

Existe-t-il un algorithme similaire? Je pense probablement pas.

Existe-t-il un moyen d'accélérer les choses au-delà de O (n^2)? Peut-être. Si l'on considère un nombre A être l'analogue A (x) = Σ un n x n n sont les chiffres binaires de A, votre opération avec salles d'opération au niveau du bit (appelons-le A ⊕ B) peut être exprimée comme suit, où "⇔" signifie "analogue"

a ⇔ a (x) = Σ un n x n

b ⇔ b (x) = b Σ n x n

C = A ⊕ B ⇔ C (x) = f (A (x) B (x)) = f (V (x)) où f (V (x)) = f (Σ v n x n) = Σ u (v n) x n où u (v n) = 0 si v n = 0, u (v n) = 1 sinon. Fondamentalement, vous faites l'équivalent de prendre deux polynômes et de les multiplier ensemble, puis d'identifier tous les termes non-zéro. Du point de vue de la chaîne de bits, cela signifie traiter la chaîne de bits comme un tableau d'échantillons de zéros ou de uns, convolving les deux tableaux, et réduire les échantillons résultants qui sont différents de zéro. Il y a des algorithmes de convolution rapides qui sont O (n log n), en utilisant des FFT par exemple, et l'étape de «collaps» ici est O (n) ... mais je me demande si l'évaluation O (n log n) de la convolution rapide traite quelque chose (comme la multiplication de grands entiers) comme O (1) de sorte que vous n'obtiendrez pas un algorithme plus rapide. Ou bien, ou les constantes pour les ordres de croissance sont si grandes que vous devriez avoir des milliers de bits avant d'avoir un avantage de vitesse. ORing est si simple.

modifier: il semble y avoir quelque chose appelé « convolution binaire » (voir this book par exemple) qui semble terriblement pertinent ici, mais je ne peux pas trouver de bons liens avec la théorie sous-jacente et s'il y a des algorithmes rapides .

modifier 2: peut-être le terme est « convolution logique » ou « convolution bitwise » ... voici un page from CPAN (bleah!) Parler un peu à ce sujet avec Walsh et Hadamard transforme qui sont peu l'équivalent bitwise à transformées de Fourier ... hmm, non, cela semble être l'analogue pour XOR plutôt que pour OU.

0

Je présume, vous demandez le nom de la technique additif vous avez donné
lorsque vous écrivez « est un algorithme similaire pour le fonctionnement, j'ai présenté ici? » ...

Avez-vous regardé la technique Peasant multiplication ?
Veuillez lire la description de Wikipedia si vous n'obtenez pas la 3ème colonne dans cet exemple.

B X A 
27 X 15 : 1 
13 30 : 1 
6 60 : 0 
3 120 : 1 
1 240 : 1 

B is 27 == binary form 11011b 

27x15 = 15 + 30 + 120 + 240 
     = 15<<0 + 15<<1 + 15<<3 + 15<<4 
     = 405 

Cela vous semble familier?


Voici votre algorithme.

  1. Choisissez le plus petit nombre que votre A
    • Initialiser C comme zone de résultat
    • alors que B est non nul,
      1. si lsb de B est 1, ajouter A à C
    • décalage vers la gauche Une fois
    • décalage vers la droite B une fois
    • C a votre résultat de la multiplication (sauf si vous rouliez sur sizeof C)

Mise à jour Si vous essayez d'obtenir un algorithme rapide pour le décalage et l'opération OU sur 6000 bits,
, il peut en être un. Je vais y réfléchir un peu plus.

Il semblerait que "flouter" un nombre sur l'autre. Intéressant.
Un exemple assez grossière ici,

110000011 X 1010101 would look like 
     110000011 
    110000011 
    110000011 
110000011 
--------------- 
111111111111111 

Le nombre de 1 s dans les deux chiffres décidera de la quantité de flou vers un numéro avec tous ses bits fixés.
Je me demande ce que vous voulez faire avec elle ...


Update2 Telle est la nature du + Maj ou opération avec deux numéros 6000 bits.

  1. Le résultat sera 12000 bits de cours
    • l'opération peut se faire avec deux flux de bits; mais, ne doit pas être fait à son ensemble
    • la partie « milieu » du flux 12000 bits sera presque certainement tous s (fourni deux numéros ne sont pas nuls)
    • le problème sera à identifier la profondeur à laquelle nous devons traiter cette opération pour obtenir les deux extrémités du flux
    • 12000 bits le modèle aux deux extrémités du flux dépendra de la plus grande consécutive de la présente dans les deux numéros

Je n'ai pas encore d'algorithme propre pour ça. Ont mis à jour pour quelqu'un d'autre qui veulent revérifier ou aller plus loin d'ici. De plus, décrire la nécessité d'une telle opération pourrait motiver davantage d'intérêt :-)

+2

La multiplication paysanne est un algorithme "régulier" pour la multiplication, c'est toujours O (n^2) pour l'arithmétique bignum où n est le nombre de bits. –

+0

Hmmm, j'ai lu votre commentaire dans la question maintenant. Je n'ai pas interprété sa question de cette façon. Mais laissez-moi comprendre cela mieux. Si vous faites une opération OU, avec des changements pouvez-vous obtenir plus optimal que la boucle de décalage-OU de base? – nik

+0

@Lukasz, pouvez-vous élaborer votre question un peu plus? – nik

0

Vous pouvez le faire O (# 1 bits dans A * # 1 bits dans B).

a-bitnums = set(x : ((1<<x) & A) != 0) 
b-bitnums = set(x : ((1<<x) & B) != 0) 

c-set = 0 
for a-bit in a-bitnums: 
    for b-bit in b-bitnums: 
    c-set |= 1 << (a-bit + b-bit) 

Cela peut être utile si A et B sont rares dans le nombre de bits à 1 présents.

0

Le meilleur que je pourrais utiliser est d'utiliser une sortie rapide sur la logique de bouclage. Combiné avec la possibilité d'utiliser l'approche non-zéro comme décrit par themis, vous pouvez répondre à votre question en inspectant moins de 2% du problème N^2. Vous trouverez ci-dessous un code qui donne le minutage pour les nombres compris entre 80% et 99% de 0%. Lorsque les nombres obtiennent environ 88% de zéro, l'approche de leur approche passe à être meilleure (mais elle n'a pas été codée dans l'exemple ci-dessous).

Cette solution n'est pas très théorique, mais elle est pratique.

OK, voici quelques « théorie » de l'espace de problème:

Fondamentalement, chaque bit de X (la sortie) est le OU sommation des bits sur la diagonale d'un réseau construit en ayant les bits de A le long du haut (MSB à LSB de gauche à droite) et les bits de B sur le côté (MSB à LSB de haut en bas). Puisque le bit de X est 1, s'il y en a un sur la diagonale, vous pouvez effectuer une sortie rapide sur la traversée de la cellule.

Le code ci-dessous fait cela et montre que même pour les nombres qui sont ~ 87% de zéro, il suffit de vérifier ~ 2% des cellules. Pour les nombres plus denses (plus de 1), ce pourcentage diminue encore plus.En d'autres termes, je ne m'inquiéterais pas des algorithmes complexes et je ferais juste une vérification logique efficace. Je pense que l'astuce consiste à regarder les bits de votre sortie comme les diagonales de la grille par opposition aux bits de A shift-OU avec les bits de B. La chose la plus délicate est de garder une trace des bits que vous pouvez regarder en A et B et comment indexer les bits correctement.

Espérons que cela a du sens. Faites-moi savoir si j'ai besoin d'expliquer un peu plus loin (ou si vous trouvez des problèmes avec cette approche). REMARQUE: Si nous connaissions un peu mieux l'espace de votre problème, nous pourrions optimiser l'algorithme en conséquence. Si vos nombres sont pour la plupart non-nuls, alors cette approche est meilleure que celle-ci, car il en résulterait plus de calculs et plus d'espace de stockage nécessaire (sizeof (int) * NNZ).

NOTE 2: Cela suppose que les données sont essentiellement des bits, et j'utilise BitArray de .NET pour stocker et accéder aux données. Je ne pense pas que cela causerait des maux de tête importants lorsqu'ils sont traduits dans d'autres langues. L'idée de base s'applique toujours.

using System; 
using System.Collections; 

namespace BigIntegerOr 
{ 
    class Program 
    { 
     private static Random r = new Random(); 

     private static BitArray WeightedToZeroes(int size, double pctZero, out int nnz) 
     { 
      nnz = 0; 
      BitArray ba = new BitArray(size); 
      for (int i = 0; i < size; i++) 
      { 
       ba[i] = (r.NextDouble() < pctZero) ? false : true; 
       if (ba[i]) nnz++; 
      } 
      return ba; 
     } 

     static void Main(string[] args) 
     { 
      // make sure there are enough bytes to hold the 6000 bits 
      int size = (6000 + 7)/8; 
      int bits = size * 8; 

      Console.WriteLine("PCT ZERO\tSECONDS\t\tPCT CELLS\tTOTAL CELLS\tNNZ APPROACH"); 
      for (double pctZero = 0.8; pctZero < 1.0; pctZero += 0.01) 
      { 
       // fill the "BigInts" 
       int nnzA, nnzB; 
       BitArray a = WeightedToZeroes(bits, pctZero, out nnzA); 
       BitArray b = WeightedToZeroes(bits, pctZero, out nnzB); 

       // this is the answer "BigInt" that is at most twice the size minus 1 
       int xSize = bits * 2 - 1; 
       BitArray x = new BitArray(xSize); 

       int LSB, MSB; 
       LSB = MSB = bits - 1; 

       // stats 
       long cells = 0; 
       DateTime start = DateTime.Now; 

       for (int i = 0; i < xSize; i++) 
       { 
        // compare using the diagonals 
        for (int bit = LSB; bit < MSB; bit++) 
        { 
         cells++; 
         x[i] |= (b[MSB - bit] && a[bit]); 
         if (x[i]) break; 
        } 

        // update the window over the bits 
        if (LSB == 0) 
        { 
         MSB--; 
        } 
        else 
        { 
         LSB--; 
        } 

        //Console.Write("."); 
       } 

       // stats 
       TimeSpan elapsed = DateTime.Now.Subtract(start); 
       double pctCells = (cells * 100.0)/(bits * bits); 
       Console.WriteLine(pctZero.ToString("p") + "\t\t" +elapsed.TotalSeconds.ToString("00.000") + "\t\t" + 
        pctCells.ToString("00.00") + "\t\t" + cells.ToString("00000000") + "\t" + (nnzA * nnzB).ToString("00000000")); 
      } 

      Console.ReadLine(); 
     } 
    } 
} 
0

utiliser tout simplement FFT polynomiale algorithme de multiplication et de transformer tous les coefficients résultants qui sont supérieures ou égales à 1 1.

Exemple:

10011 * 10001 
[1 x^4 + 0 x^3 + 0 x^2 + 1 x^1 + 1 x^0] * [1 x^4 + 0 x^3 + 0 x^2 + 0 x^1 + 1 x^0] 
== [1 x^8 + 0 x^7 + 0 x^6 + 1 x^5 + 2 x^4 + 0 x^3 + 0 x^2 + 1 x^1 + 1 x^0] 
-> [1 x^8 + 0 x^7 + 0 x^6 + 1 x^5 + 1 x^4 + 0 x^3 + 0 x^2 + 1 x^1 + 1 x^0] 
-> 100110011 

Pour un exemple de l'algorithme, chèque :

http://www.cs.pitt.edu/~kirk/cs1501/animations/FFT.html

BTW, il est de la complexité linearithmic, à savoir O (n log (n))

Voir aussi:

http://everything2.com/title/Multiplication%2520using%2520the%2520Fast%2520Fourier%2520Transform

+0

C'est ce que j'ai dit dans ma réponse.(ta réponse est un peu plus directe, la mienne est plus formelle) –

Questions connexes