Pel setembre d’aquest any passat vaig estar per Barcelona a la PHPConference’09, i allà hi havia un estant de l’editorial O’Relly. D’aquestes coses que un es possa a veure titols de llibres (la veritat tenen títols molts interessants) en vaig trobar un sobre concurrència que la veritat tenia molt bona pinta, de la serie Theory/In/Practice anomenat The Art of Concurrency.
Això que el mateix vespre en vaig possar a llegir-lo, i hem vaig trobar un exemple com calcular el valor de la constant emprant integració numèrica. Per a capitols posteriors mostrar de com descomposar el problema i paral·lelitzar-lo. El càlcul de la constant
ho fa integrant numèricamet entre
la funció
, dins el meu infim coneixement sobre anàlisi i mètodes numèrics vaig mirar analìticament de fer la integral definida:
Com sabem l’ordinador no resol a nivell analític si no que tot és numèric, funcions com la del i d’altres es fan mitjançant eines numèriques com les series de MacLaurin per fer aproximacions. En el nostre cas, seria obtenir el valor aproximat de
emprant integració nunèrica, més concretament la regla del rectàngle que és una de les regles de quadratura numèrica que s’ensenyen durant la carrera. En aquell moment no m’hi vaig fixar, però, per què l’autor va triar aquest mètode com exemple? Tots sabem que el mètode en integració numèrica que convergeix més rapidament és el de Simpson i el que en poques iteracions obtenim el resultat esperat amb el mínim error. Si miram el codi d’exemple que possa ell, emprava unes 100000 iteracions per obtenir un valor molt aprop de
-
float calcPiRect(long num_rects)
-
{
-
int i;
-
double mid, height, width, area, sum=0.0;
-
-
width=1.0/(double) num_rects;
-
for (i=0;i<num_rects ;i++)
-
{
-
mid=(i+0.5)*width;
-
height=4.0/(1.0+mid*mid);
-
sum+=height;
-
}
-
return(sum*width);
-
}
Fa poc dies vaig tornar a agafar el llibre i vaig fer la versió però emprant Simpson, i ja que hi era amb Python. Vaig fer una petita variació, en aquesta versió. Empro una variable pels valors dels nodes en lloc d’emprar un array, això sacrificaria una mica de memòria. La veritat vaig notar la diferència només fent 14 iteracions s’aproximava molt al valor real amb un error molt baix (
) en front a l’error donat per l’algorisme de rectàngle amb les mateixes iteracions (
)
-
def func(x):
-
return(4.0/(1+x*x))
-
-
def simpson(a, b, n):
-
if n< =0:
-
raise Exception, "n ha d'esser mes gros que 0"
-
-
if a>=b:
-
raise Exception, "a ha d'esser mes petit que b"
-
-
h = (float(b)-float(a))/n
-
ini = h/6
-
sum = 0.0
-
-
xn = a
-
for i in range(1, n+1):
-
xn += h
-
sum += (func(xn-h)+4*func((xn+(xn-h))/2)+func(xn))
-
-
return(ini*sum)
Com es pot observar a la taula i a la gràfica (en escala logarítmica) següents el mètode del rectangle és molt lent a convergir cap a , en canvi el de Simpson amb 14 iteracions ja tenim un valor amb 10 decimals estable amb un error més que acceptable. Per això moltes vegades i en aquests tipus d’algorismes no només s’ha de veure el cost computacional si no que també la velocitat de convergència, ja que això ens reduirà el temps de càlcul del problema en el que estam treballant.

Ho he estat provant i he trobat una cosa curiosa. Per molt que puig el nombre d’itreracions (milers o milions), la precisió no puja. De fet, va oscil.lant.
Deu ser per la precisió de les operación en punt flotant?
Te pos l’algoritme (escrit un poc diferent) en Ruby:
def simpson(a, b, n)
h = (b.to_f – a.to_f) / n
sum = (1 … n * 2).inject(0) { |t, x| t += ((x % 2) + 1) * yield((a + h * x)/2) }
h / 6 * (yield(a) + 2 * sum + yield(b))
end
pi = simpson(0, 1, 16) { |x| 4.0 / (1 + x * x) }
Si és cert, es que inclús es pot calcular el limit en el qual permet saber quina serà la precisió màxima de l’algorisme.
Crec que he confirmat les sospites. Usant aritmetica “sencera” (emprant el tipus rational de ruby), a mesura que augment el nombre d’iteracions la precisió augmenta. Cosa que amb punt flotant no passa.
Amb aquests mètodes es pot saber també la fita d’errors, per exemple en Simpson tendriem que la fita de l’error és $latex \frac{(b-a)^5}{2880}K_5$ on $latex K_5$ és la derivada 5 de la f nostre funció que calculam.