Message Boards Message Boards

Optimize search for rational numbers on unit circle?

11 Replies

Here is a somewhat improved version (using Compile and the better performance of functions being Listable):

cTest = Compile[{{frac, _Integer, 1}}, 
   With[{c = Sqrt[frac[[2]]^2 - frac[[1]]^2]}, 
    If[IntegerPart[c] == c, frac, {-1, -1}]], RuntimeAttributes -> {Listable}];

ratUniFareyC[n_Integer] := Module[{fareyData},
  fareyData = Function[frac, {Numerator[frac], Denominator[frac]}, Listable]@FareySequence[10^n - 1];
  Rational @@@ Select[cTest@fareyData, # != {-1, -1} &]]

With this I get on my pretty old hardware (Linux makes it possible!):

ratUniFareyC[4] // Length // AbsoluteTiming
(*  Out:   {101.15, 3188}   *)
POSTED BY: Henrik Schachner
Posted 1 year ago

I'm not sure of the exact performance implications, but using CoprimeQ with just one pass might be better than creating all of the fractions and then selecting based on the length of the denominator. Using a helper function,

AllFractionsWithDenom[d_] := Select[Range@d, CoprimeQ[#, d] &]/d

and then timing this

Flatten[AllFractionsWithDenom /@ Range[1000, 9999]]

takes about 55 seconds on my machine.

On the other hand, timing this

Union[Divide @@@ Flatten[Table[{m, k}, {k, Range[10^4 - 1]}, {m, k - 1}], 1]]

(which doesn't include the next step to filter down to just 4 digit denominators) takes 259 seconds on my machine.

POSTED BY: Eric Rimbey

Dear Vitaliy,

you always come up with interesting problems! My first guesses would be:

  • making use of the build in function FareySequence[];
  • using Select[] instead of Cases[] to avoid inefficient pattern matching;

So I end up with a simple one-liner:

ratUniFarey[n_] := Select[Sqrt[1 - #^2] & /@ FareySequence[10^n - 1], # \[Element] Rationals &]

Making "performance measurement" does not make much sense on my really old hardware. And I do not have an idea on using parallelization here.

Maybe that helps a bit! Best regards -- Henrik

POSTED BY: Henrik Schachner

StackOverflow, at the link above, has already provided a few very fast implementations; here is another one. It uses the CoprimeQ trick from @Eric Rimbey and the fact that if m/n is an x-coordinate of a rational point on a uni circle, then n^2-m^2 is an exact square. We can then hash all the exact squares once to speed up the calculation considerably.

ClearAll[RationalUni, DenominatorLength, PythagoreanTuples]

DenominatorLength[x_] := 
 If[IntegerQ[x], 0, Ceiling[Log10[Denominator[x]]]]

PythagoreanTuples[n_, squares_] :=
 {#/n, Sqrt[1 - (#/n)^2]} & /@
  Select[Range[1, Ceiling[Sqrt[n^2/2]]], 
   CoprimeQ[n, #] && squares[n^2 - #^2] &]

RationalUni[n_] := Module[{squares},
  squares[_] = False;
  Scan[(squares[#^2] = True) &, Range[10^n]];
  Union@Flatten@Select[
     Flatten[
      Parallelize[
       PythagoreanTuples[#, squares] & /@ 
        Range[10^(n - 1), 10^n - 1]], 1],
     DenominatorLength[Last[#]] == n &]]

Let's test it:

In[220]:= Sort@ratUni[3] == Sort@RationalUni[3]
Out[220]= True

In[221]:= ratUni[4] // Length // AbsoluteTiming
Out[221]= {970.987, 2870}

In[219]:= RationalUni[4] // Length // AbsoluteTiming
Out[219]= {7.80111, 2870}

In[213]:= RationalUni[5] // Length // AbsoluteTiming
Out[213]= {739.865, 28652}

We managed to speed up the calculation for n=4 by over 100 times, measuring on the same CPU, and calculate RationalUni[5] in just over 12 minutes, which is still faster than the original solution for n=4.


macOS Big Sur 11.7.2 / Wolfram Desktop 13.2 /
3.6 GHz 8-Core Intel Core i9

POSTED BY: Victor Kryukov

@Victor - this is the best timing I've seen around -- thank you! Do you want to post it this answer also on Stack Exchange?

https://mathematica.stackexchange.com/q/278250/13

I plan to post a bounty there and if no one beats your time I will give then the bounty to you :-)

POSTED BY: Vitaliy Kaurov

@Henrik - wonderful idea - thank you very much!

POSTED BY: Vitaliy Kaurov

POSTED BY: Vitaliy Kaurov

...and @Victor -- thanks a lot for computing RationalUni[5] -- it was unattainable for me. I wonder what the next few points are for $n=6,7,8,...etc.$ but we'd probably need a cluster for that. Time seems rapidly increasing.

POSTED BY: Vitaliy Kaurov

Thanks for the kind words, but I don't think my solution is that fast - e.g., the one using Solve is almost instantaneous for n=5.

POSTED BY: Victor Kryukov

E.g.

In[5]:= ParallelTable[{a, b}/c /. # & /@ 
       Solve[a^2 + b^2 == c^2 && a <= b, PositiveIntegers], {c, 5, 
       100000, 4}] // Flatten[#, 1] & // SortBy[Denominator@*First] //
    DeleteDuplicates // Length // AbsoluteTiming
Out[5]= {7.43017, 15919}

Which matches the total of the first five elements of your sequence below:

In[4]:= Total@{1, 15, 142, 1435, 14326}
Out[4]= 15919
POSTED BY: Victor Kryukov
Posted 1 year ago

I'm a bit late to the game, but...

For an octant there are roughly $n$ solutions for denominators less or equal to $2 \pi n$. See https://mathematica.stackexchange.com/a/278283/3056, and in particular https://oeis.org/A020882, which is the sequence of denominators of solutions to this problem. I don't believe there is a closed form or a recurrence formulation for this.

POSTED BY: Jari Kirma
Reply to this discussion
Community posts can be styled and formatted using the Markdown syntax.
Reply Preview
Attachments
Remove
or Discard

Group Abstract Group Abstract