Skip to content

Instantly share code, notes, and snippets.

@yuigoto
Last active June 10, 2023 17:21
Show Gist options
  • Save yuigoto/55285ce84a7b3640757de5d6d752db1a to your computer and use it in GitHub Desktop.
Save yuigoto/55285ce84a7b3640757de5d6d752db1a to your computer and use it in GitHub Desktop.
Estimando o valor de PI usando Monte Carlo, em diferentes linguagens.

Estimando o valor de PI com Monte Carlo

Implementando MonteCarlo em diferentes linguagens, para aprendizado e testes.

package;
// Importando packages
import openfl.display.Sprite;
import openfl.Lib;
import openfl.events.Event;
import openfl.geom.Point;
import openfl.text.TextField;
import openfl.text.TextFieldAutoSize;
import openfl.text.TextFormat;
/**
* ESTIMANDO PI COM MONTECARLO : Haxe + OpenFl
* ============================================================
*
* Baseado no vídeo e no sketch P5.js feitos pelo Guilherme Rey
* (Yarquen) em https://www.youtube.com/watch?v=VQvw3u-zSZg.
*
* Assista! É importante para entender bem o que acontece aqui!
*
* Esse é o meu take usando a linguagem de programação Haxe,
* junto da biblioteca OpenFL.
*
* Do compilador do Haxe, é possível exportar este projeto para:
* - HTML5 + JavaScript;
* - Flash;
* - C++ (Windows nativo);
* - Neko (Neko Virtual Machine);
* - Android;
* - Tizen;
* - (...);
* - Torradeira (desde que tenha um sistema operacional
* compatível ;P);
*
* @author Fabio Y. Goto <lab@yuiti.com.br>
*/
class Main extends Sprite
{
/**
* Raio da circunferência.
*/
public var radius:Int = 500;
/**
* Quantidade de pontos dentro da circunferência.
*/
public var n_in:Int = 0;
/**
* Número máximo de pontos permitidos.
*/
public var n_max:Int;
/**
* Array para armazenar os pontos a serem desenhados.
*/
public var points:Array<Point>;
/**
* Campo de texto, para exibir o valor de PI.
*/
public var text:TextField;
/**
* Construtor.
*/
public function new()
{
// Super construtor
super();
// Se stage não estiver definido...
if (stage == null) {
// ...executa `init` como event listener
stage.addEventListener(Event.ADDED_TO_STAGE, init);
} else {
// ...caso contrário, vai direto mesmo e dane-se
init(null);
}
}
/**
* Event listener inicial, faz o bootstraping de variáveis.
*
* @param e Event
* Event handler
*/
public function init(e:Event):Void
{
// Remove event listener inicial
stage.removeEventListener(Event.ADDED_TO_STAGE, init);
// Definindo quantidade de partículas
#if (flash)
// Em flash, suporta bem 10000 pontos
n_max = 10000;
#elseif (cpp)
// Em C++, suporte bem até 20000 pontos
n_max = 20000;
#else
// Nas outras plataformas, meh, 5000 pontos
n_max = 5000;
#end
// Inicializa o array de pontos
points = new Array();
// Cria novo campo de texto e posiciona
text = new TextField();
text.alpha = 0.75;
text.background = true;
text.backgroundColor = 0x000000;
text.defaultTextFormat = new TextFormat(
"_sans",
20,
0xffffff,
false,
false,
false
);
text.x = 10;
text.y = 10;
// Define auto resizing para o campo
text.autoSize = TextFieldAutoSize.LEFT;
// Adiciona campo de texto ao stage
stage.addChild(text);
// Define event listener de frames
stage.addEventListener(Event.ENTER_FRAME, update);
}
/**
* É executado a cada frame, atualiza o status da aplicação.
*
* @param e Event
* Event handler
*/
public function update(e:Event):Void
{
if (points.length < n_max) {
this.graphics.clear();
drawCircle();
if (points.length < n_max) {
for (n in 0...100) {
// Adiciona ponto
addRandomPoint();
}
}
for (i in 0...points.length) {
if (isPointIn(points[i])) {
this.graphics.beginFill(0xffff00, 1);
} else {
this.graphics.beginFill(0xff0000, 1);
}
this.graphics.drawCircle(points[i].x, points[i].y, 2);
}
this.graphics.endFill();
text.text = "PI: " + Std.string((4 * n_in) / points.length);
}
}
/**
* Desenha o círculo base da aplicação.
*/
public function drawCircle():Void
{
// Desenha o círculo
this.graphics.beginFill(0x009900, 1);
this.graphics.drawCircle(0, 0, radius);
this.graphics.endFill();
}
/**
* Adiciona um ponto aleatório.
*/
public function addRandomPoint():Void
{
var _x:Float = Math.random() * radius;
var _y:Float = Math.random() * radius;
var pt:Point = new Point(_x, _y);
n_in += (isPointIn(pt)) ? 1 : 0;
points.push(pt);
}
/**
* Verifica se o ponto está dentro, ou não, da circunferência, retornando
* um boolean.
*
* @param pt Point
* Ponto a ser verificado
* @return Bool
*/
public function isPointIn(pt:Point):Bool
{
var square_x:Float = Math.pow(pt.x, 2);
var square_y:Float = Math.pow(pt.y, 2);
return (Math.sqrt(square_x + square_y) <= radius);
}
}
--[[
estimando pi com monte carlo
versao lua (pico8)
baseado no video e no sketch
de p5.js feitos pelo guilherme
rey em:
https://youtu.be/vqvw3u-zszg
assista! e importante para
entender o funcionamento.
esse e o meu take miniatura
feito em lua para o fantasy
console pico8.
super compacto, mas funciona!
author: fabio y. goto
mail: lab@yuiti.com.br
]]--
--[[
funcao da api do pico8.
inicializa os dados do
cartucho.
]]--
function _init()
-- points inside
n_in = 0;
-- maximum points
n_mx = 5000;
-- radius
rads = 128;
-- point array
pnts = {};
add_pt();
end
--[[
funcao da api do pico8.
atualiza a cada frame.
]]--
function _draw()
-- limpa a tela
cls(0);
-- desenha circulo base
circfill(0, 0, rads, 6);
-- cria os pontos
if count(pnts) < n_mx then
add_pt();
end
-- desenha os pontos
for i=1, count(pnts) do
if is_in(pnts[i]) then
circfill(
pnts[i].x,
pnts[i].y,
2,
7
);
else
circfill(
pnts[i].x,
pnts[i].y,
2,
10
);
end
end
-- desenha o texto
color(0);
pi_val = (4*n_in)/count(pnts);
print("pi: "..pi_val, 2, 2);
end
--[[
adiciona um ponto aleatorio
ao array 'pnts'
]]--
function add_pt()
pt = {};
pt["x"] = rnd(rads);
pt["y"] = rnd(rads);
--[[
se estiver na
circunferencia
incrementa n_in
]]--
if is_in(pt) then
n_in += 1;
end
-- adiciona ao table
add(pnts,pt);
end
--[[
verifica se um ponto esta
dentro da circunferencia
ou nao.
]]--
function is_in(point)
_x = point.x ^ 2;
_y = point.y ^ 2;
_s = sqrt(_x + _y);
return _s <= rads;
end
/**
* ESTIMANDO PI COM MONTECARLO
* ============================================================
*
* Baseado no vídeo e no sketch P5.js feitos pelo Guilherme Rey
* (Yarquen) em https://www.youtube.com/watch?v=VQvw3u-zSZg.
*
* Assista! É importante para entender bem o que acontece aqui!
*
* Apesar do P5.js ser uma versão do processing, pelo fato de
* ser em JavaScript, algumas coisas funcionam um pouco diferente.
*
* Este é o meu take de como estimar o valor de PI, de acordo com
* o vídeo. Tem algumas peculiaridades do Processing que tentei
* explicar, mas podem estar meio mal escritas, portanto: RTFM! :P
*
* @author Fabio Y. Goto <lab@yuiti.com.br>
*/
// GLOBAIS
// ============================================================
/**
* Raio da circunferência.
*/
int r = 500;
/**
* Total de pontos dentro da circunferência.
*/
int n_in = 0;
/**
* Máximo de pontos a serem criados.
*/
int n_max = 50000;
/**
* Framerate do sketch, diminua isso e a quantidade de pontos se
* tiver problemas de performance.
*/
int fps = 60;
/**
* Array para armazenar pontos.
*/
Point[] points;
// CLASSES
// ============================================================
/**
* Um objeto de ponto.
*
* Possui apenas três atributos: x, y e is_in, que indica se
* o ponto está dentro, ou não, da circunferência.
*/
class Point {
float x;
float y;
boolean is_in;
/**
* Construtor da classe.
*
* @var {float} x
* @var {float} y
*/
Point(float x, float y) {
this.x = x;
this.y = y;
}
}
// FUNÇÕES AUXILIARES
// ============================================================
/**
* Adiciona um ponto ao array.
*/
void addPoint() {
Point coord = getRandomPoint();
boolean is_in = isPointIn(coord);
n_in += (is_in) ? 1 : 0;
coord.is_in = is_in;
// Para adicionar itens ao array no Procesing, você precisa
// usar append e, ainda, indicar o tipo de dado.
points = (Point[]) append(points, coord);
}
/**
* Desenha a elipse utilizada no vídeo.
*/
void drawEllipse() {
fill(#009900);
noStroke();
ellipse(0, 0, r * 2, r * 2);
}
/**
* Retorna um novo ponto, com uma posição (pseudo-)aleatória.
*
* @return {Point}
*/
Point getRandomPoint() {
float _x = random(0, r);
float _y = random(0, r);
return new Point(_x, _y);
}
/**
* Verifica se um ponto está dentro, ou fora, da circunferência.
*
* @var {Point} pt
* @return {boolean}
*/
boolean isPointIn(Point pt) {
float square_x = pow(pt.x, 2);
float square_y = pow(pt.y, 2);
// Hipotenusa é menor ou igual ao raio?
return (sqrt(square_x + square_y) <= r);
}
// EXECUÇÃO
// ============================================================
/**
* Setup do Processing.
*/
void setup() {
/**
* Definimos o tamanho do canvas aqui, usando o P2D como
* renderer, embora seu uso seja opcional, visto que é
* o renderer padrão.
*/
size(500, 500, P2D);
// Define framerate
frameRate(fps);
// Cria o array (importante fazer isso aqui!)
// O "0" é pra criar um array vazio, PRECISA ser declarado!
points = new Point[0];
}
/**
* Update + Desenha.
*/
void draw() {
// Define fundo + desenha elipse
background(0);
drawEllipse();
// Vamos adicionar pontos ao array, enquanto possível
if (points.length < n_max) {
for (int a = 0; a < 100; a++) {
addPoint();
}
}
// Adicionou o ponto? Ótimo, vamos desenhar e tirar estimativa!
strokeWeight(3);
// Vamos apenas desenhar e testar se length > 0
for (int i = 0; i < points.length; i++) {
if (points[i].is_in) {
// Tá dentro?
stroke(255, 255, 0);
} else {
// Ou tá fora?
stroke(255, 0, 0);
}
point(points[i].x, points[i].y);
}
/**
* Note que, antes de dividir o valor pelo comprimento do array,
* multiplicamos o comprimento por "1.0".
*
* O motivo? É simples, se você simplesmente dividir (4 * n_in)
* pelo valor do comprimento do array, ele "arredonda" o resultado
* final para baixo (fica 3.0), visto que points.length é um int.
*
* Coisa do Processing. :P
*/
float pi_value = (4 * n_in) / (points.length * 1.0);
println(pi_value);
// Desenhando o texto na tela com COMIC SANS, pois TODOS GOSTAM
PFont font = createFont("Comic Sans MS", 40, true);
textFont(font, 40);
fill(0);
int text_x = 30;
int text_y = 60;
// Desenhando uma borda para o texto, já que strokeWidth não funciona.
// Deve haver meios melhores que este. :P
for (int yy = -2; yy <= 2; yy++) {
for (int xx = -2; xx <= 2; xx++) {
text("PI: " + pi_value, text_x + xx, text_y + yy);
}
}
fill(0, 255, 255);
text("PI: " + pi_value, text_x, text_y);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment