Output the nth rational number according to the Stern-Brocot sequence

30

1

The Stern-Brocot sequence is a Fibonnaci-like sequence which can be constructed as follows:

  1. Initialise the sequence with s(1) = s(2) = 1
  2. Set counter n = 1
  3. Append s(n) + s(n+1) to the sequence
  4. Append s(n+1) to the sequence
  5. Increment n, return to step 3

This is equivalent to:

s(n) = \begin{cases} 1 & \textrm{if } n = 1 \ s(\frac n 2) & \textrm{if } n \textrm{ is even} \ s(\frac{n-1}2) + s(\frac{n+1}2) & \textrm{otherwise} \end{cases}

Amongst other properties, the Stern-Brocot sequence can be used to generate every possible positive rational number. Every rational number will be generated exactly once, and it will always appear in its simplest terms; for example, 1/3 is the 4th rational number in the sequence, but the equivalent numbers 2/6, 3/9 etc won't appear at all.

We can define the nth rational number as r(n) = s(n) / s(n+1), where s(n) is the nth Stern-Brocot number, as described above.

Your challenge is to write a program or function which will output the nth rational number generated using the Stern-Brocot sequence.

  • The algorithms described above are 1-indexed; if your entry is 0-indexed, please state in your answer
  • The algorithms described are for illustrative purposes only, the output can be derived in any way you like (other than hard-coding)
  • Input can be via STDIN, function parameters, or any other reasonable input mechanism
  • Ouptut can be to STDOUT, console, function return value, or any other reasonable output stream
  • Output must be as a string in the form a/b, where a and b are the relevant entries in the Stern-Brocot sequence. Evaluating the fraction before output is not permissable. For example, for input 12, output should be 2/5, not 0.4.
  • Standard loopholes are disallowed

This is , so shortest answer in bytes will win.

Test cases

The test cases here are 1-indexed.

n    r(n)
--  ------
1    1/1
2    1/2
3    2/1
4    1/3
5    3/2
6    2/3
7    3/1
8    1/4
9    4/3
10   3/5
11   5/2
12   2/5
13   5/3
14   3/4
15   4/1
16   1/5
17   5/4
18   4/7
19   7/3
20   3/8
50   7/12
100  7/19
1000 11/39

OEIS entry: A002487
Excellent Numberphile video discussing the sequence: Infinite Fractions

Sok

Posted 2016-08-18T12:32:39.830

Reputation: 5 592

Can the output use Trues instead of 1s? – Loovjo – 2016-08-18T13:54:19.960

1@Loovjo No, True/2 isn't a valid fraction (as far as I'm concerned). As an aside, True isn't always 1 - some languages use -1 instead to avoid potential mistakes when applying bitwise operators. [citation needed] – Sok – 2016-08-18T14:01:48.257

related – flawr – 2016-08-18T15:40:45.523

2

@Sok citation

– mbomb007 – 2016-08-18T17:13:13.373

1@Sok but in Python, True is equivalent to 1 and True/2 would be 1/2. – Leaky Nun – 2016-08-20T08:06:06.263

Are equivalent representations (like -2/-1 == 2/1 or 4/2 == 2/1) acceptable? – Mego – 2016-08-20T08:46:12.743

@LeakyNun That may be the case, but True is not the same in every language. This would open the door to different representations coming from different languages. In my opinion, the output from every language should be consistent, so I'm not allowing True in the place of 1 – Sok – 2016-08-20T09:33:38.303

@Mego No, as the fractions should be generated using some variation of the Stern-Brocot sequence. Negative numbers don't feature in that sequence, and as noted in the question, every rational number generated will be in its simplest terms. – Sok – 2016-08-20T09:35:17.740

Answers

3

Jelly, 14 bytes

3ẋḶŒpḄċ
’Ç”/⁸Ç

Try it online!

Ooh, looks like I can beat the accepted answer by @Dennis, and in the same language at that. This works using a formula from OEIS: the number of ways to express (the number minus 1) in hyperbinary (i.e. binary with 0, 1, 2 as digits). Unlike most Jelly programs (which work either as a full program or a function), this one works only as a full program (because it sends part of the output to stdout, and returns the rest; when used as a full program the return value is sent to stdout implicitly, so all the output is in the same place, but this wouldn't work for a function submission).

This version of the program is very inefficient. You can create a much faster program that works for all input up to 2ⁿ via placing n just after the on the first line; the program has O(n × 3ⁿ) performance, so keeping n small is fairly important here. The program as written sets n equal to the input, which is clearly large enough, but also clearly too large in almost all cases (but hey, it saves bytes).

Explanation

As usual in my Jelly explanations, text in braces (e.g. {the input}) shows something that automatically filled in by Jelly due to missing operands in the original program.

Helper function (calculates the nth denominator, i.e. the n+1th numerator):

3ẋḶŒpḄċ
3ẋ       3, repeated a number of times equal to {the argument}
  Ḷ      Map 3 to [0, 1, 2]
   Œp    Take the cartesian product of that list
     Ḅ   Convert binary (or in this case hyperbinary) to a number
      ċ  Count number of occurrences of {the argument} in the resulting list

The first five bytes are basically just generating all possible hyperbinary numbers up to a given length, e.g. with input 3, the output of is [[0,1,2],[0,1,2],[0,1,2]] so the cartesian product is [[0,0,0],[0,0,1],[0,0,2],[0,1,0],…,[2,2,1],[2,2,2]]. basically just multiplies the last entry by 1, the penultimate entry by 2, the antepenultimate entry by 4, etc., and then adds; although this is normally used to convert binary to decimal, it can handle the digit 2 just fine, and thus works for hyperbinary too. Then we count the number of times the input appears in the resulting list, in order to get an appropriate entry in the sequence. (Luckily, the numerator and denominator follow the same sequence).

Main program (asks for the numerator and denominator, and formats the output):

’Ç”/⁸Ç
’Ç      Helper function (Ç), run on {the input} minus 1 (‘)
  ”/    {Output that to stdout}; start again with the constant '/'
    ⁸Ç  {Output that to stdout}; then run the helper function (Ç) on the input (⁸)

Somehow I keep writing programs that take almost as many bytes to handle I/O as they do to solve the actual problem…

user62131

Posted 2016-08-18T12:32:39.830

Reputation:

Good grief, you weren't kidding about it being inefficient - on TIO 12 takes 20s to complete, and 13 times out completely! Accepted, even though I can't verify all the test cases. – Sok – 2017-02-16T09:00:02.260

11

Haskell, 78 77 65 58 bytes

Shamelessly stealing the optimized approach gives us:

(s#t)0=show s++'/':show t
(s#t)n=t#(s+t-2*mod s t)$n-1
1#1

Thanks to @nimi for golfing a few bytes using an infix function!

(Still) uses 0-based indexing.


The old approach:

s=(!!)(1:1:f 0)
f n=s n+s(n+1):s(n+1):(f$n+1)
r n=show(s n)++'/':(show.s$n+1)

Damn the output format... And indexing operators. EDIT: And precedence.

Fun fact: if heterogenous lists were a thing, the last line could be:

r n=show>>=[s!!n,'/',s!!(n+1)]

ThreeFx

Posted 2016-08-18T12:32:39.830

Reputation: 1 435

Using a guard to bind s!!n should be one byte shorter: f n|x<-s!!n=x:x+x+1:f$n+1 – Laikoni – 2016-08-18T12:53:27.067

@Laikoni s!!n+1 is not (s!!n)+1 but s!!(n+1) which is why I can't do that :/ – ThreeFx – 2016-08-18T12:55:39.890

Indeed, that should have been obvious. Its just ... so many s!!n in there! – Laikoni – 2016-08-18T13:00:51.997

Sadly, even using g=s!! isn't shorter... – ThreeFx – 2016-08-18T13:02:57.013

1You can use ++'/':(show.s$n+1) in r to save a byte. – nimi – 2016-08-19T04:56:37.327

1Switch to an an infix function: (s#t)0=show..., (s#t)n=t#(s+t-2*mod s t)$n-1, r=1#1. You can even omit r, i.e. the last line is just 1#1. – nimi – 2016-08-19T14:09:42.437

@nimi Thanks, I wasn't aware I can omit the r= at the bottom. – ThreeFx – 2016-08-20T12:48:50.783

11

CJam (20 bytes)

1_{_@_2$%2*-+}ri*'/\

Online demo. Note that this is 0-indexed. To make it 1-indexed, replace the initial 1_ by T1.

Dissection

This uses the characterisation due to Moshe Newman that

the fraction a(n+1)/a(n+2) can be generated from the previous fraction a(n)/a(n+1) = x by 1/(2*floor(x) + 1 - x)

If x = s/t then we get

  1 / (2 * floor(s/t) + 1 - s/t)
= t / (2 * t * floor(s/t) + t - s)
= t / (2 * (s - s%t) + t - s)
= t / (s + t - 2 * (s % t))

Now, if we assume that s and t are coprime then

  gcd(t, s + t - 2 * (s % t))
= gcd(t, s - 2 * (s % t))
= gcd(t, -(s % t))
= 1

So a(n+2) = a(n) + a(n+1) - 2 * (a(n) % a(n+1)) works perfectly.

1_           e# s=1, t=1
{            e# Loop...
  _@_2$%2*-+ e#   s, t = t, s + t - 2 * (s % t)
}
ri*          e# ...n times
'/\          e# Separate s and t with a /

Peter Taylor

Posted 2016-08-18T12:32:39.830

Reputation: 41 901

Love the methodology here, awesome answer! – Sok – 2016-08-19T08:00:57.110

If you scroll further down the OEIS entry you'll find that Mike Stay already submitted that formula. – Neil – 2016-08-21T11:04:58.707

6

Jelly, 16 bytes

L‘Hị⁸Sṭ
1Ç¡ṫ-j”/

Try it online! or verify all test cases.

How it works

1Ç¡ṫ-j”/  Main link. Argument: n

1         Set the return value to 1.
 Ç¡       Apply the helper link n times.
   ṫ-     Tail -1; extract the last two items.
     j”/  Join, separating by a slash.


L‘Hị⁸Sṭ   Helper link. Argument: A (array)

L         Get the length of A.
 ‘        Add 1 to compute the next index.
  H       Halve.
   ị⁸     Retrieve the item(s) of A at those indices.
          If the index in non-integer, ị floors and ceils the index, then retrieves
          the items at both indices.
    S     Compute the sum of the retrieved item(s).
     ṭ    Tack; append the result to A.

Dennis

Posted 2016-08-18T12:32:39.830

Reputation: 196 637

5

05AB1E, 34 33 25 23 bytes

XX‚¹GÂ2£DO¸s¦ìì¨}R2£'/ý

Explanation

XX‚                        # push [1,1]
   ¹G           }          # input-1 times do
     Â                     # bifurcate
      2£                   # take first 2 elements of reversed list
        DO¸                # duplicate and sum 1st copy, s(n)+s(n+1)
           s¦              # cut away the first element of 2nd copy, s(n)
             ìì            # prepend both to list
               ¨           # remove last item in list
                 R2£       # reverse and take the first 2 elements
                    '/ý    # format output
                           # implicitly print

Try it online

Saved 2 bytes thanks to Adnan.

Emigna

Posted 2016-08-18T12:32:39.830

Reputation: 50 798

Does this also work?: XX‚¹GÂ2£DO¸s¦ìì¨}R2£'/ý. – Adnan – 2016-08-18T14:47:50.017

@Adnan Indeed. I forgot that ý can format a list. Nice. – Emigna – 2016-08-18T14:51:40.833

4

Python 2, 85 81 bytes

x,s=input(),[1,1]
for i in range(x):s+=s[i]+s[i+1],s[i+1]
print`s[x-1]`+"/"+`s[x]`

This submission is 1-indexed.

Using a recursive function, 85 bytes:

s=lambda x:int(x<1)or x%2 and s(x/2)or s(-~x/2)+s(~-x/2)
lambda x:`s(x)`+"/"+`s(x+1)`

If an output like True/2 is acceptable, here's one at 81 bytes:

s=lambda x:x<1 or x%2 and s(x/2)or s(-~x/2)+s(~-x/2)
lambda x:`s(x)`+"/"+`s(x+1)`

Loovjo

Posted 2016-08-18T12:32:39.830

Reputation: 7 357

4

MATL, 20 bytes

FT+"@:qtPXnosV47]xhh

This uses the characterization in terms of binomial coefficients given in the OEIS page.

The algorithm works in theory for all numbers, but in practice it is limited by MATL's numerical precision, and so it doesn't work for large entries. The result is accurate for inputs up to 20 at least.

Try it online!

Explanation

FT+      % Implicitly take input n. Add [0 1] element-wise. Gives [n n+1]
"        % For each k in [n n+1]
  @:q    %   Push range [0 1 ... k-1]
  tP     %   Duplicate and flip: push [k-1 ... 1 0]
  Xn     %   Binomial coefficient, element-wise. Gives an array
  os     %   Number of odd entries in that array
  V      %   Convert from number to string
  47     %   Push 47, which is ASCII for '\'
]        % End for each
x        % Remove second 47
hh       % Concatenate horizontally twice. Automatically transforms 47 into '\'
         % Implicitly display

Luis Mendo

Posted 2016-08-18T12:32:39.830

Reputation: 87 464

3

C (GCC), 79 bytes

Uses 0-based indexing.

s(n){return!n?:n%2?s(n/2):s(-~n/2)+s(~-n/2);}r(n){printf("%d/%d",s(n),s(n+1));}

Ideone

betseg

Posted 2016-08-18T12:32:39.830

Reputation: 8 493

1x?:y is a gcc extension. – rici – 2016-08-20T06:00:32.710

3

JavaScript (ES6), 43 bytes

f=(i,n=0,d=1)=>i?f(i-1,d,n+d-n%d*2):n+'/'+d

1-indexed; change to n=1 for 0-indexed. The linked OEIS page has a useful recurrence relation for each term in terms of the previous two terms; I merely reinterpreted it as a recurrence for each fraction in terms of the previous fraction. Unfortunately we don't have inline TeX so you'll just have to paste it onto another site to find out how this formats:

$$ \frac{a}{b} \Rightarrow \frac{b}{a + b - 2(a \bmod b)} $$

Neil

Posted 2016-08-18T12:32:39.830

Reputation: 95 035

3

Python 2, 66 bytes

f=lambda n:1/n or f(n/2)+n%2*f(-~n/2)
lambda n:`f(n)`+'/'+`f(n+1)`

Uses the recursive formula.

xnor

Posted 2016-08-18T12:32:39.830

Reputation: 115 687

3

Actually, 18 bytes

11,`│;a%τ@-+`nk'/j

Try it online!

This solution uses Peter's formula and is likewise 0-indexed. Thanks to Leaky Nun for a byte.

Explanation:

11,`│;a%τ@-+`nk'/j
11                  push 1, 1
  ,`│;a%τ@-+`n      do the following n times (where n is the input):
                      stack: [t s]
    │                 duplicate the entire stack ([t s t s])
     ;                dupe t ([t t s t s])
      a               invert the stack ([s t s t t])
       %              s % t ([s%t s t t])
        τ             multiply by 2 ([2*(s%t) s t t])
         @-           subtract from s ([s-2*(s%t) s t])
           +          add to t ([t+s-2*(s%t) t])
                      in effect, this is s,t = t,s+t-2*(s%t)
              k'/j  push as a list, join with "/"

Mego

Posted 2016-08-18T12:32:39.830

Reputation: 32 998

A controversial byte off – Leaky Nun – 2016-08-20T08:44:49.697

@LeakyNun I'll hold off on that improvement until there's clarification from the OP. – Mego – 2016-08-20T08:45:48.787

2

MATL, 32 30 bytes

1i:"tt@TF-)sw@)v]tGq)V47bG)Vhh

This uses a direct approach: generates enough members of the sequence, picks the two desired ones and formats the output.

Try it online!

Luis Mendo

Posted 2016-08-18T12:32:39.830

Reputation: 87 464

2

R, 93 bytes

f=function(n)ifelse(n<3,1,ifelse(n%%2,f(n/2-1/2)+f(n/2+1/2),f(n/2)))
g=function(n)f(n)/f(n+1)

Literally the simplest implementation. Working on golfing it a bit.

user5957401

Posted 2016-08-18T12:32:39.830

Reputation: 699

2

m4, 131 bytes

define(s,`ifelse($1,1,1,eval($1%2),0,`s(eval($1/2))',`eval(s(eval(($1-1)/2))+s(eval(($1+1)/2)))')')define(r,`s($1)/s(eval($1+1))')

Defines a macro r such that r(n) evaluates according to the spec. Not really golfed at all, I just coded the formula.

Program man

Posted 2016-08-18T12:32:39.830

Reputation: 531

2

Ruby, 49 bytes

This is 0-indexed and uses Peter Taylor's formula. Golfing suggestions welcome.

->n{s=t=1;n.times{s,t=t,s+t-2*(s%t)};"#{s}/#{t}"}

Sherlock9

Posted 2016-08-18T12:32:39.830

Reputation: 11 664

1

Mathematica, 108 106 101 bytes

(s={1,1};n=1;a=AppendTo;t=ToString;Do[a[s,s[[n]]+s[[++n]]];a[s,s[[n]]],#];t@s[[n-1]]<>"/"<>t@s[[n]])&

numbermaniac

Posted 2016-08-18T12:32:39.830

Reputation: 639

1

R, 84 bytes

function(n,K=c(1,1)){for(i in 1:n)K=c(K,K[i]+K[i+1],K[i+1])
paste0(K[i],"/",K[i+1])}

Try it online!

The older R implementation doesn't follow the specs, returning a floating point rather than a string, so here's one that does.

Giuseppe

Posted 2016-08-18T12:32:39.830

Reputation: 21 077

1

><>, 34+2 = 36 bytes

After seeing Peter Taylor's excellent answer, I re-wrote my test answer (which was an embarrassing 82 bytes, using very clumsy recursion).

&01\$n"/"on;
&?!\:@}:{:@+@%2*-&1-:

It expects the input to be present on the stack, so +2 bytes for the -v flag. Try it online!

Sok

Posted 2016-08-18T12:32:39.830

Reputation: 5 592

1

Octave, 90 bytes

function f(i)S=[1 1];for(j=1:i/2)S=[S S(j)+S(j+1) (j+1)];end;printf("%d/%d",S(i),S(i+1));end

dcsohl

Posted 2016-08-18T12:32:39.830

Reputation: 641

1

C#, 91 90 bytes

n=>{Func<int,int>s=_=>_;s=m=>1==m?m:s(m/2)+(0==m%2?0:s(m/2+1));return$"{s(n)}/{s(n+1)}";};

Casts to Func<int, string>. This is the recursive implementation.

Ungolfed:

n => 
{
    Func<int,int> s = _ => _; // s needs to be set to use recursively. _=>_ is the same byte count as null and looks cooler.
    s = m =>
        1 == m ? m               // s(1) = 1
        : s(m/2) + (0 == m%2 ? 0 // s(m) = s(m/2) for even
        : s(m/2+1));             // s(m) = s(m/2) + s(m/2+1) for odd
    return $"{s(n)}/{s(n+1)}";
};

Edit: -1 byte. Turns out C# doesn't need a space between return and $ for interpolated strings.

milk

Posted 2016-08-18T12:32:39.830

Reputation: 3 043

1

Python 2, 59 bytes

s,t=1,1;exec"s,t=t,s+t-2*(s%t);"*input();print'%d/%d'%(s,t)

Uses Peter's formula and is similarly 0-indexed.

Try it online

Mego

Posted 2016-08-18T12:32:39.830

Reputation: 32 998

1

J, 29 bytes

([,'/',])&":&([:+/2|]!&i.-)>:

Usage

Large values of n require a suffix of x which denotes the use of extended integers.

   f =: ([,'/',])&":&([:+/2|]!&i.-)>:
   f 1
1/1
   f 10
3/5
   f 50
7/12
   f 100x
7/19
   f 1000x
11/39

miles

Posted 2016-08-18T12:32:39.830

Reputation: 15 654

100 counts as a "large value"? – dcsohl – 2016-08-22T14:11:07.633

1@dcsohl In this method, the binomial coefficients are computed, and for n = 100, the largest one computed is C(72, 28) = 75553695443676829680 > 2^64 and will require extended integers to avoid floating point values. – miles – 2016-08-22T15:16:56.090