Uma coisa que está na minha lista faz algum tempo é fazer testes de velocidade com as várias plaquinhas que eu tenho. Mais pela brincadeira, pois estes testes normalmente não representam direito o desempenho em aplicações reais. E aí me surgiu a ideia de fazer a comparação calculando os dígitos do Pi.
Chovendo no molhado, o Pi é a razão entre a circunferência de um círculo e o seu diâmetro. É um número "irracional", que não pode ser colocado na forma de fração e na forma decimal tem um número infinito de dígitos. Calcular os dígitos do Pi tem ocupado pessoas desde muito tempo atrás (a wikipedia tem uma cronologia).
A partir de 1400 sabe-se que o Pi pode ser calculado através da soma de séries infinitas. Com o advento do computador foram desenvolvidos algorítimos que facilitam e aceleram o cálculo. Na minha busca eu rapidamente achei o seguinte programa (de autoria de Dik T Winter):
int a=10000,b,c=2800,d,e,f[2801],g;main(){for(;b-c;)f[b++]=a/5; for(;d=0,g=c*2;c-=14,printf("%.4d",e+d/a),e=d%a)for(b=c;d+=f[b]*a, f[b]=d%--g,d/=g--,--b;d*=b);}
Você talvez encontre uma versão com pequenas diferenças, esta acima calcula os primeiros 800 dígitos do Pi. Este programa está, propositalmente, compactado e ofuscado. Para os meus testes eu fiz uma versão um pouco mais legível e adaptada para compilação na IDE do Arduino:
/* * Calculo dos dígito de Pi * Usando o algoritimo Spigot de Rabinowitz e Wagon * Adaptação da versão compacta e obsfucada escrita por Dik T. Winter: * * int a=10000,b,c=2800,d,e,f[2801],g;main(){for(;b-c;)f[b++]=a/5; * for(;d=0,g=c*2;c-=14,printf("%.4d",e+d/a),e=d%a)for(b=c;d+=f[b]*a, * f[b]=d%--g,d/=g--,--b;d*=b);} * * Comentários nas declarações copiados de * https://stackoverflow.com/questions/4084571/implementing-the-spigot-algorithm-for-%CF%80-pi * * Daniel Quadros junho/2021 */ #define NDIGITS 10000 //max digits to compute #define LEN (NDIGITS/4+1)*14 //nec. array length int32_t a = 10000; //new base, 4 decimal digits int32_t b = 0; //nominator prev. base int32_t c = LEN; //index int32_t d = 0; //accumulator and carry int32_t e = 0; //save previous 4 digits int32_t f[LEN+1]; //array of 4-digit-decimals int32_t g = 0; //denom previous base void setup() { delay(1000); Serial.begin(115200); } // Não usado void loop() { while (Serial.read() != '\n') { delay(100); } Serial.println("PI = "); long inicio = millis(); calcula(); long tempo = millis() - inicio; Serial.println(); Serial.print ("Tempo = "); Serial.print (tempo); Serial.println ("ms"); } // Cálculo dos dígitos do Pi void calcula() { char dig[5] = "0000"; // para fazer o print int n = 0; // para mudar de linha a cada 100 dígitos c = LEN; for(b = 0; b < c; b++) { f[b] = a/5; } e = 0; for (; c > 0; c -= 14) { d = 0; g = c*2; b = c; for (;;) { d += f[b]*a; f[b] = d % --g; d /= g--; if (--b == 0) { break; } d *= b; } uint16_t val = e+d/a; dig[0] = (val / 1000) + '0'; dig[1] = ((val / 100) % 10) + '0'; dig[2] = ((val / 10) % 10) + '0'; dig[3] = (val % 10) + '0'; Serial.print (dig); n += 4; if (n == 100) { Serial.println(); n = 0; } e = d % a; } }
Mas o que está sendo feito, e porque? Não me arrisco a dizer que entendi completamente, mas aqui vão algumas informações e referências. O algoritmo usado pertence a uma classe chamada "spigot" (torneira): os dígitos vão sendo gerados na ordem e não são usados diretamente no cálculo dos dígitos seguintes. Este algorítimo específico foi criado em 1995 por Stan Wagon and Stanley Rabinowitz.
Algumas características interessantes deste algorítimo:
- Requer somente soma, subtração, multiplicação e divisão de inteiros
- Os dígitos são gerados de 4 em 4
- A quantidade de dígitos a calcular precisa ser definida a priori
- Requer um vetor com 14 inteiros para cada conjunto de 4 dígitos
Eu rodei o programa (acertando o número de dígitos conforme a memória disponível) em algumas placas da minha coleção: Arduino Uno, Raspberry Pi Pico, ESP32, Black Pill (STM32) e Seeduino XIAO. O resultado esta aqui, no meu canal do YouTube.
Nenhum comentário:
Postar um comentário