Re: Re: Finding the closest number from a list
- To: mathgroup at smc.vnet.net
- Subject: [mg39551] Re: [mg39517] Re: Finding the closest number from a list
- From: Dr Bob <drbob at bigfoot.com>
- Date: Sat, 22 Feb 2003 03:39:00 -0500 (EST)
- References: <b32ab1$b3o$1@smc.vnet.net> <200302210907.EAA16923@smc.vnet.net>
- Reply-to: drbob at bigfoot.com
- Sender: owner-wri-mathgroup at wolfram.com
Robert's solution: GetClosest[lower_, upper_, item_, slist_] := Module[{dl, du, pos}, pos = If[upper â?¤ lower, upper, {dl, du} = Abs[item - slist[[{lower, upper}]]]; If[du > dl, upper, lower]]; slist[[pos]]] ClosestMatches[ list_?VectorQ, things_?VectorQ] := Module[{sorted, lint, first, last, n = Length[list], lower, upper}, sorted = Sort[list]; first = Max[Min[things], First[sorted]] - 1; last = Min[Max[things], Last[sorted]] + 1; lint = ListInterpolation[N[Join[{1}, Range[n], {n}]], {Join[{ first}, sorted, {last}]}, InterpolationOrder -> 1]; estimates = lint[things]; lower = Floor[estimates]; upper = Ceiling[estimates]; MapThread[GetClosest[##, sorted] &, {lower, upper, things}]] My solution (inspired by Robert's): closest[list_?VectorQ, things_?VectorQ] := Module[{sorted = Sort[list], lint, first, last, n = Length[list]}, first = Max[Min[things], First[sorted]] - 1; last = Min[Max[things], Last[sorted]] + 1; lint = Interpolation[Transpose@{ Join[{first}, sorted, {last}], Join[{1}, Range[ n], {n}]}, InterpolationOrder -> 1]; list[[Round@lint@things]] ] Test data: n = 30000; list = Table[Random[], {n}]; things = Table[Random[], {10000}]; Timings: Timing[ClosestMatches[list, things]; ] {0.9679999999999893*Second, Null} Timing[closest[list, things];] {0.5319999999999965*Second, Null} The answers aren't the same: Take[things, 5] Take[ClosestMatches[list, things], 5] Take[closest[list, things], 5] {0.6384367070141534, 0.6250093104126428, 0.12184236611421798, 0.09511197717954557, 0.679769352921642} {0.6383160624966754, 0.624879790611537, 0.12186365194457346, 0.09509389371548846, 0.679694223307205} {0.6384510214302745, 0.6250442930656402, 0.12184191844340701, 0.09512514224893226, 0.6797705952963301} Rounding the Interpolation output gets the closer result, and faster. Bobby On Fri, 21 Feb 2003 04:07:41 -0500 (EST), Robert Knapp <rknapp at wolfram.com> wrote: > 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. >> > > Here is an approach suggested by Stephen Wolfram which is very efficient: > The idea is to use a first order interpolation to find an estimate to the > closest index, then use actual comparison to determine which element is > actually closest. The reason this approach is fast is because > InterpolatingFunctions in Mathematica use a fast method to determine > where an input lies in a data list. > > This function finds which element is actually closest given an upper and > lower bound on the index into the sorted list: > > In[1]:= > GetClosest[lower_, upper_, item_, slist_] := Module[{dl, du, pos}, > pos = If[upper <= lower, > upper, > {dl, du} = Abs[item - slist[[{lower, upper}]]]; > If[du > dl, > upper, > lower] > ]; > slist[[pos]] > ] > > This function returns the list of closest matches in the List list given > a List things of items you want to find the closest matches to. I have > not included it in the argument checking, but this will only work if both > lists are real or integer -- for complex numbers a more complicated > scheme would have to be concocted. > > In[2]:= > ClosestMatches[list_?VectorQ, things_?VectorQ] := > Module[{sorted, lint, first, last, n = Length[list], lower, upper}, > sorted = Sort[list]; > first = Max[Min[things], First[sorted]] - 1; > last = Min[Max[things], Last[sorted]] + 1; > lint = > ListInterpolation[ > N[Join[{1},Range[n],{n}]],{Join[{first},sorted,{last}]}, > InterpolationOrder\[Rule]1]; > estimates = lint[things]; > lower = Floor[estimates]; > upper = Ceiling[estimates]; > MapThread[GetClosest[##, sorted]&, {lower, upper, things}] > ] > > This sets up a random list and a random set of things. > > In[3]:= > n = 30000; > list = Table[Random[],{n}]; > things = Table[Random[],{10000}]; > > This finds the matches > > In[6]:= > Timing[ClosestMatches[list, things];] > > Out[6]= > {2.51 Second,Null} > > > > The timing was done on a 750MhZ PentiumIII system. > > > > -- majort at cox-internet.com Bobby R. Treat
- References:
- Re: Finding the closest number from a list
- From: Robert Knapp <rknapp@wolfram.com>
- Re: Finding the closest number from a list