3

Say I'm given two real numbers as inputs: $x$ and $y$, with $x < y$. I want to find the smallest natural number n such that there's at least one integer between $nx$ and $ny$ (inclusive of $nx$ and $ny$).

The largest $n$ can possibly be is $\lceil{\frac{1}{y-x}}\rceil$ because at that point the gap between $nx$ and $ny$ is at least $1$.

In general we should expect $x$ and $y$ to be 'very close' so checking all the possibilities is undesirable.

How can I determine $n$ efficiently?

Roby5
  • 4,287
Dave
  • 316
  • So... what is your question? – Mastrem Jul 01 '16 at 10:52
  • Oh, okay. maybe you should state that a bit more clearly in your post. – Mastrem Jul 01 '16 at 10:54
  • 1
    I have edited your question to LaTeX and added the word "efficiently". You might want to check this is what you intend – Henry Jul 01 '16 at 10:59
  • @Henry Yes, thank you. Are there any other tags that you think are applicable? – Dave Jul 01 '16 at 11:00
  • I replaced "smallest integer" by "smallest natural number" because there is no smallest integer satisfying the condition. – Peter Jul 01 '16 at 11:12
  • Not sure, whether this works, but it is worth trying : Determine the continued fractions of $x$ and $y$ and look upto which position they coincide. Truncate the continued fraction at the first place the continued fractions differ. Take the denominator of the number corresponding to the truncated continued fraction. – Peter Jul 01 '16 at 11:25

2 Answers2

1

I think $n$ will be the smallest denominator $d$ of any rational number $\frac{m}{d}$ in the range $[x,y]$. We know $n \leq d$ because multiplying a fraction $\frac{m}{d} \in [x,y]$ by $d$ will give you an integer in the range $[dx, dy]$, namely $m$. We know $n \geq d$ because if $n$ were smaller than $d$ you could construct a fraction in the range $[x,y]$ with a smaller denominator than $d$ (the fraction's denominator would be $n$ and its numerator would be the integer in range $[nx, ny]$)

So you want to find the "simplest" (smallest denominator) rational number in the range $[x,y]$. This can be done in something like $O[\log(\frac{1}{y - x})]$ time by dividing an conquering with mediants.

The mediant of $\frac{a}{b}$ and $\frac{c}{d}$ is $\frac{a + b}{c + d}$. The mediant of $\frac{a}{b}$ and $\frac{c}{d}$ will lie between $\frac{a}{b}$ and $\frac{c}{d}$, and (if $ad - bc = 1$) the mediant will be the rational in that range with the smallest possible denominator. You can use this to binary search for lowest denominator fraction in the desired range

Here's some python code to search a Stern–Brocot tree to give the answer. It starts with the range $[\frac{0}{1}, \frac{1}{1}]$ and uses the simplest fraction within that range as a pivot to binary search until it finds a fraction within $[x, y]$.

from fractions import Fraction

def simplest_fraction(x, y):
    """Finds the rational number with smallest denominator in the given range.
    Range is inclusive"""
    if x > y:
        raise ValueError('{} > {}'.format(x, y))
    if int(x) != int(y):
        return 1
    assert int(x) == int(y)
    int_part = int(x)
    x -= int_part
    y -= int_part
    left_frac, right_frac = Fraction(0), Fraction(1)
    assert left_frac < x < y < right_frac
    while left_frac < x:
        determinant = (left_frac.denominator * right_frac.numerator -
                    (left_frac.numerator * right_frac.denominator))
        assert determinant == 1, ('Mediant not guaranteed '
                                'to have smallest denominator in the range. '
                                'left: {}, right: {}, determinant: {}').format(
                                left_frac, right_frac, determinant)
        mediant = Fraction(upper.right_frac + left_frac.numerator,
                    left_frac.denominator + right_frac.denominator)
        if x <= mediant <= y:
            return mediant + int_part
        elif y < mediant:
            right_frac = bisection
        elif mediant < x:
            left_frac = mediant

def smallest_n(x, y):
    return simplest_fraction.denominator
-1

You know that: $1\leq n\leq \big\lceil\dfrac{1}{x-y}\big\rceil$ You can create a pretty fast algorithm for finding the lowest $n$ by splitting possibilities in half repeatedly. Here's some Python code.

from math import ceil
from math import floor

def findN(x,y):
    lower = 1
    upper = ceil(1/(x-y))

    while upper - lower >= 1:
        if floor(x * floor((upper+lower)/2)) - floor(y * floor((upper+lower)/2))     >= 1:
           upper = ceil((upper+lower)/2)
        else:
           lower = ceil((upper+lower)/2)
    return upper

If the average of the lower and upper bounds is a possibillity for $n$. Then the average is the new upperbound and otherwise it's the new upperbound, until the difference between the two is no longer greater than 1. In the beginning, upper is a value for $n$ and upper only gets replaced with numbers that also work for $n$.

I just realised this won't always give you the lowest possible value of $n$, though often it does.

Mastrem
  • 8,331
  • 1
    This doesn't work. n can satisfy the property without n+1 also satisfying the property. – Dave Jul 01 '16 at 11:20
  • You're right. would it work if I returned both upper and lower? I think it will, because it has been tested for at least one of them – Mastrem Jul 01 '16 at 11:22
  • For binary search to work you need a way to test which side of the solution you're on. – Dave Jul 01 '16 at 11:34
  • No you don't because upper always works. One can easily prove this. At the start upper works. upper only gets replaced by numbers that also work (look at the if statement) – Mastrem Jul 01 '16 at 11:40
  • Oh, now I see. this won't always return the lowest value of $n$. I'll edit my post. If you don't want it to stay because it doesn't always return the lowest value of $n$, I'll delete it. – Mastrem Jul 01 '16 at 11:44