Wikipedia:Reference desk/Archives/Mathematics/2010 December 8

= December 8 =

Approximating the relative value of three positive real numbers x:y:z with positive integers a:b:c
For any three positive real numbers, $$x$$, $$y$$, and $$z$$, I would like to find a sequence of triples $$(a_0,b_0,c_0), (a_1,b_1,c_1), \ldots$$ (where $$a_i$$, $$b_i$$, and $$c_i$$ are positive integers for all $$i$$) such that each triple approximates the relative values of the triple $$(x, y, z)$$ "better" than the triple before it. I am flexible on what precisely we mean by "better." Or, in other words, if I draw a ray from the origin, as $$(xt,yt,zt)$$ for $$t>0$$, how can I quickly determine what integer lattice points the ray comes closest to?

If there were only two positive real numbers, $$x$$ and $$y$$, I could use the continued fraction expansion of $$x/y$$. I'd set $$a_i$$ equal to the numerator of the $$i$$th convergent and I'd set $$b_i$$ equal to the denominator of the $$i$$th convergent. This would give me a sequence $$(a_0,b_0), (a_1,b_1), \ldots$$ where the pairs give better and better approximations to the relative value of $$x$$ and $$y$$. But how do I do this for three or more positive real values?

This is not a work or homework question, just idle curiosity. Thanks -- — Q uantling (talk &#124; contribs) 20:45, 8 December 2010 (UTC)


 * The LLL algorithm is probably overkill for that. 67.117.130.143 (talk) 22:39, 8 December 2010 (UTC)


 * I wrote a FORTRAN program and included subroutine to do what you want. Look through the comments to tease out the logic:


 * Here's a run (trimmed a bit to fit on this screen):

Ray V1.0

Enter i,j,k values: 1.1 2.2 333

(i,j,k) = (         1.100000000,          2.200000000,        333.000000000 )

(X,Y,Z) = ( 0, 0,  1), Distance from ray = .00738621, Distance from origin =    1.00000 (X,Y,Z) = ( 1, 2, 302), Distance from ray = .00537179, Distance from origin = 302.00828 (X,Y,Z) = ( 1, 2, 303), Distance from ray = .00201442, Distance from origin = 303.00825 (X,Y,Z) = ( 3, 6, 908), Distance from ray = .00134295, Distance from origin = 908.02478 (X,Y,Z) = ( 4, 8,1211), Distance from ray = .00067147, Distance from origin = 1211.03303 (X,Y,Z) = (11,22,3330), Distance from ray = .00000000, Distance from origin = 3330.09084


 * Let me know if you have any questions or suggestions for ways to improve it. (One efficiency improvement might be to only do the square root required to find DIST once you've determined that you want to print that value, since SQRT is a rather CPU-intensive operation.  Thus, you could get by with comparing distances squared, until then.)  I also considered doing the calcs in spherical coords, since that would eliminate the need for X, Y, and Z loops in the main program, and replace them with a single R loop, but that would necessitate some conversions between Cartesian and spherical coords, so I decided against it. StuRat (talk) 07:14, 9 December 2010 (UTC)

Applied &lt;syntaxhighlight>.&mdash;msh210 &#x2120; 07:26, 9 December 2010 (UTC)


 * Thanks, that makes it much more readable, although it does seem to change some text to white, which disappears against my white background, but highlighting the text fixes that prob. StuRat (talk) 07:34, 9 December 2010 (UTC)


 * Here's a stupid but simple way to do it: $$(a_i,b_i,c_i)=(\lfloor10^ix\rfloor,\lfloor10^iy\rfloor,\lfloor10^iz\rfloor)$$. —Bkell (talk) 08:15, 9 December 2010 (UTC)

It's been too long since I've done FORTRAN, so it is hurting me too much to try to figure out the underlying logic there. But, I am guessing that the approach presented there has a number of floating point operations that is more than linear in the number of output triples. I am hoping for something like the continued fraction approach that works for two numbers x:y; it has a count of floating point operations that is linear in the number of output pairs $$(a_i,b_i)$$. The decimal approach works in a sense but, for instance, it misses the perfect answers for 1:1/3:1/9. Any further ideas? Thanks! — Q uantling (talk &#124; contribs) 13:48, 9 December 2010 (UTC)


 * OK, I will list my program logic here:


 * 1) Determine which is bigger, the i, j, or k component of the vector for the ray starting at the origin. Count up along the x, y, or z axis, accordingly.  In my example, the k component was largest, so I incremented from z=1, up along the z-axis.


 * 2) For each plane (z=1, z=2, z=3, in my example), find the x,y,z coords of the intersection of that plane with the ray. Since only one of those coords is guaranteed to be an integer (z, in our example), the other two coords must be rounded both up and down, to create a total of 4 test points.  In our example, the true intersection with the z=908 plane would be at (2.9994,6.0533,908.0000).  We therefore test the points (2,6,908) (2,7,908) (3,6,908) and (3,7,908), to see if any of them is closer to the ray than our previous closest point.  If so, we print it out, and store that distance as the one we need to beat for the next "close point".


 * 3) The determination of the distance of a test point from the ray is done by first finding the T parameter (length along the ray from the origin) of the normal projection of the test point onto the ray. This parameter is then used to find the (x,y,z) coords of that normal projection, and the distance from there to the test point can then be determined.


 * Here's a run with the (i,j,k) values of (1,1/3,1/9):

Ray V1.0

Enter i,j,k values: 1 .3333333333 .1111111111

(i,j,k) = (         1.000000000,          0.333333333,          0.111111111 )

(X,Y,Z) = (1,0,0), Distance from ray = .33149677, Distance from origin = 1.00000 (X,Y,Z) = (9,3,1), Distance from ray = .00000000, Distance from origin = 9.53939


 * Isn't that the desired answer ? StuRat (talk) 18:37, 9 December 2010 (UTC)


 * The obvious (to me) definition of closeness is the angle between the two vectors, so that you want a sequence of points lying within successively thinner cones. The first algorithm that comes to mind is to simply start at (1,1,1) and at each step move to the neighbor (among the 7 in the local first octant) that minimizes that angle.  This may not be linear in the output, but it explores only an asymptotically one-dimensional volume.  It may be possible to (that is, I think you still get the right answer if you) optimize it by at each step incrementing the one coordinate that will then be the smallest multiple of the corresponding goal coordinate.  It may also be possible to project the cone into two (or all three) planes and search the resulting infinite wedges in parallel, discarding the occasional false hit that lies within the intersection of the projections but not within the cone proper.  --Tardis (talk) 15:41, 9 December 2010 (UTC)


 * I made a tweak to the FORTRAN program and subroutine to find some values I may have skipped before. Here's the modified source code:


 * Here's a run of the original program, with a new test case that shows the difference:

Ray V1.0

Enter i,j,k values:

93 97 101

(i,j,k) = (        93.000000000,         97.000000000,        101.000000000 )

(X,Y,Z) = ( 0, 0, 1), Distance from ray = .79938580, Distance from origin =   1.00000 (X,Y,Z) = ( 1, 1, 1), Distance from ray = .05828506, Distance from origin =   1.73205 (X,Y,Z) = (23,24, 25), Distance from ray = .01457126, Distance from origin = 41.59327 (X,Y,Z) = (93,97,101), Distance from ray = .00000000, Distance from origin = 168.10413


 * And here's a run of the modified version, using the same new test case, including a value I missed before:

Ray V2.0

Enter i,j,k values: 93 97 101

(i,j,k) = (        93.000000000,         97.000000000,        101.000000000 )

(X,Y,Z) = ( 0, 0, 1), Distance from ray = .79938580, Distance from origin =   1.00000 (X,Y,Z) = ( 0, 1, 1), Distance from ray = .78274502, Distance from origin =   1.41421 (X,Y,Z) = ( 1, 1, 1), Distance from ray = .05828506, Distance from origin =   1.73205 (X,Y,Z) = (23,24, 25), Distance from ray = .01457126, Distance from origin = 41.59327 (X,Y,Z) = (93,97,101), Distance from ray = .00000000, Distance from origin = 168.10413


 * The problem with the original program was in step 2. While it was correctly finding the actual intersections with the ray in an increasing order of distance from the origin, the 4 nearby test points tried for each intersection weren't necessarily tried in order of increasing distance from the origin.  Now they are. StuRat (talk) 20:17, 9 December 2010 (UTC)

StuRat &mdash; yes, that's a good answer in every respect, except that I was hoping for a faster run-time.

Tardis &mdash; I like the "smaller angle is better" formulation. And I like the idea of trying to simultaneously work in the projected wedges, to somehow bring the count of floating point operations down to linear in the number of output triples. How to accomplish that though? — Q uantling (talk &#124; contribs) 20:52, 9 December 2010 (UTC)


 * How fast does it need to be ? I do have a check that stops further searching once an exact intersection (or one within a set distance from an exact intersection) is found, and that limits run-time considerably.  My first example took between 40 and 50 milliseconds on a mid-level PC, but, as is typical for this type of program, the print statements are most of this time.  If I turn off the prints, that drops to 10 milliseconds of run-time.  As I said before, I could further reduce this time by only doing square roots when absolutely necessary.  Would you like me to make this change, to see how much faster that will make it ? StuRat (talk) 01:17, 10 December 2010 (UTC)


 * By the clock, your solution is excellent; I have no practical reason to need anything faster. (Actually, I have no practical reason to need any answer at all; this is just idle curiosity on my part.)  The only objection I have to your fine solution is that, as is possible with the simpler $$x:y$$ case, I am hopeful for an algorithm that has $$O(N)$$ floating point operations where $$N$$ is the size of the output.

I think I figured it out, actually. Let me play with it a bit more. — Q uantling (talk &#124; contribs) 13:46, 10 December 2010 (UTC)


 * OK, please let us know what you come up with. StuRat (talk) 22:10, 10 December 2010 (UTC)