Friday, March 25, 2011

How to use Perl, PHP, Python, R, and Ruby to find Euclidean distance between two points

According to Wikipedia:
In the Euclidean plane, if p = (p1, p2) and q = (q1, q2) then the distance is given by:

Assuming you are using the Cartesian coordinate system, your two coordinates must both be of length n, where n can be any non-zero positive integer. So if the two points in question, p and q, each had 3 values:

p = (p1, p2, p3), q = (q1, q2, q3)

Then n would equal 3, and the distance formula would be:



The hardest part for me, when putting this all together, was figuring out, for each scripting language, how to iterate over, i.e., step through, the respective elements of both coordinates, simultaneously:

Perl 5 (5.12.2):
use List::Util qw(sum);
use List::MoreUtils qw{ any pairwise };

sub get_distance_between
{
if (scalar(@_) != 2)
{
die "You must supply 2, and only 2, coordinates, no more, no less. Stopped";
}

my $a_ref = shift;
my $b_ref = shift;

if (@$a_ref != @$b_ref)
{
die "Coordinates do not have the same number of elements, stopped";
}
else
{
@x = pairwise { ($a - $b)**2 } @a, @b;
return sqrt(sum(@x));
}
}

# Usage:
@a = (1008, 2243, 976), @b = (1005, 2249, 974);
$ad = get_distance_between(\@a, \@b);
print "\$ad == $ad";

PHP 5.3.6:
function subtract_and_square($n, $m)
{
return(pow($n - $m, 2));
}

function find_distance_between($p, $q)
{
$numargs = func_num_args();
if ($numargs != 2)
{
die("You must supply 2, and only 2, coordinates, no more, no less.\n");
}
else if (sizeof($p) != sizeof($q))
{
die("Coordinates do not have the same number of elements.\n");
}
else
{
$c = array_map("subtract_and_square", $p, $q);
return pow(array_sum($c), .5);
}
}

Python 2 (2.5.4):
def get_distance_between(p, q):
if len(p) != len(q):
raise Exception
else:
sum = 0
for pval, qval in zip(p, q):
sum += (pval - qval)**2
return sum**(.5)

R (2.12.1):
get_distance_between = function(p, q)
{
if (length(p) != length(q))
{
stop("\nCoordinates do not have the same number of elements.")
}
else
{
element_products = mapply(function(a,b) (a - b)**2, p, q)
sqrt(sum(element_products))
}
}

Ruby 1.9.2:
require 'generator'

def get_distance_between(p, q)
if p.length != q.length
raise Exception
else
sum = 0
syen = SyncEnumerator.new(p, q)
for pval, qval in syen
sum += (pval - qval)**2
end
end
return Math.sqrt(sum)
end

In the Perl example, thanks to Miller Hall and Jon Bjornstad for their review and suggestions on how to improve my Perl code to make it more readable.
In the R example, thanks to Spencer Graves and Nicholas Lewin-Koh for pointing me to mapply and plyr (which I did not have time to research and use).

Update: On October 31, 2011, my friend Nikita Borisov, assistant professor in Computer Science, offered the following suggestion on facebook re the R and Python code:
This person clearly hasn't used these languages very much. In R, you can say sqrt(sum((p-q)**2)). Similarly, in Python you can write sqrt(sum([(x-y)**2 for (x,y) in zip(p,q)]))
Thanks, Nikita!

3 comments:

  1. Here's a ruby version without the exception handling (easy to put in if you'd like though) ...

    def distance(a, b)
    Math.sqrt(a.zip(b).inject(0) { |d, c| d + (c[0] - c[1]) ** 2 })
    end

    ReplyDelete
  2. A nicer Ruby euclidean distance would be as follows:

    euclidean_distance = ->(p1, p2) do
    Math.sqrt(p1.zip(p2).map{|a,b| a-b}.map{|d| d*d}.reduce(:+))
    end

    http://philipcunningham.org/blog/2011/12/13/ruby-euclidean-distance/

    ReplyDelete
  3. the perl code should be
    @x = pairwise { ($a - $b)**2 } @{$a_ref}, @{$b_ref};

    as the existing code is accessing the global arrays @a and @b defined outside of the subroutine.

    ReplyDelete