:::: MENU ::::

Calcular PI con lanzamientos

¿Nunca has escuchado que se puede calcular PI de forma aproximada con granos de arroz?

Pues muchas de esas formas de calcular PI de forma aproximada se pueden realizar programáticamente, ya que al fin y al cabo se basan en el azar y en la colocación en un plano de elementos.

Para esto, nosotros vamos a utilizar «agujas» (o cualquier elemento que tenga dicha forma, ya sean palillos u otro que queramos imaginar).

¿Por qué agujas? La fórmula que utilizamos es derivada de La aguja de Buffon, acerca de la cual os dejo un artículo interesante:
http://www.estadisticaparatodos.es/taller/buffon/buffon.html

¡Vamos a ello!
Se nos dice que, si tenemos un plano dividido en filas del mismo alto que el tamaño de unas simples agujas, siendo esas divisiones marcadas con lápiz, y lanzamos una cantidad N de agujas sobre ese plano, con la cantidad de colisiones entre las agujas y las líneas marcadas N’ y una simple fórmula obtendremos aproximadamente PI.

A todo esto y aplicando la lógica, no hace falta decir que a mayor número de intentos, mayor «precisión» obtendremos, siempre sabiendo que es imposible obtener el número exacto, y más sabiendo que tiene infinitos decimales.

La fórmula que nos proporcina La aguja de Buffon es la siguiente:

 π = 2N/N'
Siendo
N los lanzamientos totales
N' la cantidad de colisionens

Por tanto, teniendo dicha fórmula, nos ponemos manos a la obra y obtenemos dicho código (PHP):

<?php
// Config
$times = 1000000; // Lanzamientos de aguja
$size = 30; // Tamaño de aguja
$lines = 40; // Cantidad de líneas
$separation = $size; // Separación entre líneas (tiene que ser igual al tamaño)

$gridsize = $lines * $separation;

$count = 0;

for($i = 0; $i < $times; $i++)
{
	// Generamos aguja
	$position = rand(0, $gridsize);
	$angle = rand(0, 90);
	
	// Detectamos las líneas de arriba y abajo (nos ahorramos generar un tablero y recorrerlo en busca de colisiones)
	$upper = floor($position / $separation) * $separation;
	$bottom = ceil($position / $separation) * $separation;
	
	// Borde arriba y abajo de la aguja
	$up = $position - sin(deg2rad($angle)) * $size/2;
	$down = $position + sin(deg2rad($angle)) * $size/2;
	
	if($up <= $upper || $down >= $bottom)	// Si tocan o pasan por encima...
		$count++;							// Sumamos 1 al contador
	
	// Para debuggear:
	// echo 'Pos: ', $position, ' | a: ', $angle, ' | Upper: ', $upper, ' | Bottom: ', $bottom, ' | Up: ', $up, ' | Down: ', $down, "\n";
}

echo 'Colisiones: ', $count, '/', $times, "\n";

// Calculemos PI
echo 'PI = ', 2*$times/$count; // Aplicamos la fórmula

while(true){} // Mantengamos la consola abierta para ver el Output

Como siempre, aquí os dejo el enlace a paste.bin:
https://pastebin.com/KtFpbYVa

Y con simplemente con este trocito de código, habremos aproximado PI de forma casera. Cuanto más aumentemos $times, más cerca de 3,14 estará el resultado.

Os recomiendo también ir probando con otros tamaños de agujas o cantidad de líneas, puede ser que varíe el resultado, pese a que la teoría indique que no.

Aquí podemos ver 2 resultados de prueba:
633840/1000000 colisiones // PI = 3.1553704404897
632918/1000000 colisiones // PI = 3.1599670099444
No son lo más preciso del mundo, pero nos puede valer :)

Y con esto, ¡me despido y os deseo una feliz semana!