A metaprogramação por template é uma técnica de metaprogramação usada pelo compilador para gerar um código fonte temporário que é acoplado ao resto do código fonte para ser compilado depois. A saída desse templates podem ser constante em tempo de compilação, estrutura de dados ou funções completas. O uso de templates pode ser pensando como uma execução em tempo de compilação.
No blog do ricbit, ele sugere que é possível utilização da metaprogramação através de templates para transferir partes das tarefas que são realizadas em tempo de execução para tempo de compilação e, assim, diminuir o tempo de execução no spoj. Eu resolvi testar essa idéia em um problema simples do spoj:
Primeiramente, vamos considerar a versão mais lenta do algoritmo:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
unsigned int factorial(unsigned int n)
{
if (n == 0)
return 1;
return n * factorial(n - 1);
}
int main(){
unsigned int n;
scanf("%u",&n);
printf("%d\n",factorial(n));
return 0;
}
O tempo de execução no spoj é 0.03 (o tempo de execução do spoj nem sempre é muito confiável :( ).
A versão mais rápida seria pré-calcular previamente todos os valores do fatorial:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
int n;
unsigned int fat[13];
int main(){
fat[0] = 1;
fat[1] = 1;
fat[2] = 2;
fat[3] = 6;
fat[4] = 24;
fat[5] = 120;
fat[6] = 720;
fat[7] = 5040;
fat[8] = 40320;
fat[9] = 362880;
fat[10] = 3628800;
fat[11] = 39916800;
fat[12] = 479001600;
scanf("%d",&n);
printf("%u\n",fat[n]);
return 0;
}
O menor tempo de execução do spoj foi 0.01s.Podemos definir um programa usando metaprogramação com o mesmo tempo de execução da seguinte maneira:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
template <int N>
struct Factorial
{
enum { value = N * Factorial<N - 1>::value };
};
template <>
struct Factorial<0>
{
enum { value = 1 };
};
int n;
unsigned int fat[13];
int main(){
fat[0] = Factorial<0>::value;
fat[1] = Factorial<1>::value;
fat[2] = Factorial<2>::value;
fat[3] = Factorial<3>::value;
fat[4] = Factorial<4>::value;
fat[5] = Factorial<5>::value;
fat[6] = Factorial<6>::value;
fat[7] = Factorial<7>::value;
fat[8] = Factorial<8>::value;
fat[9] = Factorial<9>::value;
fat[10] = Factorial<10>::value;
fat[11] = Factorial<11>::value;
fat[12] = Factorial<12>::value;
scanf("%d",&n);
printf("%u\n",fat[n]);
return 0;
}
Os valores de cada fat[i] é substituído por um valor constante calculado em tempo de compilação.
Uma versão mais enxuta do código acima, seria essa aqui:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
template <int N>
struct Factorial
{
enum { value = N * Factorial<N - 1>::value };
};
template <>
struct Factorial<0>
{
enum { value = 1 };
};
int n;
unsigned int fat[13]={
#define f(n) Factorial<n>::value
f(0),f(1),f(2),
f(3),f(4),f(5),
f(6),f(7),f(8),
f(9),f(10),f(11),
f(12)
};
int main(){
scanf("%d",&n);
printf("%u\n",fat[n]);
return 0;
}
Nenhum comentário:
Postar um comentário