mirror of https://github.com/tLDP/LDP
144 lines
4.5 KiB
Bash
144 lines
4.5 KiB
Bash
#!/bin/bash
|
|
# cannon.sh: Approximating PI by firing cannonballs.
|
|
|
|
# Author: Mendel Cooper
|
|
# License: Public Domain
|
|
# Version 2.2, reldate 13oct08.
|
|
|
|
# This is a very simple instance of a "Monte Carlo" simulation:
|
|
#+ a mathematical model of a real-life event,
|
|
#+ using pseudorandom numbers to emulate random chance.
|
|
|
|
# Consider a perfectly square plot of land, 10000 units on a side.
|
|
# This land has a perfectly circular lake in its center,
|
|
#+ with a diameter of 10000 units.
|
|
# The plot is actually mostly water, except for land in the four corners.
|
|
# (Think of it as a square with an inscribed circle.)
|
|
#
|
|
# We will fire iron cannonballs from an old-style cannon
|
|
#+ at the square.
|
|
# All the shots impact somewhere on the square,
|
|
#+ either in the lake or on the dry corners.
|
|
# Since the lake takes up most of the area,
|
|
#+ most of the shots will SPLASH! into the water.
|
|
# Just a few shots will THUD! into solid ground
|
|
#+ in the four corners of the square.
|
|
#
|
|
# If we take enough random, unaimed shots at the square,
|
|
#+ Then the ratio of SPLASHES to total shots will approximate
|
|
#+ the value of PI/4.
|
|
#
|
|
# The simplified explanation is that the cannon is actually
|
|
#+ shooting only at the upper right-hand quadrant of the square,
|
|
#+ i.e., Quadrant I of the Cartesian coordinate plane.
|
|
#
|
|
#
|
|
# Theoretically, the more shots taken, the better the fit.
|
|
# However, a shell script, as opposed to a compiled language
|
|
#+ with floating-point math built in, requires some compromises.
|
|
# This decreases the accuracy of the simulation.
|
|
|
|
|
|
DIMENSION=10000 # Length of each side of the plot.
|
|
# Also sets ceiling for random integers generated.
|
|
|
|
MAXSHOTS=1000 # Fire this many shots.
|
|
# 10000 or more would be better, but would take too long.
|
|
PMULTIPLIER=4.0 # Scaling factor.
|
|
|
|
declare -r M_PI=3.141592654
|
|
# Actual 9-place value of PI, for comparison purposes.
|
|
|
|
get_random ()
|
|
{
|
|
SEED=$(head -n 1 /dev/urandom | od -N 1 | awk '{ print $2 }')
|
|
RANDOM=$SEED # From "seeding-random.sh"
|
|
#+ example script.
|
|
let "rnum = $RANDOM % $DIMENSION" # Range less than 10000.
|
|
echo $rnum
|
|
}
|
|
|
|
distance= # Declare global variable.
|
|
hypotenuse () # Calculate hypotenuse of a right triangle.
|
|
{ # From "alt-bc.sh" example.
|
|
distance=$(bc -l << EOF
|
|
scale = 0
|
|
sqrt ( $1 * $1 + $2 * $2 )
|
|
EOF
|
|
)
|
|
# Setting "scale" to zero rounds down result to integer value,
|
|
#+ a necessary compromise in this script.
|
|
# It decreases the accuracy of this simulation.
|
|
}
|
|
|
|
|
|
# ==========================================================
|
|
# main() {
|
|
# "Main" code block, mimicking a C-language main() function.
|
|
|
|
# Initialize variables.
|
|
shots=0
|
|
splashes=0
|
|
thuds=0
|
|
Pi=0
|
|
error=0
|
|
|
|
while [ "$shots" -lt "$MAXSHOTS" ] # Main loop.
|
|
do
|
|
|
|
xCoord=$(get_random) # Get random X and Y coords.
|
|
yCoord=$(get_random)
|
|
hypotenuse $xCoord $yCoord # Hypotenuse of
|
|
#+ right-triangle = distance.
|
|
((shots++))
|
|
|
|
printf "#%4d " $shots
|
|
printf "Xc = %4d " $xCoord
|
|
printf "Yc = %4d " $yCoord
|
|
printf "Distance = %5d " $distance # Distance from
|
|
#+ center of lake
|
|
#+ -- the "origin" --
|
|
#+ coordinate (0,0).
|
|
|
|
if [ "$distance" -le "$DIMENSION" ]
|
|
then
|
|
echo -n "SPLASH! "
|
|
((splashes++))
|
|
else
|
|
echo -n "THUD! "
|
|
((thuds++))
|
|
fi
|
|
|
|
Pi=$(echo "scale=9; $PMULTIPLIER*$splashes/$shots" | bc)
|
|
# Multiply ratio by 4.0.
|
|
echo -n "PI ~ $Pi"
|
|
echo
|
|
|
|
done
|
|
|
|
echo
|
|
echo "After $shots shots, PI looks like approximately $Pi"
|
|
# Tends to run a bit high,
|
|
#+ possibly due to round-off error and imperfect randomness of $RANDOM.
|
|
# But still usually within plus-or-minus 5% . . .
|
|
#+ a pretty fair rough approximation.
|
|
error=$(echo "scale=9; $Pi - $M_PI" | bc)
|
|
pct_error=$(echo "scale=2; 100.0 * $error / $M_PI" | bc)
|
|
echo -n "Deviation from mathematical value of PI = $error"
|
|
echo " ($pct_error% error)"
|
|
echo
|
|
|
|
# End of "main" code block.
|
|
# }
|
|
# ==========================================================
|
|
|
|
exit 0
|
|
|
|
# One might well wonder whether a shell script is appropriate for
|
|
#+ an application as complex and computation-intensive as a simulation.
|
|
#
|
|
# There are at least two justifications.
|
|
# 1) As a proof of concept: to show it can be done.
|
|
# 2) To prototype and test the algorithms before rewriting
|
|
#+ it in a compiled high-level language.
|