Say you are a software engineer, or developer, or whatever other abstracted word for programmer that has some meaning to you. Say you have also been given some scientific computation problem, like estimating the value of with the Monte Carlo method.
Inscribe a circle of radius in a square of length . Then the probability a random point generated in the square is also inside the circle is equal to the ratio of the circle's area to the square's, which is .
We now have a method for approximating by generating a set of random points in the square and finding the ratio of those points also inside the circle. This value multiplied by 4 gives us an estimate of , which becomes more precise with an increasing volume of generated points.
Your friend is an electrical engineer, and knows how to program in JavaScript. She quickly codes a simple, easily-understandable procedure:
js
constNUM_POINTS = 1e6;constCIRCLE_RADIUS = 0.5;functioninCircle (x ,y ) {letpointRadius =Math .sqrt (Math .pow (x -CIRCLE_RADIUS , 2) +Math .pow (y -CIRCLE_RADIUS , 2));returnpointRadius <CIRCLE_RADIUS ;}letpoints = newArray (NUM_POINTS );for (leti = 0;i <NUM_POINTS ; ++i ) {letx =Math .random ();lety =Math .random ();points [i ] = [x ,y ];}letcirclePoints = 0;for (leti = 0;i <NUM_POINTS ; ++i ) {let [x ,y ] =points [i ];if (inCircle (x ,y )) {++circlePoints ;}}console .log (`Samples: ${NUM_POINTS }`);console .log (`Estimate of pi: ${(circlePoints /NUM_POINTS ) * 4}`);
Youperhaps vicariously through another, having already taken on some abstracted title for yourself, chose instead to over-engineer her perfectly good solution to be TheBestCode™
1: Everything Functional
Since the only operations done here are on an array, all the loops can be eliminated in favor of immutable function calls on the original array. Even the instantiation of random points in the array can be sugared into the initializer.
js
constpi_4 =Array .from ({length :NUM_POINTS },() => [Math .random (),Math .random ()]).filter (pt =>inCircle (...pt )).length /NUM_POINTS ;console .log (`Samples: ${NUM_POINTS }`);console .log (`Estimate of pi: ${pi_4 * 4}`);
2: On inCircle
~Idiomatic~ JavaScript calls for arrow notation on small functions, so that's what we'll do. We can also allow for a variable circle radius - of course, that's totally useless for this example, but makes the function ~reusable~. Finally, there is a marginal cost reduction in comparing the square of the euclidean distance to the square of the radius rather than computing a square root.
This gives the crafted program:
js
constinCircle = (x ,y ,CIRCLE_RADIUS = 0.5) =>Math .pow (x -CIRCLE_RADIUS , 2) +Math .pow (y -CIRCLE_RADIUS , 2) <Math .pow (CIRCLE_RADIUS , 2);
We're not done yet. If we take a quarter circle of radius 1 inscribed in the square instead, the ratio of quarter-circle-to-square area remains , but now the center of the circle is at the origin, which eliminates two subtractions in the function call. The code is further reduced with JavaScript's exponentiation operator.
For now, this will be our final form:
js
constinCircle = (x ,y ,CIRCLE_RADIUS = 1) =>x ** 2 +y ** 2 <CIRCLE_RADIUS ** 2;
3: Defining and Objectifying
But what is a point? In this case, a pair of coordinates. Let's define the points as named tuples:
js
constinCircle = (point ,CIRCLE_RADIUS = 1) =>point .x ** 2 +point .y ** 2 <CIRCLE_RADIUS ** 2;constpi_4 =Array .from ({length :NUM_POINTS },() => ({x :Math .random (),y :Math .random () })).filter (pt =>inCircle (pt )).length /NUM_POINTS ;
We abstracted inCircle
to be of any radius earlier, but of course that's kind of useless since the function is specifically designed for measuring . What we should do is wrap our estimation script into one experimentation procedure that accepts a variable point sample size. This is the ultimate abstraction we could ask for.
After a multitude of revisions and totally misaligned priorities, we arrive at paradise.
js
constNUM_POINTS = 1e6;constpiEstimate =numPoints => {constinCircle =point =>point .x ** 2 +point .y ** 2 < 1;return ((Array .from ({length :numPoints },() => ({x :Math .random (),y :Math .random () })).filter (inCircle ).length /numPoints ) * 4);};console .log (`Samples: ${NUM_POINTS }`);console .log (`Estimate of pi: ${piEstimate (NUM_POINTS )}`);