20
$\begingroup$

I'd like to get exactly 5 divisions from x to y on a log scale. Can FindDivisions do this?

$\endgroup$

5 Answers 5

22
$\begingroup$

EDIT:

After 3 years, it has been discovered that this oft-linked-to answer doesn't truly space logarithmically. It's close, which was all I was going for at the time (and handles zeros), but it's not quite right. Anyway, thanks to @Pickett's careful moderation, here's a better version...

logspace[increments_, start_?Positive, end_?Positive] :=
 Exp@Range[Log@start, Log@end, Log[end/start]/increments]

This one, by the stodgy nature of Logs, won't handle non-positive numbers, so I'll leave the old answer. Sorry to all that lost millions in the stock market using the old function. :)

OLD FUNCTION

I built a function that calculates log spaced increments for a job at work. I've added a catch where it will handle log spacing from 0 to a number.

logspace [increments_, start_, end_] := Module[{a}, (
   a = Range[0, increments];
   Exp[a/increments*Log[(end - start) + 1]] - 1 + start
   )]

To try it out:

N@logspace[5,1,1000]

(*{1., 3.98107, 15.8489, 63.0957, 251.189, 1000.}*)

To view it on a number line:

a = N@logspace[10, 0, 10];
Graphics[Point@Transpose[{a, ConstantArray[.5, 11]}], Axes -> {True, False}, 
   AxesStyle -> Arrowheads[.05]]

enter image description here

And if you want to find the distances between divisions, use Differences:

Differences[a]

(*{0.270982, 0.344413, 0.437742, 0.556362, 0.707126, 0.898744, 1.14229, 1.45183, 1.84524, 2.34527}*)
$\endgroup$
4
  • $\begingroup$ A problem (I think?) with this function was discovered, please see the comments here. $\endgroup$
    – C. E.
    Commented Sep 14, 2015 at 21:53
  • $\begingroup$ @Pickett Oh dear. I wrote that 3 years ago (about the amount of time I've used MMA).... eek. Yeah, it log-ish spaces, but isn't a "true" logspace. Let me work on that... $\endgroup$
    – kale
    Commented Sep 15, 2015 at 0:13
  • $\begingroup$ Thanks for the quick response! :) $\endgroup$
    – C. E.
    Commented Sep 15, 2015 at 8:41
  • $\begingroup$ So how to get the logspace from -1 to 2? $\endgroup$
    – yode
    Commented Mar 7, 2016 at 8:23
24
$\begingroup$

Writing one wouldn't be that hard. You can convert it to Log10 and then let FindDivisions do all the work in log space before converting it back. For example:

findLogDivisions[{xmin_, xmax_}, n_Integer] := 10^FindDivisions[Log10@{xmin, xmax}, n]

Then, to find 4 "nice" divisions in log space between 1 and 1000, you simply need to do:

findLogDivisions[{1, 1000}, 5] 
(* {1, 10, 100, 1000} *)
$\endgroup$
8
$\begingroup$

Here's a mathematically simple approach, assuming that exactly n divisions are sought, no matter how nice or not.

This produces exactly n intervals:

Clear[logDiv];
logDiv[{x_?Positive, y_?Positive}, n_Integer /; n > 0] := 
  x (y/x)^Range[0, 1, 1/n]

logDiv[{10, 10000}, 3]
(* {10, 100, 1000, 10000} *)

If you want n "fence posts", use

Clear[logDiv];
logDiv[{x_?Positive, y_?Positive}, n_Integer /; n > 1] := 
  x (y/x)^Range[0, 1, 1/(n-1)]

If you want n interior division points, change n to n+1 in the first version.


Comparisons. Kuba's method produces the same output as logDiv mutatis mutandis​*. Below I use the first version of logDiv and omit Kuba's output.

Comparison 1:

N @ logDiv[{1/10, 100000}, 3]           (* same output as Kuba *)
N @ logspace[3, 1/10, 100000]           (* kale *)
N @ findLogDivisions[{1/10, 100000}, 3] (* rm -rf *)
(*
  {0.1, 10., 1000., 100000.}
  {0.1, 45.516, 2153.55, 100000.}
  {0.01, 1., 100., 10000., 1.*10^6}
*)

Comparison 2:

N @ logDiv[{10, 100000}, 5]
N @ logspace[5, 10, 100000]
N @ findLogDivisions[{10, 100000}, 5]
(*
  {10., 63.0957, 398.107, 2511.89, 15848.9, 100000.}
  {10., 18.9998, 108.996, 1008.95, 10008.3, 100000.}
  {10., 100., 1000., 10000., 100000.}
*)

Note the outputs vary; in particular logspace does something quite different than the others when start is different than 1. Depending on the application, one or the other might be desired.


*In honor of the language survey.

$\endgroup$
3
  • $\begingroup$ +1, what do you think about editing the question. Now the answer RandomReal[{start, end}, 5] fits quite well. $\endgroup$
    – Kuba
    Commented Mar 18, 2014 at 13:10
  • $\begingroup$ +1 to you, too. I'm assuming RandomReal is a joke :) However the question could more clearly state what exactly counts as a division. It seems to me the OP should decide, but it's not a big deal. I wish the OP had accepted or commented on the other answers, though. $\endgroup$
    – Michael E2
    Commented Mar 18, 2014 at 13:17
  • $\begingroup$ Yes it is a joke but it fits :) Maybe it's better with it's vague form, more answers are valid. $\endgroup$
    – Kuba
    Commented Mar 18, 2014 at 13:20
6
$\begingroup$

I do a lot of work where I need to have function evaluations at equal logarithmic spacings. The code I use is

GeometricRange[imin_, imax_, perRange[n_]] := GeometricRange[imin, imax, 
  (imax/imin)^(1/(n - 1))]; 
GeometricRange[imin_, imax_, r_] := Exp[Range @@ Log[N[{imin, imax, r}]]]; 
perDecade[n_] := 10^(1/(n - 1)); 
perOctave[n_] := 2^(1/(n - 1)); 

GeometricRange has the same calling syntax as Range, where the increment (in this case, the ratio r) is given as an argument, but also has the option of specifying the total number of samples in the range with perRange or commonly used log resolutions with perDecade or perOctave. The calling syntax is

In[10]:= GeometricRange[10, 1000, 10]
Out[10]= {10., 100., 1000.}

or

In[9]:=GeometricRange[10, 1000, 10 // perRange]
Out[9]= {10., 16.681, 27.8256, 46.4159, 77.4264, 129.155, 215.443, 359.381, 599.484, 1000.}

or

In[6]:= GeometricRange[10, 1000, 10 // perDecade]
Out[6]= {10., 12.9155, 16.681, 21.5443, 27.8256, 35.9381, 46.4159,
59.9484, 77.4264, 100., 129.155, 166.81, 215.443, 278.256, 359.381,
464.159, 599.484, 774.264, 1000.}

Here is the complete code for Version 10 and up, using PowerRange

perDecade[(n_)?Positive] := 10^(1/(n - 1)); 
perOctave[(n_)?Positive] := 2^(1/(n - 1)); 
perRange /: PowerRange[imin_, imax_, perRange[(n_Integer)?Positive]] := 
  PowerRange[imin, imax, (imax/imin)^(1/(n - 1))]; 
$\endgroup$
1
  • 4
    $\begingroup$ As of version 10.0, GeometricRange can be replaced by the built-in function PowerRange. $\endgroup$
    – Daniel W
    Commented Sep 15, 2015 at 12:04
5
$\begingroup$

Array may be used just like here by rm -rf but only for V9 or later version: see this post

10^Array[# &, 6, Log10@{1., 1000}]
{1., 3.98107, 15.8489, 63.0957, 251.189, 1000.}
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.