Fattorizzare i numeri interi (di Cristiano Armellini, [email protected]) Ci sono molti algoritmi efficienti per fattorizzare numeri interi e molti sono i testi dove è possibile trovare una completa trattazione sull'argomento, Qui vogliamo proporre dei sistemi (in parte abbastanza originali e forse relativamente veloci ma senza nessuna pretesa da parte nostra) per cercare di trovare i fattori di un numero intero con metodi algebrici e facilmente implementabili in un moderno calcolatore. L'idea è comunque quella di trovare dei limiti entro cui cercare con maggiori probabilità le soluzioni. Nella ricerca delle soluzioni prendiamo principalmente in esame solo valori interi positivi benché in linea di principio potremmo considerare anche i negativi dal momento che 9 = 3*3 = (-3)*(-3). Facendo girare tutti gli algoritmi in parallelo aumenta la probabilità di trovare soluzioni in tempi rapidi. Ogni algoritmo sarà implementato con una applicazione scritta in C/C++ o in PARI/Gp per la gestione dei grandi numeri 1
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
int main(int argc, char *argv[]){long double x, p;long i = 0;int cifre;cout << "inserisci il numero di cifre del numero intero";cin >> cifre;cout <<" inserisci il numero da fattorizzare";cin >> p;x = pow(10, int(cifre/2)-1 );do{
2
x = x + 1;i = i + 1;} while(int(p/x) != (p/x));cout << "un fattore e' " << x;cout <<" passi di elaborazione: " << i;cin >> "---------";return 0;}
LISTATO IN PARI/GP
{ algo2(cifre) = local(x, p);
p = nextprime(10^10)*nextprime(10^12);
x = 10^(floor(cifre/2)-1);
while(floor(p/x) != (p/x), x=x+1);
print(x);
print("-----");
print(p/x);
return (1);}
3
Primo algoritmo (AL-1) (variante della fattorizzazione alla Fermat)p=x1 x2
(in generale non è detto che ci siano solo due fattori come nell'RSA ma il
procedimento di fattorizzazione può essere ripetuto per ognuno dei due fattori trovati finché
non si arriva a fattori che hanno decomposizione banale cioè che sono primi).
x1>√ p ,x1<p2
x2<√ px1,2>0
x1ORx2>W
dove OR sta ad indicare che la relazione vale per una radice o per entrambe
tutti i numeri interi positivi (ove la ricerca può essere fatta in modo sequenziale
oppure random)
Attenzione: per P molto grande A(p) cioè il numero dei primi minori di P è
approssimativamente p/log(p)
LISTATI IN C++ E IN VISUAL BASIC .NET
Nei codici sorgenti in C++ che seguono ho usato l'espressione 'int(p/x)' per
semplicità tuttavia sarebbe meglio sostituirla con la più esatta sintatticamente
floor(fabs(p/x)).
#include <stdio.h>#include <iostream.h>
4
#include <math.h>
int main(int argc, char *argv[]){ long double x, p; long i, j; j = 0; cout << "inserisci il numero da fattorizzare "; cin >> p; for( i = int(sqrt(p)); i <= int(p/2); i++) { j = j +1; if(int(p/i) == (p/i)) { cout << "fattore: " << i << "\n"; cout << "passi: " << j << "\n"; } } cin >> "-----------------"
return 0;}
lo stesso algoritmo può essere implementato in Visual Basic .Net usando per la ricerca
dei fattori un generatore di numeri casuali: la ricerca si rileva molto più veloce:
Module Module1
Sub Main() Dim p As Decimal Dim generator As New Random Dim a As Decimal Dim i As Integer i = 0 Console.WriteLine("inserisci il numero da fattorizzare") p = Console.ReadLine() Do a = generator.Next(2, Int(Math.Sqrt(p))) i = i + 1 Loop While ((p / a) <> Int(p / a)) Console.Write("fattore: ") Console.Write(a) Console.Write("passi: ") Console.Write(i) Console.ReadLine() End Sub
Supponiamo di stare a considerare un tipico problema RSA quindi p ha solo due fattori ed s è
sicuramente un numero pari essendo la somma di due primi quindi di due numeri dispari.
Questo algoritmo è particolarmente veloce quando x1 , x2 10n/2 dove p 10n ma è comunque
molto oneroso quando le radici differiscono per almeno 3-4 cifre e perché comunque fa uso
delle radici quadrate che sono complesse da calcolare per valori elevati.
Cerchiamo di velocizzare la ricerca dei fattori usando l’elemento W precedentemente
descritto.
x1=s−√s2−4 p
2>W
mi porta a dover risolvere il seguente sistema:
s>2√ p oppure s<−2√ p
s<W 2+ pW
,s−2W >0
x2=s+√s2−4 p
2>W
mi porta a dover risolvere il seguente sistema:
8
2W−s<0s2−4 p>0
unito alle soluzioni del sistema
s2−4 p>02W−s>0
s>W 2+ pW
Osservazione
1)
s2−4 p>0
equivale alla ben nota relazione
( x+ y2 )
2
>xy
2) se
d= s2
allora
x1,2=d±√d2−p
(più avanti approfondiremo questo caso)
3)
x+ y< xyk
, xy=p
porta a
x2 k+ pk−pxxk
<0
ovvero
k< px
x2+ p
dove pi può supporre
k≈10int( n
2)
con n il numero delle cifre di P. Trovare
k=int ( px
x2+ p)
ci aiuta a calcolare una
stima superiore per s = x+y per aumentare anche grazie al punto 1) la ricerca delle soluzioni.
LISTATO IN C++
#include <stdio.h>#include <math.h>#include <iostream.h>int main(int argc, char *argv[]){ long double p, x, y, s;
9
long i; cout << "inserisci il numero da fattorizzare "; cin >> p; s = 2* int(sqrt(p)); i = 0; do { x = (s + sqrt(pow(s,2)-4*p))/2; y = (s - sqrt(pow(s,2)-4*p))/2; s = s+1; i = i+1; }while((int(p/x) != (p/x)) || (int(p/y) != (p/y))) ; // si può mettere && (and) al posto di || (OR) ma una delle due cout << "un fattore e' " << x << "\n"; //soluzioni trovate potrebbe non essere intera cout << "un fattore e' " << y << "\n"; cout << "passi: " << i; cin >> "--------------------"; return 0;}
{algo1(p) = local( s, d, x, y);s = 2 * floor(sqrt(p));d = sqrt(abs(s^2-4*p));x = (s + d)/2;y = (s - d)/2;while (floor(p/x) != (p/x) && floor(p/y) != (p/y),s++;d = sqrt(abs(s^2-4*p));x = (s + d)/2;y = (s - d)/2);print(x);print("----");print(y);return (1);}
10
{algo1(p) = local(s, d, x, y);s = floor(sqrt(p));d = sqrt(abs(s^2-p));x = (s + d);y = (s - d);while (floor(p/x) != (p/x) && floor(p/y) != (p/y),s = s+1;d = sqrt(abs(s^2-p));x = (s + d);y = (s - d));print(x);print("----");print(y);return (1);}
{algo2b(p) = local(b, d, x, y);b = floor(sqrt(p));d = sqrt(abs(b^2-p));x = b + d;y = b - d;while (floor(p/x) != (p/x) && floor(p/y) != (p/y),b++ ;d = sqrt(abs(b^2-p));x = b + d;y = b - d);print(x);print("----");print(y); return (1);}
VARIANTE
s2>4 p s2=4 p+a ,a>0 , p=xy a numero pari perché differenza di due numeri pari
Ciò porta all’equazione biquaratica
x4−x2 (a+2 p )+ p2=0
11
x1,2=√ a+2 p±√a2+4ap2
12
LISTATO IN C++
#include <stdio.h>#include <math.h>#include <iostream.h>int main(int argc, char *argv[]){ float a, x, y, p; long double i; cout << "inserisci il numero da fattorizzare "; cin >> p; a = 0; i = 0; do{ x = sqrt( (a+2*p+sqrt(pow(a,2)+4*a*p))/2); y = sqrt( (a+2*p-sqrt(pow(a,2)+4*a*p))/2); a = a+1; i = i+1; } while ((int(p/x) != (p/x)) || (int(p/y) != (p/y))) ; // si può mettere && (and) al posto di || (OR) ma una
cout << "fattore: " << x << "\n"; // delle due soluzioni trovate potrebbe non essere intera cout << "fattore: " << y << "\n"; cout << "passi: " <<i << "\n"; cin >> " -----"; return 0;}
da cui si può partire direttamente da m per trovare le soluzioni
x1=m+√m2+4 p
2
x2=−m+√m2+4 p
2
con
m=±√s2−4 p,
s>m ,s>2√ p
(possiamo considerare anche le soluzioni negative di X1, X2 quelle con il segno - ovvero
14
x1=m−√m2+4 p
2
x2=−m−√m2+4 p
2
ma il ragionamento sarebbe identico a quello già
fatto)
Sempre nell’ipotesi di considerare un problema di fattorizzazione RSA usiamo il fattore W per
accelerare la ricerca delle soluzioni. Consideriamo per semplicità le soluzioni positive ma lo
stesso discorso vale per quelle negative
x=m+√m2+4 p
2>W
porta a risolvere il seguente sistema
m2+4 p>02W−m<0
unito alle soluzioni di
m−2W >0
m>W 2−pW
x =
−m+√m2+4 p2
>W
porta a dover risolvere il sistema
m2+4 p>02W +m>0
m< p−W 2
W
15
x=−m−√m2+4 p
2>W
allora
m<−2Wm2+4 p>0
m<W 2−pW
LISTATO IN C++
#include <stdio.h>#include <math.h>#include <iostream.h>int main(int argc, char *argv[]){ long double m, x, y, p; long i; cout << "inserisci il numero da fattorizzare "; cin >> p; m = 0; i = 0; do{ x = (m + sqrt(pow(m, 2) + 4*p))/2; y = (-m + sqrt(pow(m, 2) + 4*p))/2; m = m+1; i = i+1; } while ( (int(p/x) != (p/x)) || (int(p/y) != (p/y))); // si può mettere && (and) al posto di || (OR) ma una
cout << "fattore: " << x << "\n"; // delle due soluzioni trovate potrebbe non essere intera cout << "fattore: " << y << "\n"; cout << "passi: " <<i << "\n"; cin >> " -----";
(possiamo considerare anche la soluzione negativa quella
con segno - , però poi occorre calcolare il suo valore assoluto)
17
Come nei casi precedenti
x=a+√a2−p>W porta a:
a2−p>0W −a<0 unito alle soluzioni di
a>√ p ,a<−√ pa−W <0
a>W 2+ pW
x=a−√a2−p>W porta al sistema di condizioni
a>√ p ,a<−√ pa>W
a<W 2+ pW
allora
b2+ p>0W +b<0 unito a
b2+ p>0W +b>0
b< p−W 2
2W
x=−b−√b2+ p>W ciò porta a:
b<−Wb2+ p>0
b> p−W 2
2W
Wpbbx 2
18
osservazione: se
p=x1 x2
è un problema RSA allora
x1+x2=2a
x1=2a−x2
quindi
x22−2ax2+ p=0
x2=a±√a2−p
con
a2> p
LISTATO IN C++
#include <stdio.h>#include <math.h>#include <iostream.h>int main(int argc, char *argv[]){long double x, y, p;long a;long i = 0;cout<< "inserisci il numero da fattorizzare ";cin >> p;a = int(sqrt(p));a = a+1;do {x = a + sqrt(pow(a,2) -p);y = a - sqrt(pow(a,2) - p);a = a+1;i = i+1;} while( (int(p/x)!=(p/x)) ||(int(p/y) != p/y)) ; // si può mettere && (and) al posto di || (OR) ma una delle duecout << "fattore: " << x << "\n"; // soluzioni trovate potrebbe non essere interacout << "fattore: " << y << "\n";cout << "passaggi: " << i;cin >> "-------------"; return 0;}
int main(int argc, char *argv[]){long double x, y, p;long int i, a, c;a = 0;i = 0;cout << "inserisci numero ";cin >> p;do{ x = sqrt(p+a+sqrt(pow(a,2)+2*p*a)); y = sqrt(p+a-sqrt(pow(a,2)+2*p*a)); a = a+1; // qui è meglio mettere a = a+ 1/2 se non è un problema RSA; i = i +1;}while( (int(p/y) != (p/y)) || (int(p/x) != (p/x)) ) ; // si può mettere && (and) al posto di || (OR) macout << "fattore: " << x << "\n"; // una delle due soluzioni trovate potrebbe non essere intera cout << "fattore: " << y << "\n";cout << "passi: " << i;}
{ long double m, x, y, p; long i; cout << "inserisci il numero da fattorizzare "; cin >> p; x = int(pow(10, log10(p)/2))+1; y = int(pow(10, log10(p)/2))+1; do{ x = x-1; y = y+1; i = i+1; } while ( (int(p/x) != (p/x)) && (int(p/y) != (p/y)) );
cout << "fattore: " << x << "\n"; cout << "fattore: " << y << "\n";
Fattorizzazione per individuazione dell’intervallo migliore
Partendo dalla già nota relazione
x2+ y2
2>xy
e ponendo y = x+a arriviamo a risolvere una disequazione di secondo grado le cui soluzioni sono:
x>−a+√4 p−a2
2 x<
−a−√4 p−a2
2 con 4 p−a2>0
Nell’ipotesi invece che y = x-a ho che
x>a+√4 p−a2
2 x<
a−√4 p−a2
2 con 4 p−a2>0
Il problema è capire quale valore di a assegnare. Nei problemi RSA le radici non sono né troppo
vicine tra loro né troppo lontane quindi un buon valore per a potrebbe essere a=±√ p
Stesso discorso se partiamo da ( x+ y )2>4 p :
per y = x-a ottengo che x> a+2√ p
2, x< a−2√ p
2 per y = x+a x>−a+2√ p
2, x<−a−2√ p
2
38
Teoria dei grandi numeri
Come è possibile gestire in un normale calcolatore numeri molto grandi, diciamo
numeri interi a 200 cifre ? Vediamo l'algoritmo con un esempio e poi generalizziamo con il
formalismo matematico
Supponiamo di voler calcolare 321*44 = 14124
sappiamo che ogni numero intero lo possiamo scrivere nella sua notazione polinomiale:
(3 x2+2 x+1 )(4 x+4 )
con X = 10
svolgiamo la moltiplicazione tra i polinomi e consideriamo che 12 mod 2 = 2 e che
12-(12 mod 10) = 10 (per i coefficienti del polinomio prodotto usiamo l'aritmetica
modulo 10 ovvero se abbiamo coefficienti maggiori o uguali a 10 cioè ad esempio 12
= 10 +2 scriviamo 12 = X+2 per poi continuare a semplificare e a ridurre modulo 10
finché tutti i coefficienti del polinomio sono in base 10)
tralasciano i passaggi abbiamo che
1 x4+4 x3+1 x2+2 x+4
cioè 14124 che è la soluzione
Il caso della somma è ancora più semplice perché39
3 x2+2 x+1+4 x+4=3 x2+6 x+5
cioè 321 + 44 = 365 (anche per la somma se ce ne fosse
stato bisogno avremmo dovuto ridurre i coefficienti del polinomio in base l'aritmetica modulo
10)
Idem è il caso della differenza tra numeri
Questo ci permetterà di programmare un calcolatore per fargli compiere operazioni molto
complesse che un normale PC non riuscirebbe a fare. Infatti dall'algebra elementare esistono
formule che generalizzano la somma, la differenza la moltiplicazioni e la divisione tra polinomi
di qualunque grado
∑i=0
h
(ri xi)+∑
i=0
k
(ui xi )= ∑
i=0
max(h , k )
(ri+ui ) xi
∑i=0
h
ri xi∑i=0
k
ui xi=∑
j=0
h+k
( ∑i+ p= j
ri up ) x j
caso con la virgola:
12,6 +15,4 =
x+2+ 3x+x+5+ 4
x=2 x+7+1=2 x+8
40
Il caso della divisione è un po',più complesso anche se in realtà una divisione non è altro che
una serie di sottrazioni come una moltiplicazione non è che una serie di somme
Il Crivello di Eratostene
crivello di Eratostene (formulazione grafica: le x sono i primi perché non hanno intersezioni
con "gialli")
14
13 Primo
12
11 Primo
10
9
8
7 Primo
6
5 Primo
4
3
2 Primo
1
1 2 3 4 5 6 7
41
Analisi della complessità computazionale di alcuni algoritmi di
fattorizzazione
In precedenti occasioni ci siamo occupati di studiare nuovi algoritmi di fattorizzazione in parte varianti della
fattorizzazione alla Fermat e del metodo delle divisioni banali. In generale gli algoritmi descritti risultano
essere efficienti solo nel caso in cui il numero da fattorizzare p si compone di fattori anche molto grandi ma
dello stesso ordine di grandezza o che comunque hanno un numero di cifre simile ovvero che non
differiscano di più di 3-4 cifre.
Vediamo per alcuni casi semplici come si può affrontare una stima della complessità computazionale.
Sia p=x1 x2 , s=x1+x2 da x2−sx+ p=0 abbiamo che
x1,2=s ±√ s2−4 p
2
Che impone la condizione per l’esistenza del radicale s≥ 2√ p, s intero e pari perché somma di due primi
Quindi se cerchiamo s con s < n e s≥ 2√ p, e pari al massimo arriveremo alla soluzione in un numero di
passi pari a: p2−√ p che in ogni caso è enorme rispetto alle dimensioni di p
42
D’altra parte un po’ meglio va se valutiamo di considerare le soluzioni tra i numeri dispari compresi tra 2 e
√ p (in questo intervallo ci deve essere sempre la soluzione più piccola) perché in questo caso otterrei la
soluzione in al massimo √ p2
−1 passi.
Nelle considerazioni precedenti abbiamo supposto di cercare le soluzioni tra i numeri dispari ipotizzando di
non avere una tavola di primi già a disposizione.
Considerazioni identiche valgono per altri metodi già esposti come per
φ ( p )=( x1−1 ) ( x2−1 )=4a≤ p=¿a≤p4
, p=x1 x2
Dove ovviamente va aggiunta la condizione di a per l’esistenza del radicale.
43
Stima della complessità computazionale della fattorizzazione con il
metodo dell’attacco casuale (rapporti tra statistica e numeri primi)
Supponiamo che p sia un numero da fattorizzare molto grande (es RSA). Sappiamo che il fattore più piccolo
deve necessariamente trovarsi tra 2 e la radice quadrata di p. Supponiamo di avere un calcolatore che
generi numeri casuali e che ogni numero casuale generato sia un numero primo tra 2 e la radice quadrata
di p. Per ogni numero casuale estratto si verifica se è o no un divisore di p e , in caso negativo, si passa al
successivo numero. Ipotizziamo che il nostro programma tenga “memoria” dei numeri estratti in modo che
l generatore di “primi casuali” non possa ripetere mai lo stesso numero.
Al primo tentativo la probabilità di aver trovato la soluzione sarà
P1=1x
logx
= logxx
Ove è stato usato il ben noto teorema dei numeri primi e x=∫(√ p) e log(x) è il logaritmo naturale di x. In
luogo di x/log(x) avremmo potuto anche usare una stima di ∫2
xdy
log ( y)
Al secondo tentativo
P2=1
xlogx
−1
… al k-esimo tentativo
44
Pk=1
xlogx
−k+1
Questa formula ci dà una stima della probabilità che al k-esimo tentativo (k intero) abbiamo di “indovinare”
la soluzione. Come si vede man mano che aumentano i tentativi aumenta la probabilità di “vittoria”,
probabilità che è comunque condizionata dalla grandezza di p numero da fattorizzare. Come si può ben
vedere all’aumentare delle dimensioni di p la probabilità diminuisce in modo esponenziale
Primi e probabilità
Sia datato ora un intervallo [a, b]
Prendiamo a caso un qualunque numero dispari in questo intervallo la probabilità che sia primo è
P=( (b−a)/2b/ log (b)−a/ log (a ) )
Oppure
P=(b−a)/2
∫a
bdy
log( y )
Con log(x) il logaritmo naturale di x e il simbolo = sta per “circa, valore approssimato”
45
Dove abbiamo usato il teorema dei numeri primi, se consideriamo intervalli che contengono numeri primi
molto grandi
46
Codice in C++ per generare una tabella di primi (per applicazioni reali è consigliabile usare una libreria specifica o un programma per la gestione dei grandi numeri come PARI/GP o Python
long mcd(long a, long b) {long t; while(b !=0){ t = b; b = a%b; a = t; } return a; }
using namespace std;
int main(int argc, char *argv[])
{ long k, p; int j; k = 3; p = 2; for(j= 1; j <= 20; j++) { if (mcd(p, k) ==1) { cout << k << "\n"; p = k*p; } k = k+2; } system("PAUSE"); return EXIT_SUCCESS;}
47
Complessità computazionale degli algoritmi di fattorizzazione
In questo articolo valuteremo la complessità computazionale del migliore algoritmi di fattorizzazione
possibile (escludendo quelli di tipo quantistico).
Il teorema di numeri primi afferma che
limx→+∞
π (x )x
ln (x)
=1
ovvero
π ( x ) xln (x )
Dove π ( x ) è il numero dei primi minori o uguali a x. (per x opportunamente grande).
Un formulazione più evoluta dello stesso teorema mostra che
π ( x ) Li ( x )=∫0
xdy
ln ( y)
Ove Li(x) è il logaritmo integrale
Ora prendiamo in esame il problema della fattorizzazione p = ab con a, b primi e supponiamo a < b.
Sappiamo che a deve essere compreso tra 2 e la radice quadrata di p. Il nostro obiettivo è trovare a
perché così b è facilmente calcolabile con b = p/a (problemi di tipo RSA). Per fattorizzare un qualunque
numero intero di procederà ricorsivamente come per p=ab con a, b primi.
Il numero dei numeri primi compresi tra 2 la radice quadrata di p (per p grande) è approssimativamente:
π (√ p )− 2ln (2)
√ pln (√ p)
− 2ln (2)
48
(anche se l’ultimo termine possiamo trascurarlo)
π (√ p ) √ pln(√ p)
Oppure:
π (√ p ) ∫2
√ pdy
ln ( y)
Grazie all’aiuto del software Mathematica stimiamo i passi necessari (nel caso peggiore) per trovare il
fattore più piccolo del numero p = ab con a, b primi molto grandi e a < b. Ci poniamo cioè nella condizione
di disporre di un algoritmo che ci fornisce tutti i numeri primi da 2 alla radice quadrata di p (anzi
supponiamo di averli già calcolati) e che altro non ci rimane da fare che verificare quali di questi numeri
divide p. Ci poniamo nel caso migliore possibile (algoritmo ideale).
Con il Mathematica:
p:=10^12
Sqrt[p]/Log[Sqrt[p]]
N[%]
NIntegrate[1/Log[x], {x, 2, Sqrt[p]}]
Facendo variare p (numero da fattorizzare) costruiamo la seguente tabella:
dimensione (dim) P (numero da fattorizzare = ab Steps (caso peggiore) stimati con
il teorema dei numeri primi
20 10^20 k*10^8
25 10^25 k*10^11
30 10^30 k*10^13
49
35 10^35 k*10^15
40 10^40 k*10^18
Ove k < 10, k > 1, k diverso per ogni P.
È facile provare che gli steps (passi nel caso peggiore del migliore algoritmo possibile) seguono la legge:
Steps k10∫(dim
2 )−2
Questa formula ci dà una stima per confrontare tutti gli algoritmi conosciuti e quelli nuovi per capire quanto
ancora è possibile migliorare. Nel nostro ragionamento ci siamo posti nel “caso peggiore” perché
ovviamente la soluzione potrebbe essere trovata anche in un solo passo o perché p è un quadrata perfetto
o magari perché un suo divisore è un numero relativamente piccolo che qualunque calcolatore può
facilmente trovare in pochi secondi.
Deduciamo che gli steps seguono una legge di tipo esponenziale rispetto alla dimensione del numero da
fattorizzare e questo nel peggiore del migliore algoritmo di fattorizzazione possibile di tipo non quantistico
m algebrico.
Deduciamo che P != NP.
50
Funzioni che generano molti numeri primi e i software che generano
numeri primi al computer
f ( x )=x2+x+q ,q=2,3,5,11,17,41 ;0≤ x≤q−2
f ( x )=x2−x+41 , x=1 ,2 ,3 ,…. ,40
f ( x )=x2−79 x+1601 , x=1,2 ,3 ,…. ,79
f ( x )=2x2+p ; p=2 ,3 ,5 ,11 ,29; 0≤ x≤ p−1
Sono tutte funzioni che generano numeri primi sotto le condizioni restrittive indicate nelle formule. Da
verifiche di tipo statistico fatte al computer risulta che in ogni caso queste funzioni continuano a generare
una gran quantità di numeri primi (non solo numeri primi) anche al di fuori dei limiti descritti, quindi
potrebbero essere usate per tentare di trovare i fattori di numeri primi molto grandi. A queste possiamo
aggiungere i numeri primi di Mersenne e di Fermat ovvero:
f ( x )=22x
+1, f ( x )=2x−1
51
Per realizzare delle tabelle di numeri primi al computer possiamo usare specifici software come il PARI/GP
/** * @param args the command line arguments */ public static void main(String[] args) { BigInteger p = new BigInteger("187"); long i = 2; BigInteger fatt = BigInteger.probablePrime(40 ,new SecureRandom()); while( i < 100 ){
fatt = BigInteger.probablePrime(40 , new SecureRandom());
System.out.println("fattore" + fatt);
System.out.println("resto" + p.mod(fatt)); }
}
}
55
Un algoritmo per fattorizzare numeri RSA
Supponiamo di trovarci nella seguente condizione n = pq dove p, q sono numeri primi anche molto grandi.
φ (n )=φ ( pq )=φ ( p ) φ (q )=( p−1 ) (q−1 )=4a
Dove ho usato il fatto che MCD(p, q)= 1, essendo p, q primi e le note proprietà della funzione phi di Eulero.
Svolgendo i calcoli (q = p/ n) si arriva facilmente a:
p2−p (n+1−4 a )+n=0
Le cui soluzioni sono
p=n+1−4 a±√(n+1−4a)2−4n
2
E operando sull’esistenza del radicale ho le condizioni (si cerca a tra gli interi) :
a> n+1+2√n4
∪a< n+1−2√n4
Quindi una possibile implementazione in C++ è:
caso 1) in C++
#include <cstdlib>#include <iostream>#include <math.h>using namespace std;int main(int argc, char *argv[]){ double p, q, n; long i; i = 0; double a; cout << "inserisci il numero da fattorizzare "; cin >> n; a = floor((n+1-2*sqrt(n))/4); do{ p = ((n+1-4*a)- sqrt(pow(n+1-4*a, 2)-4*n))/2; a = a-1; i = i+1; }while(floor(p) != p);cout << "fattore " << p << "\n";cout << "fattore " << n/p << "\n";;
Questo algoritmo risulta essere particolarmente veloce e affidabile anche per numeri molto grandi
caso 2) in C++
#include <cstdlib>#include <iostream>#include <math.h>using namespace std;int main(int argc, char *argv[]){ double p, q, n; long i; i = 0; double a; cout << "inserisci il numero da fattorizzare "; cin >> n; a = floor((n+1-2*sqrt(n))/4); do{ p = ((n+1-4*a)+sqrt(pow(n+1-4*a, 2)-4*n))/2; a = a-1; i = i+1; }while(floor(p) != p);cout << "fattore " << p << "\n";cout << "fattore " << n/p << "\n";;cout << "passaggi " << i << "\n"; system("PAUSE"); return EXIT_SUCCESS;}
caso 3) in C++
#include <cstdlib>#include <iostream>#include <math.h>using namespace std;int main(int argc, char *argv[]){ double p, q, n; long i; i = 0; double a; cout << "inserisci il numero da fattorizzare "; cin >> n; a = floor((n+1+2*sqrt(n))/4); do{ p = ((n+1-4*a)+ sqrt(pow(n+1-4*a, 2)-4*n))/2; a = a+1;
57
i = i+1; }while(floor(p) != p);cout << "fattore " << p << "\n";cout << "fattore " << n/p << "\n";;cout << "passaggi " << i << "\n"; system("PAUSE"); return EXIT_SUCCESS;}
caso 4) in C++
#include <cstdlib>#include <iostream>#include <math.h>using namespace std;int main(int argc, char *argv[]){ double p, q, n; long i; i = 0; double a; cout << "inserisci il numero da fattorizzare "; cin >> n; a = floor((n+1+2*sqrt(n))/4); do{ p = ((n+1-4*a)- sqrt(pow(n+1-4*a, 2)-4*n))/2; a = a+1; i = i+1; }while(floor(p) != p);cout << "fattore " << p << "\n";cout << "fattore " << n/p << "\n";;cout << "passaggi " << i << "\n"; system("PAUSE"); return EXIT_SUCCESS;}
Per I grandi numero si consiglia l’uso del PARI/GP con I codici che riporto di seguito
Questi algoritmi sono parte integrante di altri sistemi già descritti nell’articolo “teoria dei numeri” e pubblicati in questo sito.
Ora vediamo le cosa da una prospettiva leggermente diversa. Sia
P = xy , x = p-a, y = p-b con a > 0, b > 0
59
p= ( p−a ) ( p−b )=p2−pb−pa+ab
a= p2−p−pbp−b
>0
Allora possiamo scegliere b < p-1 e decrementare b fino a trovare un a intero che soddisfi le condizioni iniziali per poi calcolare x, y ma questo procedimento ci porterebbe al metodo delle divisioni banali che è utile solo quando un fattore è molto più piccolo rispetto all’altro quindi non nel caso di un problema RSA.
Codice C++ ( variante a divisioni banali)
#include <cstdlib>#include <iostream>#include <math.h>using namespace std;int main(int argc, char *argv[]){double a, b, p, i;i =0;cout << "Inserisci il numero da fattorizzare ";cin >> p;b = p-1;do{ a = (pow(p,2)-p-p*b)/(p-b); b--; i++; } while (floor(a) != a);cout << p-a << "\n";cout << p/(p-a) << "\n";cout << "passaggi" << i << "\n";; system("PAUSE"); return EXIT_SUCCESS;}
Facciamo un ulteriore passo avanti cercando di migliorare il modo per trovare le soluzioni intere:
60
Possiamo però ipotizzare che a = b ( a è circa b). Questo ci porta a all’equazione:
b2−2 pb+ p2−p=0
b=p±√ p
Consideriamo per semplicità la radice più piccola di b quella con il segno – e incrementiamo o decrementiamo b di una unità alla volta, allora il codice del nostro programma diventa:
using namespace std;int main(int argc, char *argv[]){double a, b, p, i;i =0;cout << "Inserisci il numero da fattorizzare ";cin >> p;b = floor(p-sqrt(p));do{ a = (pow(p,2)-p-p*b)/(p-b); b++; // va bene anche b--; i++; } while (floor(a) != a);
Possiamo considerare P = xy, con x, y numeri primi diversi da 2 e y = x-a con a numero intero ovviamente pari essendo la differenza di numeri primi quindi di numeri dispari. Facendo le sostituzioni otteniamo che:
x2−xa−p=0
Analogamente se x = y-b ho che
y2− yb−p=0
Ove
x=a±√a2+4 p2
Quindi lì applicazione in C++ che ne deriva sarà:
61
#include <cstdlib>#include <iostream>#include <math.h>using namespace std;int main(int argc, char *argv[]){double a, p, i, x;i =0;cout << "Inserisci il numero da fattorizzare ";cin >> p;a =0;do{ x = (a+sqrt(pow(a,2)+4*p))/2; a = a-2;; i++; } while (floor(x) != x);cout << x << "\n";cout << p/x << "\n";cout << "passaggi" << i << "\n";; system("PAUSE"); return EXIT_SUCCESS;}
Un crivello per generare numeri primi (poco efficiente)
Il metodo è facilmente programmabile con qualunque linguaggio (PARI/GP, C/C++, ecc)
Il sistema genera effettivamente tutti e soli numeri primi ovvero la lista dei primi
Svantaggi del metodo:
Il metodo richiede un enorme capacità di calcolo ( ad ogni passo si calcola il prodotto di n primi
consecutivi) tuttavia grazie ai moderni PC in grado di moltiplicare numeri a centinaia di cifre questo
problema è solo in parte risolto (vedi es PARI/GP, Python, Mathematica).
Applicazione: a primo MCD (a, prodotto dei primi < int(Radq(a))=1
La formula per i numeri primi
Se p = ab e p dispari , con a < b a, b primi allora MCD(p, floor(sqrt(p)!!))= a oppure a p ove il simbolo !! sta ad indicare il semifattoriale cioè il prodotto di tutti i dispari minori della radice quadrata di p (sqrt(p)). In tutti i casi la potenza di calcolo necessaria è davvero impressionante.
Più in generale Se p = ab, allora MCD(p, floor(sqrt(p)!))= a oppure a p ove ! è il fattoriale di p.