MathGroup Archive 2003

[Date Index] [Thread Index] [Author Index]

Search the Archive

Re: Finding the closest number from a list

  • To: mathgroup at smc.vnet.net
  • Subject: [mg39542] Re: [mg39510] Finding the closest number from a list
  • From: Daniel Lichtblau <danl at wolfram.com>
  • Date: Sat, 22 Feb 2003 03:37:55 -0500 (EST)
  • References: <200302201014.FAA11320@smc.vnet.net>
  • Sender: owner-wri-mathgroup at wolfram.com

Jacob Rome wrote:
> 
> Hi,
> 
> I have a seemingly simple problem. I want to find the element in a
> list that is closest to a number I specify. In my case, I have a list
> of about 30,000 numbers (y[i]). I want to find the closest match for
> each of about 10,000 other numbers (call them x[i]). Obviously, speed
> is important. I've sorted the large list, and right now I'm going
> through each y[i] from lowest to highest and testing it to see if x[i]
> is less than that value. This takes about .1 seconds for each x[i].
> 
> I'm wondering if anyone has had a similar problem, and if there is a
> better function built-in to Mathematica. Alternatetively, I could
> build my own. I've just recently realized that I could also reduce the
> search time considerably if I sort the x[i] list as well, and only
> start my search from where I last left off.  Any ideas on which
> approach would be more efficient? Thanks.


If all elements are machine real numbers this can be done quite fast by
sorting both lists together, augmenting elements in each list with an
index of e.g. 1 or 2 to denote from which list they originate. To allow
for fast code throughout we use machine reals for these as well (so
packed array technology will be used behind the scenes).

We use fake "ends" for the second list in order to be certain the entire
merged list is bracketed by elements from that list. We will choose them
in such a way that they cannot be taken as closest elements to elements
in the first list.

Once sorted, we go through the list and augment each element by the
closest element from the second list that is to its left. We repeat that
with closest elements to the right.

list. They now have the nearest second list elements from each side
attached to them. We determine which is closer, and return pairs of the
form

{list 1 element, closest list 2 element}.

If instead we want to retain original positions in the first list and/or
give positions of closest elements in second list, minor modifications
of the code below (to retain list index positions) could be used. Again
one would want to store them as machine reals in order to get optimal
speed.

closestPoints = Compile[{{list1,_Real,1}, {list2,_Real,1}},
	Module[{lo, hi, n1, n2, len1=Length[list1], len2=Length[list2]+2,
	  full, bound=1., t1},
	lo = -2.*(Abs[Min[list1]] + Abs[Min[list2]]);
	hi = 2.*(Abs[Max[list1]] + Abs[Max[list2]]);
	n1 = Transpose[{list1,Table[1.,{len1}]}];
	n2 = Transpose[{Join[{lo},list2,{hi}], Table[2.,{len2}]}];
	full = Sort[Join[n1,n2]];
	t1 = Table[
		If [full[[j,2]]==2., bound = full[[j,1]]];
		Append[full[[j]],bound]
		, {j, len1+len2}
		];
 	t1 = Table[
		If [full[[j,2]]==2., bound = full[[j,1]]];
		Append[t1[[j]],bound]
		, {j, len1+len2, 1, -1}
		];		
  	t1 = Select[t1, (#[[2]]==1.)&];
	Map[
		If[#[[1]]-#[[3]] < #[[4]]-#[[1]],
		  {#[[1]],#[[3]]}, {#[[1]],#[[4]]}]&, t1]
	]];

Example:

l1 = Table[Random[], {30000}]
l2 = Table[Random[], {10000}]

In[66]:= Timing[closestPoints[l1,l2];]
Out[66]= {0.58 Second, Null}

This on a 1.5 GHz machine.


Daniel Lichtblau
Wolfram Research


  • Prev by Date: Re: Finding the closest number from a list
  • Next by Date: Re: Re: Finding the closest number from a list
  • Previous by thread: Finding the closest number from a list
  • Next by thread: Re: Finding the closest number from a list