Tuesday, June 21, 2022

Mathmatical Recreations

Throughout history, man has had a fascination with Pi. Per common knowledge, it is an irrational number and has no exact decimal representation. Ancient civilizations didn’t even know what its approximate value was. Babylonians had it calculated to 3.125, while the Egyptians thought it was 3.16. These values I got from Petr Beckmann’s book “A History of Pi”.


For this article I am most interested in the Babylonian method. Their idea was to fill a circle with triangles with each triangle focus point being the center of the circle and then just calculate the sum of all of the area of the triangles. As many of you will know the area of a triangle is calculated as


.5 * (Height * Width)


This is a picture of Babylonian method of calculating Pi:

 


For the record, I took this image from Petr Beckmann’s book “A History of Pi”


The problem with this method (which is also known as the hand method), is all of the area outside of the triangles but inside the circle. The curvature is hard to calculate. The solution is to make a lot more, but very skinny triangles. As the triangles get skinnier, the amount of space lost due to the straight lines of the triangle gets smaller and if you go small enough, the amount of space lost becomes irrelevant.


We can use this method with a computer to try to calculate the value of Pi. But first we need a tool to create a virtual circle. That tool is known as the Unit Circle, the Unit circle is created as a series of points on the XY coordinate system. So, if one calculates point x = cos(angle) and y = sin(Angle) and work your way from 0 – 360 degrees, you will get the outline of a circle with a radius of one.


You can have fun with the Unit Circle. I have used it to approximate the distance of the planets. It’s not a great approximation, but it is ok. Planetary orbits are ellipses and are not perfect circles while the Unit Circle makes a perfect circle, so there is an error in doing such.


With this information we can make a virtual triangle inside our Unit Circle. And then calculate the area of said triangle. Here is the plan, we calculate the XY coordinate for some angle, lets start off with zero, and then we calculate the XY coordinates for an angle greater than the previous one, lets say one degree. That becomes our triangle. We know the length of leg A and hypotenuse, they are both one. But Leg B will have to be calculated.


We can calculate the distance between our two points, by turning it also into a virtual triangle (Oh NO! Not another triangle!) and using The Pythagorean theorem where A2 + B2 = C2. In my program, I turned this into a function. Or basically the Square(X1-X2) + Square(Y1-Y2) and then take the Square Root of the sum.


I am a fan of Pascal, the modern implementation of Pascal is Free Pascal. It produces relatively fast and efficient code. It is a true compiler and not an interpreter, more on this later. Free Pascal uses a dialect of Pascal called Object Pascal (And so does Delphi) There is a Free Pascal Compiler available for most computers in use today, including Linux, Mac and Windows.

Let's take a look at my end results:










In the approximate Error column, the one for the one degree angle size. The 3.99E-5 means

3.99 * 10-5, this is how computers will print scientific notation. For the record this was ran on a i7-8700 desktop. So, as the results get better, the amount of computer time used goes up.

Here is my source code:

Program CalculatePi;
//***********************************************************************
// Program uses the Unit Circle to calculate the area of a circle
// by dividing it up into small triangles (The Hand Method) and 
// calculating the area of each triangle.  Then it sums each triangle's  
// area up into a Total sum and that should equal approximate value of Pi.
// Each point on the Unit Circle is calculated as 
//      x := Cos(Angle)
// 	  y := sin(Angle)
// **********************************************************************

Uses SysUtils, Math;
Type
	MyFloat = Double;
	MyInt   = LongInt;

Var
	i, Cnt:						MyInt;	
	Area, A1, A2, y1, y2, x1, x2, d:		MyFloat;
	DegToCalc, MyDeg:					MyFloat;
	
Function Distance(x1, x2, y1, y2: MyFloat): MyFloat;
//***********************************************************************
// The distance between (x1,y1) and (x2, y2) is calculated 
// using Pythagorean theorem. Pythagorean theorem states that
// Square of the length of Leg A plus the square of Leg B equal 
// the square of length of the hypotenuse. So we can turn these 
// points into a triangle.  Leg A is x1 to x2, Leg b is Y1 to Y2. 
// and then the formula becomes: 
//
//	Hypotenuse = ((x1-x2)^2 +(y1-y2)^2)^0.5
// 
//***********************************************************************
Var
	x, y:		MyFloat;
Begin
	x := x1 - x2;
	y := y1 - y2;
	x := Sqr(x);
	y := Sqr(y);
	x := Sqrt(x + y);
	Distance := x;
End;

Begin
	If ParamCount <> 2 Then
	Begin
		WriteLn('Wrong Number of Parms ... Aborting');
		WriteLn('There needs to be two parameters!');
		WriteLn('     1st parm is how many degrees to calculate per leg.');
		WriteLn('     2nd Parm is how many loops to process');
		Halt(4);
	End;
	DegToCalc := StrToFloat(ParamStr(1));
	Cnt := StrToInt(ParamStr(2));
	Area := 0.0;
	MyDeg := 0.0;
	For i:= 0 To Cnt Do 
	Begin
		A1 := MyDeg;
		A2 := MyDeg + DegToCalc;
		y1 := sin(DegToRad(A1));
		y2 := sin(DegToRad(A2));
		x1 := cos(DegToRad(A1));
		x2 := cos(DegToRad(A2));
		d := Distance(x1, x2, y1, y2);
		Area := Area + (0.5 * 1.0 * d);
		MyDeg := MyDeg + DegToCalc;
	End;
	WriteLn('Pi: ', Area:6:15);
End.

If you examine my program and note the trigonometric functions sin and cos all make a call to DegToRad. DegToRad is a standard Free Pascal Function to convert Degrees to Radians. My Scientific Calculator allows me to enter trigonometric functions using degrees. But every programming language I have ever used has required the inputted angle be in Radians. I have a friend who has a degree in mathematics and he claims to prefer Radians. I and almost anyone else I know prefer to work in degrees. In fact I can’t really wrap my head around Radians. And actually our math coprocessors built into all of our computers have the functions hard coded to use Radians. I have read that mathematicians at the beginning of computer age required these functions use radians.


Some may argue about my choice of programming language is odd. Almost everyone today prefers to work with one of the cool languages. Pascal hasn’t been cool since the 1980s. I went back and converted and than ran these in both c and Python, The outputted values were the same but execcution time was considerbly different.

 











It may help to show a graph of the worst case:

 


 


As you can see, c clearly out performed Pascal and Pascal clearly out performed Python. In this case the difference in coding between Pascal and c was trivial, the semantics were a little different; But that was all.


Why are c and Pascal so much faster than Python? Well Python is an interpreter and c, c++ and Pascal are compilers. Compilers turn all of the statements into one big object and than at run time that file is loaded and executed. At run time, the compiler need not even be loaded. Interpreters compile each statement and then execute it. Programs tend to have code that executed many times and this is where interpreters perform badly because each statement ends up being compiled and executed many times while a compiler would compile the statement only once.


If c runs so much faster than Pascal why do I use Pascal then? At least Five reasons:


  • Pascal(Lazarus) makes it much easier to create a GUI or Windowing application.

  • Pascal minimizes the use of Pointers.

  • Pascal Arrays perform almost as well as c Arrays and are much simpler to program.

  • Pascal tends to self document much better, c and descendant languages cram in as much action in as few characters as possible while Pascal values clarity.

  • Pascal tends to compile much faster.

I can throw a simple GUI application together in less than an hour. That would include a data entry screen for a MySQL table. If multiple tables are required, that might require an afternoon.


In this case, the program was tiny. In most cases that isn’t true. A large application in c or c++ has many functions and to pass a value by reference in c or c++, the address must be passed. Therefore in the called function the variable must be accessed with a pointer. If you forget and access it like a normal variable, it will change the address and you will be pointing at an unintended object. Programs may crash due to errors like this or even worse it may continue to run but with unintended results. The only time pointers are required in Pascal is if you are working with Linked Lists or Trees. Therefore Pascal programs are just simpler to look at and maintain.


Pascal Arrays perform almost as well as they do in c. The problem in c is that you almost are required to use pointers to access or modify the individual elements. Look at a Shell Sort in Pascal versus one in c, the Pascal version is much simpler to look at and each element is accessed via a index, not a pointer. And then there is the pointer math that is required. I downloaded the c and Pascal shell sorts off of Rosetta and played with them once. For large arrays the sort time for c was 4% faster than the Pascal version, that is really insignificant for the difference in complexity.


Lastly, compile time. In this case the Pascal program compiled in 0.007 seconds, while the c program compiled in 0.044 seconds. I have read that for large applications in c or c++ can take hours to compile. I have an Astronomy program that is fairly large, it compiles in just a couple of minutes. In truth using Pascal almost seems like you are using an interpreter since the compiles are so fast.