13 Mar 2014
Algorithms, C#, Maths, SQL
What can mathematics do for your programming? That's an interesting question to explore, even more so when answered with an example. As luck would have it, such an example presented itself to me at work a few months ago.
Our mobile phone application makes use of mobile phone GPS positioning to assist the user in finding a resource that is within their current vicinity. A list of resources is created, then sorted based on the distance between the user's location and the location of each resource. Distance is approximate, and calculated based on a straight line between the two points, and doesn't take into account streets, buildings, or anything else that modern mapping software does so well. It's a quick and dirty way to give the end user an approximate idea of how far they will need to travel. However it was super slow, taking at least 18 seconds to respond for only a handful of records - what was going on?
Now that we understand what needs to happen, let's have a look at how it was implemented. The mobile app first initiates the request to the web service, then:
This all seems pretty straight forward, so let's drill a little more into the key areas of the code. Note that this code appears in sanitised form! Here's the code that first queries the database via Entity Framework:
So here's what's this LINQ statement is essentially achieving:
Key points from the above LINQ statement:
This is all well and good, until you dig into the code contained within the db.DistanceBetween method. And here it is:
Yep, that's correct. This code goes back to the database, and executes the following scalar function:
ALTER FUNCTION [dbo].[DistanceBetween] (@Lat1 AS real, @Long1 AS real, @Lat2 AS real, @Long2 AS real) RETURNS REAL AS BEGIN DECLARE @dLat1InRad AS float(53); SET @dLat1InRad = @Lat1 * (PI()/180.0); DECLARE @dLong1InRad AS float(53); SET @dLong1InRad = @Long1 * (PI()/180.0); DECLARE @dLat2InRad AS float(53); SET @dLat2InRad = @Lat2 * (PI()/180.0); DECLARE @dLong2InRad AS float(53); SET @dLong2InRad = @Long2 * (PI()/180.0); DECLARE @dLongitude AS float(53); SET @dLongitude = @dLong2InRad - @dLong1InRad; DECLARE @dLatitude AS float(53); SET @dLatitude = @dLat2InRad - @dLat1InRad; /* Intermediate result a. */ DECLARE @a AS float(53); SET @a = SQUARE (SIN (@dLatitude / 2.0)) + COS (@dLat1InRad) * COS (@dLat2InRad) * SQUARE(SIN (@dLongitude / 2.0)); /* Intermediate result c (great circle distance in Radians). */ DECLARE @c AS real; SET @c = 2.0 * ATN2 (SQRT (@a), SQRT (1.0 - @a)); DECLARE @kEarthRadius AS real; /* SET kEarthRadius = 3956.0 miles */ SET @kEarthRadius = 6376.5; /* kms */ DECLARE @dDistance AS real; SET @dDistance = @kEarthRadius * @c; RETURN (@dDistance) END
This function is obviously a hack (we don't use miles in Australia) and has just been copied in from somewhere.
Let's not forget that the following is executed 3 times per record! When you factor in all method calls involved in performing this calculation, it becomes one EF method, and one scalar function call per record, multiplied by 3. That's now 6 moving parts per record!
As you can see, end-to-end, there's a fair bit of code being pushed around here to calculate the distance between two points - too much code in my opinion.
When I first uncovered this code, I spotted instantly the pattern, and new that there was a simple mathematical solution for this. I used the Pythagorean Distance Formula:
d = √ΔX2 + ΔY2
You can read this is: distance is the square root of delta X squared plus delta Y squared. ΔX denotes the change (Delta) in X.
Expanding this slightly:
Distance = √(X2 - X1)2 + (Y2 - Y1)2
In code, this became:
Making a few other adjustments (including throwing away Entity Framework) reduced the code size dramatically, and reduced the execution time for equivalent operations from a minimum of 18 seconds to less than a millisecond.
My approach was as pragmatic as I could be - get it done, and get it out the door. There are other ways of achieving a similar result. For example, a much nicer solution could involving sending the user's coordinates into the database query and return the results with the addition of computed column that contains the calculated value already.
It may seem that this article is simply a dig at the programmer who orignally wrote this code, after all, we all try and write the best possible solution at the time don't we? And it's very easy to be smug with the benefit of hindsight. That aside, that's not my intention at all. The goal of this article is to demonstrate how simple mathematics can improve your programming in so many ways:
I hope this article has been of some use to you. Don't be scared of maths, get into it, the results are worthwhile and are sometimes immediate.
Copyright © 2025 carlbelle.com