Skip to content

Instantly share code, notes, and snippets.

@starcalibre
Last active February 10, 2016 05:39
Show Gist options
  • Save starcalibre/aaa1471c7805ce98d1f1 to your computer and use it in GitHub Desktop.
Save starcalibre/aaa1471c7805ce98d1f1 to your computer and use it in GitHub Desktop.
Monte Carlo Estimation Of Pi
.idea/
*.iml

We can estimate the value of Pi by means of a Monte Carlo method. From high school math, we know that the area of a Circle is 2 * PI * r^2, and the area of a square with the same radius 4 * PI * r^2. Taking the ratio of these these two quantities gives us PI / 4. In other words, PI = 4 * (Area of a Cricle) / Area of a Square).

On the left hand side a circle and square. By placing points at random in this area, all the points will fall into the square but not all of the points will fall into the circle. Given enough points, this ratio can be used to estimate the value of PI using the above formulas.

Hit the start button to begin estimating the value of PI. The lineplot to the right tracks the error from the real value of PI. The more points thrown randomly into the square, the better approximation we get over time.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<style>
p {
font: 14px sans-serif;
}
#left {
float: left;
width: 50%;
text-align: right;
}
#right {
float: right;
width: 50%;
text-align: left;
}
#info {
margin-left: 50px;
}
#canvas-element {
border: solid 1px black;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.axis text {
font: 12px sans-serif;
}
.line {
fill: none;
stroke: #218380;
stroke-width: 1px;
}
</style>
</head>
<body>
<div id="container">
<div id="left">
<canvas id="canvas-element" class="plot"></canvas>
</div>
<div id="right">
<svg id="svg-element"></svg>
<div id="info">
<p>Total Iterations: <span id="total-iteration"></span></p>
<p>Total Number of points in circle: <span id="in-circle"></span></p>
<p>Current estimate value of Pi: <span id="pi-estimate"></span></p>
<p><button id="start-button">Start</button></p>
</div>
</div>
</div>
</body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.12/d3.min.js" charset="utf-8"></script>
<script>
var circleWidth = 400;
var circleHeight = 400;
var circleRadius = Math.min(circleHeight, circleWidth) / 2;
var canvasArea = circleWidth * circleHeight;
var margin = {top: 10, right: 20, bottom: 40, left: 60};
var lineWidth = 400 - margin.left - margin.right;
var lineHeight = 250 - margin.top - margin.bottom;
var totalInCircle = 0;
var totalPoints = 2500;
var radius = 4;
var drawIntervalTime = 1;
var totalPointsDrawn = 0;
var pointsInCircle = 0;
var estimatedPi = 0;
var currentError = 0;
var lineData = [];
// create the canvas for drawing the circle
var canvas = d3.select("#canvas-element")
.attr("width", circleWidth)
.attr("height", circleHeight);
var context = canvas.node().getContext("2d");
context.strokeStyle = 'black';
context.lineWidth = 3;
context.beginPath();
context.arc(circleWidth/2, circleHeight/2, circleRadius, 0, 2 * Math.PI);
context.closePath();
context.stroke();
// create the line plot
var svg = d3.select("#svg-element")
.attr("width", lineWidth + margin.left + margin.right)
.attr("height", lineHeight + margin.top + margin.bottom)
.append("g")
.attr("transform", translateString(margin.left, margin.top));
var xScale = d3.scale.linear()
.domain([0, totalPoints])
.range([0, lineWidth]);
var yScale = d3.scale.linear()
.domain([-0.4, 0.4])
.range([lineHeight, 0])
.clamp(true);
var xAxis = d3.svg.axis()
.scale(xScale)
.ticks(7)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left");
// append line along x = 0
svg.append("g")
.attr("class", "axis")
.append("line")
.attr("x1", 0)
.attr("y1", yScale(0))
.attr("x2", lineWidth)
.attr("y2", yScale(0));
// append axis labels
svg.append("g")
.attr("class", "axis")
.append("text")
.attr("text-anchor", "middle")
.attr("x", (lineWidth) / 2)
.attr("y", (lineHeight + margin.top + margin.bottom) - 10)
.text("Number of Interations");
svg.append("g")
.attr("class", "axis")
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", -margin.left + 25)
.attr("x", -lineHeight / 2)
.style("text-anchor", "middle")
.text("Error");
var lineFunction = d3.svg.line()
.x(function(d) { return d[0] })
.y(function(d) { return d[1] });
// create currently empty line
var lineSvg = svg.append("path")
.attr("class", "line")
.attr("d", lineFunction(lineData));
svg.append("g")
.attr("class", "axis")
.attr("transform", translateString(0, lineHeight))
.call(xAxis);
svg.append("g")
.attr("class", "axis")
.call(yAxis);
d3.select("#start-button").on("click", start);
// start the show!
function start() {
var count = 0;
var drawInterval = setInterval(function() {
if(count > totalPoints) {
clearInterval(drawInterval);
}
update(count);
count++;
}, 10);
// prevent from starting twice
d3.select("#start-button").on("click", null);
}
function update(count) {
// generate new random x/y pair
var x = randomFloat(0, circleWidth);
var y = randomFloat(0, circleHeight);
// check if the point is in the circle
if(euclideanDistance(x, circleWidth / 2, y, circleHeight / 2) < circleRadius) {
context.fillStyle = '#73D2DE';
totalInCircle++;
}
else {
context.fillStyle = '#D81159';
}
// draw thw point
drawPoint(x, y, radius);
totalPointsDrawn++;
estimatedPi = (totalInCircle / totalPointsDrawn) * 4;
currentError = Math.PI - estimatedPi;
// update the lineplot
// only do this every 10th iteration to reduce noise in the lineplot
if(count % 10 === 0) {
lineData.push([xScale(count), yScale(currentError)]);
lineSvg.attr("d", lineFunction(lineData));
}
updateText();
}
function updateText() {
document.getElementById('total-iteration').innerHTML = totalPointsDrawn.toString();
document.getElementById('in-circle').innerHTML = totalInCircle.toString();
document.getElementById('pi-estimate').innerHTML = estimatedPi.toString();
}
function drawPoint(x, y, r) {
context.beginPath();
context.arc(x, y, r, 0, 2 * Math.PI);
context.closePath();
context.fill();
}
function translateString(x, y) {
var out = [];
out.push("translate(");
out.push(x);
out.push(" ");
out.push(y);
out.push(")");
return out.join("");
}
function rotateString(r) {
var out = [];
out.push("rotate(");
out.push(r);
out.push(")");
return out.join("");
}
function euclideanDistance(x1, x2, y1, y2) {
return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
}
function randomFloat(min, max) {
return (max - min) * Math.random() + min;
}
</script>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment