Here I would like to give another heuristic that differs from btilly's.
The problem is to find the integers m and n so that m / n <= j < k <= (m + 1) / n , with n being as large as possible (but still under m ).
Intuitively, it is preferable that the m / n fraction be close to j . This leads to the idea of using continued fractions .
The algorithm I propose is quite simple:
- calculate all continued fractions of
j using minus signs (so fractions always squeeze j from above) until the denominator exceeds m ; - for each such fraction
m / n , find the largest integer i >= 0 such that k <= (m * i + 1) / (n * i) and n * i <= M , and replace the fraction m / n by (m * i) / (n * i) ; - among all fractions in 2, find the one with the largest denominator.
The algorithm is not symmetric in j and k . Therefore, there is a similar k -version, which in general should not give the same answer, so you can choose the larger of the two results.
Example: here I will take an example of btilly: j = 0.6 and k = 0.65 , but I will take M = 10 .
First I will go through procedure j . To calculate the continuation of the fractional fraction j , we calculate:
0.6 = 0 + 0.6 = 0 + 1 / (2 - 0.3333) = 0 + 1 / (2 - 1 / (3 - 0))
Since 0.6 is a rational number, the decomposition ends in just a few steps. Corresponding fractions:
0 = 0 / 1 0 + 1 / 2 = 1 / 2 0 + 1 / (2 - 1 / 3) = 3 / 5
Calculating the corresponding values of i in step 2, we replace three fractions:
0 / 1 = 0 / 1 1 / 2 = 3 / 6 3 / 5 = 6 / 10
The largest denominator is 6/10.
Continue with the example above, the corresponding procedure k would look like this:
0.65 = 1 - 0.35 = 1 - 1 / (3 - 0.1429) = 1 - 1 / (3 - 1 / (7 - 0))
Therefore, the corresponding fractions:
1 = 1 / 1 1 - 1 / 3 = 2 / 3 1 - 1 / (3 - 1 / 7) = 13 / 20
Step 2, we get:
1 / 1 = 2 / 2 2 / 3 = 6 / 9 13 / 20 = 0 / 0 (this is because 20 is already bigger than M = 10)
The largest denominator is 6/9.
EDIT: experimental results.
To my surprise, the algorithm works better than I thought.
I performed the following experiment, which ignores the estimate of m (equivalently, you can take m large enough).
In each round, I generate a pair (j, k) evenly distributed random numbers in inteval [0, 1) with j < k . If the difference k - j less than 1e-4 , I discard this pair, making this round ineffective. Otherwise, I calculate the true result trueN using a naive algorithm and calculate the heurN result heurN using my algorithm and add it to the statistics. This applies to the 1st round.
Here is the result:
effective round = 999789 sum of trueN = 14013312 sum of heurN = 13907575 correct percentage = 99.2262 % average quotient = 0.999415
correct percentage is the percentage of effective rounds, so trueN is heurN , and average quotient is the average of the private heurN / trueN for all effective rounds.
Thus, the method gives the correct answer in 99% + cases.
I also experimented with smaller m values and the results were similar.