4

Given multiple (x,y) ordered pairs, I want to compare distances between each one of them. So pretend I have a list of ordered pairs:

pairs = [a,b,c,d,e,f]

I have a function that takes two ordered pairs and find the distance between them:

def distance(a,b):
    from math import sqrt as sqrt
    from math import pow as pow
    d1 = pow((a[0] - b[0]),2)
    d2 = pow((a[1] - b[1]),2)
    distance = sqrt(d1 + d2)
    return distance

How can I use this function to compare every ordered pair to every other ordered pair, ultimately finding the two ordered-pairs with the greatest distance between them?

Psuedopsuedocode:

     distance(a,b)
     distance(a,c)
     ...
     distance(e,f)

Any help would be tremendously appreciated.

Brian Campbell
  • 322,767
  • 57
  • 360
  • 340
  • 3
    "from math import sqrt as sqrt" is exactly the same as "from math import sqrt". The former is like saying "Hi, my name is Roberto, but just call me Roberto" :) – Roberto Bonvallet Apr 08 '09 at 04:59
  • You can also omit the math module and use `** 0.5` – Joe Koberg Apr 08 '09 at 05:00
  • I suspect you really want unordered pairs, since your distance function is symmetric.. – John Fouhy Apr 08 '09 at 05:01
  • @John: Sounds right. Also, his example stops at distance(e, f) -- if he'd wanted ordered pairs, the last one would be distance(f, e). – John Feminella Apr 08 '09 at 05:05
  • One little tip: if you don't care about what the actual distance is (just that they're the two pairs furthest apart), you can eliminate the square root to make this faster. Squaring is monotonic and increasing for values greater than or equal to 0. i.e. x > y if and only if x*x > y*y for x, y >= 0. – ntownsend Apr 08 '09 at 17:53

5 Answers5

17

in python 2.6, you can use itertools.permutations

import itertools
perms = itertools.permutations(pairs, 2)
distances = (distance(*p) for p in perms)

or

import itertools
combs = itertools.combinations(pairs, 2)
distances = (distance(*c) for c in combs)
Evgeny Lazin
  • 9,193
  • 6
  • 47
  • 83
  • Combinations may be better, as A->B and B->A will be the same distance. – Joe Koberg Apr 08 '09 at 04:59
  • You're using the same term for 2 different things. Might want to choose a better variable name. A "pair" would seem to be an item of "pairs", but it isn't. – Algorias Apr 08 '09 at 14:27
10
try:

    from itertools import combinations

except ImportError:

    def combinations(l, n):
        if n != 2: raise Exception('This placeholder only good for n=2')
        for i in range(len(l)):
            for j in range(i+1, len(l)):
                yield l[i], l[j]


coords_list = [(0,0), (3,4), (6,8)]

def distance(p1, p2):
    return ( ( p2[0]-p1[0] ) ** 2 + ( p2[1]-p1[1] )**2 ) ** 0.5

largest_distance, (p1, p2) = max([
     (distance(p1,p2), (p1, p2)) for (p1,p2) in combinations(coords_list, 2)
     ])


print largest_distance, p1, p2
Joe Koberg
  • 25,416
  • 6
  • 48
  • 54
  • What happened to the answer that didn't require itertools? I come back to this page and it's gone! :( –  Apr 08 '09 at 05:19
6

Try:

max(distance(a, b) for (i, a) in enumerate(pairs) for b in pairs[i+1:])

This avoid identity-comparisons (e.g. distance(x, x), distance(y, y), etc.). It also avoids doing symmetric comparisons, since distance(x, y) == distance(y, x).


Update: I like Evgeny's solution to use itertools a little better, as it expresses what you're trying to do more succinctly. Both of our solutions do the same thing. (Note: make sure you use combinations, not permutations -- that will be much slower!)

Community
  • 1
  • 1
John Feminella
  • 303,634
  • 46
  • 339
  • 357
  • This is an elegant solution with a single possible downside: it might be costly to copy the list once for every element. – Joe Koberg Apr 08 '09 at 05:40
4

slightly related, you don't have to compute the euclidean distance yourself, there's math.hypot:

In [1]: a = (1, 2)
In [2]: b = (4, 5)
In [3]: hypot(a[0]-b[0], a[1]-b[1])
Out[3]: 4.2426406871192848
Autoplectic
  • 7,566
  • 30
  • 30
3

If you don't mind doing distance calculations between two points that are the same twice, the following will find the greatest distance:

max( [distance(a, b) for a in pairs for b in pairs] )

In order to have the a and b pair instead, then do the following:

import operator
max( [((a,b), distance(a, b)) for a in pairs for b in pairs], key=operator.itemgetter(1))

You can combine this with John Feminella's solution to get the (a,b) tuple without doing excess distance comparisons

Joe
  • 166
  • 1
  • 4