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