• Nenhum resultado encontrado

4.2 Análise dos algoritmos implementados nos hardwares

4.2.1 Computador

Como apresentado na Seção 3.5, os algoritmos foram submetidos a dois tipos de entradas: entradas de números ordenados e entradas de números aleatórios. Os algoritmos são classificados em algoritmos implementados (tradicional, FFT, NTT) e algoritmos de bibliotecas (GMP, Java, Python). A seguir, estão apresentadas duas tabelas (Tabelas 3 e

4) e dois gráficos (Figuras10 e11) dos tempos de execução para cada uma das entradas.

Tabela 3 – Tempo de execução dos algoritmos para entradas de pares ordenados de números menores ou iguais a 𝑛 (ver Seção 3.5), em ms

alg. 𝑛 2 4 8 16 32 64 128 256 512 1024 Tradicional 1 1 1 3 1 8 65 204 958 3839 FFT 1 1 1 1 2 6 21 107 534 2187 NTT 1 1 1 2 3 9 32 178 718 3033 GMP 0 1 1 2 2 3 4 21 83 337 Java 55 51 55 76 102 148 545 664 1082 2187 Python 19 18 23 24 23 39 79 287 987 3829

60 Capítulo 4. Resultados e Discussões

Figura 10 – Gráficos dos tempos de execução dos algoritmos em escala logarítmica para todas as combinações até 𝑛

Tabela 4 – Tempo de execução dos algoritmos para entradas de pares de números aleatórios de 𝑛 dígitos (ver Seção 3.5), em ms

alg. 𝑛 2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 16384 32768 Tradicional 1 1 1 1 1 2 3 7 22 81 312 1178 4692 19908 74207 FFT 2 2 2 2 2 2 3 1 7 14 27 51 108 225 445 NTT 1 1 1 2 1 2 2 4 6 12 25 46 97 191 402 GMP 0 2 1 2 1 1 1 1 2 2 2 3 6 14 34 Java 41 41 55 54 57 65 54 78 87 114 165 328 533 1268 2475 Python 16 19 20 18 22 17 22 19 22 23 21 27 70 201 734

Figura 11 – Gráficos dos tempos de execução dos algoritmos em escala logarítmica o qual

4.2. Análise dos algoritmos implementados nos hardwares 61

Como estudado na Seção 2.3, o algoritmo tradicional possui a pior complexidade assintótica conhecida para um algoritmo de multiplicação (𝑂(𝑛2)). Porém, vale ressaltar que só a complexidade assintótica não é suficiente para comparar eficiência de algoritmos, já que a constante que acompanha 𝑓 (𝑛) em 𝑂(𝑓 (𝑛)) influencia seu comportamento para valores pequenos de 𝑛 (ver Seção 2.2). O algoritmo tradicional para valores pequenos de

𝑛 acaba sendo mais eficiente do que a FFT e a NTT, apesar de terem complexidades

assintóticas melhores. Isso ocorre devido à simplicidade de sua implementação, que cons- titui apenas de dois loops realizando simples operações de somas e e multiplicações. Em contraste ao tradicional, a FFT e a NTT possuem etapas adicionais que impactam em suas performances, como preenchimento de zeros nos vetores de entrada, operações de resto de divisão e operações com pontos flutuantes.

Para entradas de números ordenados, os algoritmos possuíram performances simi- lares, já que a quantidade de dígitos das entradas foram pequenas (máximo de 4 dígitos). Para entradas de números aleatórios grandes, a FFT a NTT apresentaram tempos similares, com a NTT ligeiramente mais rápida que a FFT. Além disso, a FFT e a NTT possuíram tempos de execução melhores em relação ao algoritmo tradicional. Analisando o gráfico da Figura10, nota-se que o desempenho da FFT e da NTT supera o do algoritmo tradicional para números de aproximadamente 150 dígitos.

Já os algoritmos com bibliotecas os algoritmos com bibliotecas, no geral o mais rápido foi o do GMP, seguido pelo o do Python e por fim o do Java. É interessante observar que os algoritmos da NTT e FFT implementados em C++ foram mais rápidos que o Java e passaram do Python no final do gráfico da Figura 10.

4.2.2

Arduino Uno

Foi possível alocar no máximo 2048 bytes de memória contígua no Arduino Uno. Logo, foram testados números com a seguinte quantidade de dígitos utilizando os resul- tados da Tabela 2 (considerando 𝑚 = 𝑛):

∙ Tradicional: 5𝑛 + 5𝑚 − 4 = 2048 ⇒ 𝑛 ≈ 200 dígitos

∙ NTT: 2⌈log2(𝑛+𝑚)⌉+4= 2048 ⇒ 𝑛 ≈ 128 dígitos

∙ FFT: 2⌈log2(𝑛+𝑚)⌉+3 = 2048 ⇒ 𝑛 ≈ 64 dígitos

O gráfico da Figura 12 apresenta as diferentes performances dos 3 algoritmos implementados no Arduino Uno.

62 Capítulo 4. Resultados e Discussões

Figura 12 – Gráficos dos tempos de execução dos algoritmos implementados no Arduino em escala logarítmica

Do gráfico da Figura12, observa-se que o tamanho das entradas não foram grandes o suficiente para os algoritmos da FFT e NTT serem mais rápidas que o tradicional. Além disso, o algoritmo da NTT foi mais rápido que o da FFT, já que o ATmega328P não possui coprocessador aritmético capaz de realizar operações com pontos flutuantes.

4.2.3

NodeMCU

Foi possível alocar no máximo 32768 bytes de memória contígua no NodeMCU. Logo, foram testados números com a seguinte quantidade de dígitos utilizando os resul- tados da Tabela2 (considerando 𝑚 = 𝑛):

∙ Tradicional: 5𝑛 + 5𝑚 − 4 = 32768 ⇒ 𝑛 ≈ 3270 dígitos

∙ NTT: 2⌈log2(𝑛+𝑚)⌉+4= 32768 ⇒ 𝑛 ≈ 2048 dígitos

∙ FFT: 2⌈log2(𝑛+𝑚)⌉+3 = 32768 ⇒ 𝑛 ≈ 1024 dígitos

O gráfico da Figura 13 apresenta as diferentes performances dos 3 algoritmos implementados no NodeMCU.

4.2. Análise dos algoritmos implementados nos hardwares 63

Figura 13 – Gráficos dos tempos de execução dos algoritmos implementados no NodeMCU em escala logarítmica

Os resultados encontrados no NodeMCU foram semelhantes dos encontrados no Arduino. Do gráfico da Figura 13, nota-se que, apesar do algoritmo tradicional ainda ser mais rápido que a FFT e NTT, há a tendência do tempo de execução do algoritmo tradicional ultrapassar os tempos da FFT e NTT.

4.2.4

Raspberry Pi 3

O gráfico da Figura 14 apresenta as diferentes performances dos 3 algoritmos implementados no Raspberry Pi 3.

Figura 14 – Gráficos dos tempos de execução dos algoritmos implementados no Raspberry Pi em escala logarítmica

64 Capítulo 4. Resultados e Discussões

Como o Raspberry Pi 3 possui 1 GB de memória RAM, foi possível utilizar os tamanhos apresentados na Seção3.5.1. Nota-se pelo gráfico da Figura14que o algoritmo tradicional ultrapassou os algoritmos da FFT e NTT com números de cerca de 200 dígitos. Além disso, o algoritmo da FFT foi mais rápida do que a NTT, já que o Raspberry Pi coprocessador aritmético capaz de realizar operações com pontos flutuantes.

65

5 Considerações Finais

Neste trabalho, foram apresentados algoritmos para multiplicação de números com- plexidade assintótica melhor do que o método tradicional apresentado na escola, assim como a análise de suas eficiências em software. Foi visto que, na prática, o algoritmo Tradicional é eficiente para números de até 150 dígitos e a FFT e a NTT para números de mais de 150 dígitos. Além disso, ficou claro que uma simples operação matemática básica como a multiplicação pode possuir uma implementação sofisticada, envolvendo números complexos e aritmética modular.

O algoritmo de Karatsuba, FFT e NTT são conhecidos e implementados princi- palmente em software, mas raramente em hardware devido à natureza recursiva (para o algoritmo de Karatsuba) e de uso de números muito grandes (para a FFT e NTT) (KALACH; DAVID; TITTLEY, 2007). Caso não haja um hardware especializado para uma aplicação, a solução é implementar o algoritmo em software. Estes métodos eficazes de multiplicação podem se tornar ferramentas que otimizam outros algoritmos, como o RSA.

O estudo realizado no Capítulo 2 e os resultados apresentados no Capítulo 4

mostram que se deve analisar os seguintes tópicos na escolha de um algoritmo de multi- plicação para uma aplicação específica: quantidade de dígitos dos números de entrada, memória disponível no hardware local, precisão e existência da aritmética de ponto- flutuante implementada em hardware.

Trabalhos Futuros

Algoritmos para multiplicação de números inteiros é uma área recente, com novas ideias sendo exploradas atualmente. Apesar de diversos estudos com grandes progressos realizados até os dias atuais, não se conhece ainda qual o algoritmo com a menor com- plexidade assintótica para multiplicar números inteiros (ver Seção 2.6.4). Como sugestão de trabalhos futuros, pode-se apontar:

∙ Implementação e análise do algoritmo de Karatsuba;

∙ Otimizar ainda mais os algoritmos (como aproveitar melhor a memória utilizada para guardar os números);

∙ Estudo da implementação dos algoritmos utilizado-se programação paralela. Em placas de vídeo, por exemplo, é possível acelerar a velocidade do algoritmo da NTT e FFT;

66 Capítulo 5. Considerações Finais

∙ Estudo da implementação dos algoritmos em hardware, como na plataforma FPGA;

∙ Analisar os ganhos de performance do algoritmo RSA utilizando diferentes algorit- mos de multiplicação;

∙ Implementação dos algoritmos em Assembly, permitindo adicionar a instrução de multiplicação em hardwares sem multiplicadores;

∙ Criar uma implementação híbrida como foi feita na biblioteca GMP, a qual combina vários algoritmos de multiplicação e decide o uso de cada um de acordo com suas características;

67

Referências

AGARWAL, R. C.; BURRUS, C. S. Number theoretic transforms to implement fast digital convolution. Proceedings of the IEEE, v. 63, n. 4, p. 550–560, April 1975. ISSN 0018-9219. Citado na página 30.

ATMEL. ATMEL 8-BIT MICROCONTROLLER WITH 4/8/16/32KBYTES

IN-SYSTEM PROGRAMMABLE FLASH DATASHEET. 2015. Rev.: Atmel-8271J-

AVR-ATmega48A/48PA/88A/88PA/168A/168PA/328/328P-Datasheet_11/2015. Citado na página 55.

BOYER, C. A History of Mathematics. Wiley, 1991. ISBN 9780471543978. Disponível em: <https://books.google.com.br/books?id=OtsY845tywQC>. Citado na página 21. BRENT, R.; ZIMMERMANN, P. Modern Computer Arithmetic. New York, NY, USA: Cambridge University Press, 2010. ISBN 0521194695, 9780521194693. Citado 4 vezes nas páginas 15, 26, 27e 43.

BURTON, D. Elementary number theory. Allyn & Bacon, Incorporated, 1976. ISBN 9780205048144. Disponível em: <https://books.google.com.br/books?id= PPvuAAAAMAAJ>. Citado na página 42.

CHABERT, J.; BARBIN, E. A History of Algorithms: From the Pebble to the

Microchip. Springer Berlin Heidelberg, 1999. (chabert: A History of Algorithms). ISBN

9783540633693. Disponível em: <https://books.google.com.br/books?id=B1c0s3ffN_ 0C>. Citado na página 21.

COCHRAN, W. T. et al. What is the fast fourier transform? Proceedings of the IEEE, v. 55, n. 10, p. 1664–1674, Oct 1967. ISSN 0018-9219. Citado 3 vezes nas páginas 30, 32

e 33.

COHEN, H. A Course in Computational Algebraic Number Theory. New York, NY, USA: Springer-Verlag New York, Inc., 1993. ISBN 0-387-55640-0. Citado 2 vezes nas páginas 41e 42.

CORMEN, T. H. et al. Introduction to Algorithms. 2nd. ed. Cambridge, MA, USA: McGraw-Hill Higher Education, 2001. ISBN 0070131511. Citado 6 vezes nas páginas 15,

28, 30, 32,34 e35.

ESPRESSIF. ESP8266EX Datasheet. 2018. Version 5.8. Citado na página 55. FURER, M. Faster integer multiplication. In: Proceedings of the Thirty-ninth

Annual ACM Symposium on Theory of Computing. New York, NY, USA: ACM,

2007. (STOC ’07), p. 57–66. ISBN 978-1-59593-631-8. Disponível em: <http: //doi.acm.org/10.1145/1250790.1250800>. Citado 2 vezes nas páginas 22e 48.

GOURDON, P. S. X. Fft based multiplication of large numbers. 2001. Disponível em:

<xavier.gourdon.free.fr/Constants/Algorithms/fft.ps>. Citado na página 40. GUIMARAES, C. d. S. Números complexos e polinômios. : Vestseller, 2008. v. 1. (Matemática em Nível IME ITA, v. 1). Citado na página 30.

68 Referências

HALFACREE, G.; UPTON, E. Raspberry Pi User Guide. 1st. ed. : Wiley Publishing, 2012. ISBN 111846446X, 9781118464465. Citado na página56.

HASTAD, J. Notes for the course advanced algorithms. 2000. Disponível em:

<https://www.nada.kth.se/~johanh/algnotes.pdf>. Citado na página 48.

IRELAND, K.; ROSEN, M. A classical introduction to modern number theory. Springer- Verlag, 1982. (Graduate texts in mathematics). ISBN 9783540906254. Disponível em:

<https://books.google.com.br/books?id=WvjuAAAAMAAJ>. Citado na página 42. KALACH, K.; DAVID, J. P.; TITTLEY, N. Hardware complexity of modular

multiplication and exponentiation. IEEE Transactions on Computers, IEEE Computer Society, Los Alamitos, CA, USA, v. 56, p. 1308–1319, 2007. ISSN 0018-9340. Citado 2 vezes nas páginas26 e65.

KARATSUBA, A. The complexity of computations. v. 211, p. 169–, 01 1995. Citado na página22.

KLEINBERG, J.; TARDOS, E. Algorithm Design. Boston, MA, USA: Addison-Wesley Longman Publishing Co., Inc., 2005. ISBN 0321295358. Citado 3 vezes nas páginas 13,

23e 25.

KNUTH, D. E. The TeXbook. : Addison-Wesley Professional, 1986. ISBN 0201134470. Citado na página51.

KNUTH, D. E. The Art of Computer Programming, Volume 2 (3rd Ed.): Seminumerical

Algorithms. Boston, MA, USA: Addison-Wesley Longman Publishing Co., Inc., 1997.

ISBN 0-201-89684-2. Citado 4 vezes nas páginas22,27, 28e 35.

Lüders, C. Fast Multiplication of Large Integers: Implementation and Analysis of the DKSS Algorithm. ArXiv e-prints, mar. 2015. Citado 2 vezes nas páginas 19e 25. NUSSBAUMER, H. Fast Fourier Transform and Convolution Algorithms. Springer Berlin Heidelberg, 1981. (Springer Series in Information Sciences). ISBN 9783662005514. Disponível em: <https://books.google.fr/books?id=tnjpCAAAQBAJ>. Citado na página41.

O’CONNOR, J. J.; ROBERTSON, E. F. Memory, mental arithmetic and mathematics. 1997. <http://www-history.mcs.st-and.ac.uk/HistTopics/Mental_arithmetic.html>. Accessed: 24-11-2017. Citado 2 vezes nas páginas21 e50.

RAFFERTY, C.; ONEILL, M.; HANLEY, N. Evaluation of large integer multiplication methods on hardware. IEEE Transactions on Computers, IEEE Computer Society, Los Alamitos, CA, USA, v. 66, n. 8, p. 1369–1382, 2017. ISSN 0018-9340. Citado na página

19.

ROUGHGARDEN, T. Algorithms Illuminated - Part 1. : Soundlikeyourself Publishing, 2017. (Algorithms Illuminated). ISBN 0999282905. Citado na página 23.

SEDGEWICK, R. Algorithms in C++ - Parts 1-4: Fundamentals, Data Structures,

Sorting, Searching. : Addison-Wesley-Longman, 1998. ISBN 978-0-201-35088-3. Citado

Referências 69

SHAHRAM; AZMAN; KUMBAKONAM. Efficient big integer multiplication and squaring algorithms for cryptographic applications. Penang 11800, Malaysia, p. 10, 07 2014. Citado 2 vezes nas páginas 22e 27.

SMITH, J. O. Introduction to Digital Filters with Audio Applications.

http://www.w3k.org/books/: W3K Publishing, 2007. ISBN 978-0-9745607-1-7. Citado 3 vezes nas páginas 32, 43e 48.

STALLINGS, W. Cryptography and Network Security: Principles and Practice. 6th. ed. Upper Saddle River, NJ, USA: Prentice Hall Press, 2013. ISBN 0133354695, 9780133354690. Citado na página 22.

STROUSTRUP, B. The C++ Programming Language. 4th. ed. : Addison-Wesley Professional, 2013. ISBN 0321563840, 9780321563842. Citado na página 51.

TOLIMIERI, R.; AN, M.; LU, C. Algorithms for Discrete Fourier Transform and

Convolution. 2nd. ed. : Springer-Verlag New York, 1997. ISBN 978-1-4757-2767-8.

Citado 3 vezes nas páginas 28, 29e 32.

TOMMILA, M. Number theoretic transforms. 1997.<http://www.apfloat.org/ntt.html>. Accessed: 26-11-2017. Citado 2 vezes nas páginas 48e 50.

ZIVIANI, N. Projeto de Algoritmos: com implementações em Java e C++ de Fabiano

73

APÊNDICE A – Algoritmo da Convolução

Tradicional

1 v e c t o r <int> C o n v o l u c a o T r a d i c i o n a l ( v e c t o r <int> a , v e c t o r <int> b ) 2 { 3 int A = a . s i z e ( ) , B = b . s i z e ( ) ; 4 v e c t o r <int> v (A + B − 1 , 0 ) ; 5 for(int i = 0 ; i < A ; i ++) 6 for(int j = 0 ; j < B ; j ++) 7 v [ i + j ] += a [ i ] * b [ j ] ; 8 return v ; 9 }

75

APÊNDICE B – Algoritmo da FFT

Recursivo

1 v e c t o r <complex<float>> FFT( v e c t o r <complex<float>> a ) 2 {

3 int n = a . s i z e ( ) ; 4 if( n == 1 )

5 return a ;

6

7 v e c t o r <complex<float>> a_par ( n >> 1 ) , a_impar ( n >> 1 ) ; 8 for(int i = 0 ; i < ( n >> 1 ) ; i ++) 9 { 10 a_par [ i ] = a [ i << 1 ] ; 11 a_impar [ i ] = a [ ( i << 1 ) + 1 ] ; 12 } 13

14 v e c t o r <complex<float>> A_par = FFT( a_par ) ; 15 v e c t o r <complex<float>> A_impar = FFT( a_impar ) ; 16 v e c t o r <complex<float>> A( n ) ;

17

18 complex<float> w ( 1 . 0 , 0 ) ;

19 complex<float> omega ( (float) c o s ( 2 *M_PI/n ) , (float) s i n ( 2 *M_PI/n ) ) ; 20 n >>= 1 ;

21

22 for(int k = 0 ; k < n ; k++)

23 {

24 complex<float> u = A_par [ k ] ;

25 complex<float> v = w * A_impar [ k ] ; 26 A[ k ] = u + v ; 27 A[ k + n ] = u − v ; 28 w = w * omega ; 29 } 30 31 return A; 32 }

77

APÊNDICE C – Algoritmo da FFT Iterativo

1 void FFT( complex<float> a [ ] , int s i z e _ a ) 2 { 3 for(int j = 1 , i = 0 ; j <= s i z e _ a −1; j ++) 4 { 5 for(int k = s i z e _ a >> 1 ; k > ( i ^= k ) ; k >>= 1 ) ; 6 if( j < i ) 7 swap ( a [ i ] , a [ j ] ) ; 8 } 9 for(int l e n = 1 ; l e n < s i z e _ a ; l e n <<= 1 ) 10 { 11 const int u n i t y S t e p = l e n <<1;

12 const float t h e t a = −2 * M_PI / u n i t y S t e p ;

13 const complex<float> u n i t y R o o t ( c o s ( t h e t a ) , s i n ( t h e t a ) ) ; 14 for(int i = 0 ; i < s i z e _ a ; i += l e n << 1 )

15 {

16 complex<float> omega = 1 ; 17 for(int j = 0 ; j < l e n ; j ++)

18 {

19 complex<float> u = a [ i + j ] ;

20 complex<float> v = a [ i + j + l e n ] * omega ; 21 omega *= u n i t y R o o t ; 22 23 a [ i + j ] = u + v ; 24 a [ i + j + l e n ] = u − v ; 25 } 26 } 27 } 28 }

79

APÊNDICE D – Algoritmo da NTT

Recursivo

1 const int md = 9 9 8 2 4 4 3 5 3 ; // 119*2^23 + 1

2 const int gen = 3 ; // primitive root of md

3 // exponenciacao rapida

4 int mod_pow(int a , int b ) 5 { 6 int r = 1 ; 7 while( b ) 8 { 9 if( b & 1 ) 10 r = (long long) r * a % md ; 11 a = (long long) a * a % md ; 12 b >>= 1 ; 13 } 14 return r ; 15 } 16 17 v e c t o r <int> NTT( v e c t o r <int> a ) 18 { 19 int n = a . s i z e ( ) ; 20 if( n == 1 ) 21 return a ; 22

23 v e c t o r <int> a_par ( n / 2 ) , a_impar ( n / 2 ) ; 24 for(int i = 0 ; i < n /2 ; i ++) 25 { 26 a_par [ i ] = a [ 2 * i ] ; 27 a_impar [ i ] = a [ 2 * i + 1 ] ; 28 } 29

30 v e c t o r <int> y_par = NTT( a_par ) ; 31 v e c t o r <int> y_impar = NTT( a_impar ) ; 32 v e c t o r <int> y ( n ) ;

33

34 int c = (md − 1 ) / n ; // P = c * n + 1 -> c = (P - 1) / n

35 int omega = mod_pow( gen , c ) ; // omega = r ^ c mod P

36 int w = 1 ; 37 n >>= 1 ;

38 for(int k = 0 ; k < n ; k++)

39 {

80 APÊNDICE D. Algoritmo da NTT Recursivo

41 int v = (long long) w * y_impar [ k ] % md ; 42 y [ k ] = ( u + v ) % md ;

43 y [ k + n ] = ( u − v + md) % md ; 44 w = (long long) w * omega % md ;

45 }

46 return y ; 47 }

81

APÊNDICE E – Algoritmo da NTT Iterativo

1 const int md = 9 9 8 2 4 4 3 5 3 ; // 119*2^23 + 1

2 const int gen = 3 ; // primitive root of md

3

4 // exponenciacao rapida

5 int mod_pow(int a , int b ) 6 { 7 int r = 1 ; 8 while( b ) 9 { 10 if( b & 1 ) 11 r = (long long) r * a % md ; 12 a = (long long) a * a % md ; 13 b >>= 1 ; 14 } 15 return r ; 16 } 17

18 void n t t (int a [ ] , int s i z e _ a ) 19 { 20 for(int j = 1 , i = 0 ; j <= s i z e _ a −1; j ++) 21 { 22 for(int k = s i z e _ a >> 1 ; k > ( i ^= k ) ; k >>= 1 ) ; 23 if( j < i ) 24 swap ( a [ i ] , a [ j ] ) ; 25 } 26 for(int l e n = 1 ; l e n < s i z e _ a ; l e n <<= 1 ) 27 {

28 int r o o t = mod_pow( gen , (md − 1 ) / ( l e n << 1 ) ) ; 29 for(int i = 0 ; i < s i z e _ a ; i += l e n << 1 ) 30 { 31 int w = 1 ; 32 for(int j = 0 ; j < l e n ; j ++) 33 { 34 int u = a [ i + j ] ;

35 int v = (long long) a [ i + j + l e n ] * w % md ; 36 a [ k ] = ( u + v ) % md ;

37 a [ k + n ] = ( u − v + md) % md ; 38 w = (long long) w * r o o t % md ;

39 }

82 APÊNDICE E. Algoritmo da NTT Iterativo

41 }

83

APÊNDICE F – Algoritmo de multiplicação

com FFT

1 void m u l t i p l y ( vc &a , vc &b ) 2 {

3 int s i z e _ a = a . s i z e ( ) , s i z e _ b = b . s i z e ( ) ; 4

5 // complete arrays with 0 to get the size of the next power of 2

6 int s i z e = 1 , l a r g e s t = s i z e _ a + s i z e _ b − 1 ; 7 while( s i z e < l a r g e s t )

8 s i z e <<= 1 ;

9 for(int i = s i z e _ a ; i < s i z e ; i ++) 10 a . push_back ( complex<float>(0 , 0 ) ) ; 11 for(int i = s i z e _ b ; i < s i z e ; i ++) 12 b . push_back ( complex<float>(0 , 0 ) ) ; 13 14 // convolution with fft 15 a = f f t ( a ) ; 16 b = f f t ( b ) ; 17 for(int i = 0 ; i < s i z e ; i ++) 18 a [ i ] = a [ i ] * b [ i ] ; 19 20 // inverse fft 21 for(int i = 1 ; i < s i z e >> 1 ; i ++) 22 swap ( a [ i ] , a [ s i z e −i ] ) ; 23 a = f f t ( a ) ; 24 for (int i = 0 ; i < s i z e ; i ++) 25 a [ i ] = round ( a [ i ] . r e a l ( ) / (float) s i z e ) ; 26 }

85

APÊNDICE G – Algoritmo de multiplicação

com NTT

1 void m u l t i p l y ( v e c t o r <int> &a , v e c t o r <int> &b ) 2 {

3 int s i z e _ a = a . s i z e ( ) , s i z e _ b = b . s i z e ( ) ; 4

5 // complete arrays with 0 to get the size of the next power of 2

6 int s i z e = 1 , l a r g e s t = s i z e _ a + s i z e _ b − 1 ; 7 while( s i z e < l a r g e s t ) 8 s i z e <<= 1 ; 9 for(int i = s i z e _ a ; i < s i z e ; i ++) 10 a . push_back ( 0 ) ; 11 for(int i = s i z e _ b ; i < s i z e ; i ++) 12 b . push_back ( 0 ) ; 13 14 // convolution with ntt 15 a = n t t ( a ) ; 16 b = n t t ( b ) ; 17 for(int i = 0 ; i < s i z e ; i ++) 18 a [ i ] = (long long) a [ i ] * b [ i ] % md ; 19 20 // inverse ntt 21 for(int i = 1 ; i < s i z e >> 1 ; i ++) 22 swap ( a [ i ] , a [ s i z e −i ] ) ; 23 a = n t t ( a ) ; 24 int i n v = mod_pow( s i z e , md − 2 ) ; 25 for (int i = 0 ; i < s i z e ; i ++) 26 a [ i ] = (long long) a [ i ] * i n v % md ; 27 }

Documentos relacionados