mpz_multi_invert.cc

Go to the documentation of this file.
00001 // compute a[i]^(-1) mod n, i=0..k-1 
00002 // written by Thorsten Reinecke, 1999-11-21
00003 // last change: 2003-11-10
00004 
00005 /*
00006 
00007   Invertierung nach folgendem Prinzip:
00008    (1/x)*(1/y)=1/(x*y)  ["Additivität": f(x)*f(y)=f(x*y)]
00009    (1/x)=y*(1/(x*y))    ["???": f(x)=y*f(x*y)]
00010    also: anstatt 1/x und 1/y direkt zu berechnen,
00011    berechnen wir stattdessen z=1/(x*y) und erhalten 1/x=y*z, 1/y=x*z.
00012    Mehrere Kehrwerte können also zusammen schneller berechnet werden:
00013   
00014   a,b sind Felder der Größe k (a[0]..a[k-1], b[0]..b[k-1]),
00015   in a stehen die Argumente, in b anschließend die Kehrwerte,
00016   a und b müssen disjunkten Speicherplatz belegen;
00017   das Resultat ist undefiniert, falls eines der zu invertierenden Elemente
00018   nicht invertierbar ist (mithin also das Produkt der zu invertierenden
00019   Elemente nicht invertierbar ist).
00020  
00021   Algorithmus:
00022 
00023   multi_invert(var array of nat b, const var array of nat a, int k, nat n)
00024     {
00025       b[0]:=a[0];
00026       for i from 1 to k-1 do b[i]:=b[i-1]*a[i] mod n; od;
00027       x:=1/b[k-1] mod n; // x = 1/(a[0]*a[1]*...*a[k-1]) mod n
00028       for i from k-1 downto 1 do b[i]:=b[i-1]*x mod n; x:=x*a[i]; od;
00029       b[0]:=x;
00030     }
00031 
00032   Kosten: 1 Invertierung, 3*(k-1) modulare Multiplikationen
00033           anstelle von k Invertierungen.
00034   
00035   mögliche Anwendungsprobleme:
00036      Für die Anwendung müssen die k zu invertierenden Zahlen
00037      bereits bekannt sein. In vielen Algorithmen gilt leider
00038      a[i] = f(1/a[j]) für i>j, f ist irgendeine nichttriviale Funktion,
00039      d.h. die zu invertierenden Elemente sind voneinander abhängig und
00040      die Abhängigkeit kann ohne Kehrwertberechnung nicht beseitigt werden.
00041 
00042   Referenz:
00043     Peter L. Montgomery, "Speeding the Pollard and Elliptic Curve Methods
00044      of Factorization", Mathematics Of Computation, Vol. 48 (177), January 1987,
00045      pages 243-264, (hier: insbesondere Seite 260)  
00046 
00047 
00048  Bemerkung am Rande:
00049   Wie bei so vielen Algorithmen, auf die man sicher auch selbst gekommen wäre,
00050   wenn man sich "nur" das richtige Problem gestellt und konzentriert
00051   nachgedacht hätte, so findet sich auch dieser Algorithmus in der Literatur.
00052   Was mal wieder beweist, dass die eigentliche Leistung oftmals nicht in einer
00053   neuen algorithmischen Idee an sich besteht, sondern vielmehr in der Anwendung
00054   hinlänglich bekannter Eigenschaften auf eine neue, alternative
00055   Problemstellung gesucht werden kann.
00056   -> Wer will schon viele Zahlen auf einmal invertieren? Aber wenn man
00057   nun auf den Gedanken kommt, dass dies (tatsächlich) effizient möglich ist?
00058   Das müßte sich doch irgendwie anwenden lassen! -> Und wenn das der Fall ist,
00059   dann will man viele Zahlen auf einmal invertieren! -> Diesen Zusammenhang,
00060   der eine Problemtransformation ermöglicht, erkannt (und bekannt gemacht) zu
00061   haben, darin besteht die eigentliche wissenschaftliche Leistung...
00062   In dem oben angegebenen, sehr lesenswerten Paper hagelt es nur so von
00063   solchen (teilweise zitierten) "Problemtransformationen"...
00064 
00065 */
00066 
00067 
00080 
00081 // Implementierung:
00083 
00084 #include <gmp.h>
00085 
00086 int mpz_multi_invert(mpz_t *b, const mpz_t *a, int k, const mpz_t n)
00087 {
00088  /* 
00089     a and b MUST be disjoint arrays!
00090     returns 1, if inverse of (a[0]*...*a[k-1]) (mod n) exist
00091                and sets b[i]=a[i]^(-1) mod n, for i=0...k-1
00092     returns 0, otherwise
00093  */
00094   mpz_set(b[0],a[0]);
00095   for (int i=1; i<k; ++i)
00096    { 
00097      mpz_mul(b[i],b[i-1],a[i]); mpz_mod(b[i],b[i],n);
00098    }
00099   mpz_t x;
00100   mpz_init(x);
00101   if (mpz_invert(x,b[k-1],n)==0) { mpz_clear(x); return 0; } // inverse does not exist
00102   for (int i=k-1; i>0; --i)
00103    {
00104      mpz_mul(b[i],b[i-1],x); mpz_mod(b[i],b[i],n);
00105      mpz_mul(x,x,a[i]); mpz_mod(x,x,n);
00106    }
00107   mpz_set(b[0],x);
00108   mpz_clear(x);
00109   return 1; // inverse exist
00110 }
00111 
00112 
00113 #if 0
00114 
00115 #include <iostream>
00116 
00117 int main()
00118 {
00119   using std::cout;
00120   using std::endl;
00121   const unsigned int p = 23;
00122   mpz_t n, x, a[p], b[p];
00123   mpz_init(x);
00124   mpz_init(n); mpz_set_ui(n,p);
00125   for (unsigned int i=0; i<p; ++i)
00126    {
00127      mpz_init(a[i]);
00128      mpz_set_ui(a[i],i);
00129      mpz_init(b[i]);
00130    }
00131   if (mpz_multi_invert(&b[1],&a[1],p-1,n)==0) cout << "Inverse doesn't exist!" << endl;
00132   for (unsigned int i=1; i<p; ++i)
00133    {
00134      cout << i << "^(-1) mod " << p << " = ";
00135      mpz_out_str(stdout,10,b[i]);
00136      cout << " --check--> ";
00137      mpz_mul_ui(x,b[i],i); mpz_mod(x,x,n);
00138      mpz_out_str(stdout,10,x);
00139      cout << endl;
00140    }
00141 }
00142 #endif

Generated on Wed Nov 7 23:29:25 2007 for Qsieve by  doxygen 1.5.4