Approximate the perfect fifth

10

Starting at 1-TET, give equal temperaments that have better and better approximation of the perfect fifth(just ratio 3/2). (OEIS sequence A060528)

The formal description of the sequence, copied from the OEIS:

A list of equal temperaments (equal divisions of the octave) whose nearest scale steps are closer and closer approximations to the ratios of two tones of musical harmony: the perfect 4th, 4/3 and its complement the perfect 5th, 3/2.

Note that by symmetry, the perfect fourth doesn't matter.

Let's say we know that 3 is in the sequence. The frequencies in 3-TET are:

2^0, 2^⅓, 2^⅔

Where 2^⅔ is the closest logarithmic approximation of 3/2.

Is 4 in the sequence? The frequencies in 4-TET are:

2^0, 2^¼, 2^½, 2^¾

Where 2^½ is the closest approximation of 3/2. This is not better than 2^⅔, so 4 is not in the sequence.

By similar method, we confirm that 5 is in the sequence, and so on.

When given an integer n as input, the output must be the first N numbers of the sequence in order. For example, when n = 7, the output should be:

1 2 3 5 7 12 29

Sequence description by xnor

The irrational constant \$ \log_2(3) \approx 1.5849625007211563\dots\$ can be approximated by a sequence of rational fractions

$$ \frac{2}{1}, \frac{3}{2}, \frac{5}{3}, \frac{8}{5}, \frac{11}{7}, \frac{19}{12}, \frac{46}{29}, \dots$$

A fraction is included in the sequence if it's the new closest one by absolute distance \$ \left| \frac{p}{q} - \log_2(3)\ \right|\$, that is, closer than any other fraction with a smaller or equal denominator.

Your goal is to output the first \$n\$ denominators in order. These are sequence A060528 (table). The numerators (not required) are given by A254351 (table)

Rules:

  1. Do not import the sequence A060528 directly.
  2. The format doesn't matter as long as the numbers are distinguishable. In the example above, the output can also be:

    [1,2,3,5,7,12,29]

  3. As this is a code-golf, the shortest code in bytes wins.

Dannyu NDos

Posted 2019-09-24T21:49:09.453

Reputation: 2 087

5Hi and welcome to Code Golf SE! We require that all challenges be self-contained, so a description here of the sequence would be a great help. – AdmBorkBork – 2019-09-24T21:54:39.210

5I'm confused by the description from OEIS. It mentions perfect 4th as well (ratio 4/3), but the challenge is about perfect 5ths (ratio 3/2). I think it also needs explanation that the sequence values are the denominators of the rational approximations. – xnor – 2019-09-24T21:58:47.673

I improved the question. Now vote for reopen, please? – Dannyu NDos – 2019-09-24T22:37:59.853

5I like the challenge, but I find the stuff added to the description still confusing, not knowing much about music. For instance, I don't know what 1-TET or 4-TET are, and nothing seems to show up on Google. I'll try writing an explanation of how I'd describe this sequence. – xnor – 2019-09-24T23:06:31.543

@xnor For example, 12-TET means 12-tone equal temperament. – Dannyu NDos – 2019-09-24T23:16:37.007

1As a small technical point, I'd suggest setting some upper bound on what input n can be given. Due to float precision issues, coded solutions might eventually become inaccurate for large n, making it hard to judge which answers are valid. I think something like n=40, the max of the OEIS table, would be reasonable. – xnor – 2019-09-25T00:02:31.507

3@DannyuNDos Ah yes, the 12-tone equal temperament. That's my favourite instrument – Jo King – 2019-09-25T01:53:50.240

Temparement is a way to define the notes that an instrument can produce; it's not an instrument – Luis Mendo – 2019-09-25T09:43:43.710

2@DannyuNDos Thanks. So the comparison is between 1/2 and log2(1.5), not between 2^(1/2) and 1.5. That should be made clearer in the text – Luis Mendo – 2019-09-25T09:48:13.633

Answers

5

05AB1E, 19 18 bytes

µ¯ßNLN/3.²<αßDˆ›D–

Try it online!

µ                      # repeat until counter == input
 ¯                     #  push the global array
  ß                    #  get the minimum (let's call it M)
   N                   #  1-based iteration count
    L                  #  range 1..N
     N/                #  divide each by N
       3.²             #  log2(3)
          <            #  -1
           α           #  absolute difference with each element of the range
            ß          #  get the minimum
             Dˆ        #  add a copy to the global array
               ›       #  is M strictly greater than this new minimum?
                D–     #  if true, print N
                       #  implicit: if true, add 1 to the counter

Grimmy

Posted 2019-09-24T21:49:09.453

Reputation: 12 521

1Nice answer, but all I'm wondering right now is why the while-loop has 1-based indices.. :S – Kevin Cruijssen – 2019-09-25T12:48:51.063

4

Wolfram Language (Mathematica), 62 60 bytes

Denominator@NestList[Rationalize[r=Log2@3,Abs[#-r]]&,2,#-1]&

Try it online!

attinat

Posted 2019-09-24T21:49:09.453

Reputation: 3 495

How many precision? – Dannyu NDos – 2019-09-25T01:38:15.583

@DannyuNDos This function uses exact values, so computations can be done to arbitrary precision. – attinat – 2019-09-25T01:39:54.107

You win the challenge. – Dannyu NDos – 2019-09-25T01:48:51.773

5@DannyuNDos why accept an answer this quickly? It's also arguably better not to accept an answer at all.. – attinat – 2019-09-25T02:03:24.630

Regarding the floating-point errors other languages are suffering, I'd like to present an alternative method of alloting score. So hold on. – Dannyu NDos – 2019-09-25T02:10:15.990

4

JavaScript (V8), 81 80 78 bytes

-2 bytes thanks Arnauld!

n=>{for(d=g=1;w=Math.log2(3),w+=~(w*g-.5)/g,n--;g++)w*w<d?d=print(g)||w*w:n++}

Try it online!

Shieru Asakoto

Posted 2019-09-24T21:49:09.453

Reputation: 4 445

2

Python 2, 92 bytes

E=k=input()
n=0
while k:
 n+=1;e=abs((3.169925001442312*n-1)%2-1)/n
 if e<E:print n;E=e;k-=1

Try it online!

Uses the constant 3.169925001442312 for \$2 \log_2(3)\$. I wasn't sure how many digits of accuracy are required, since the inaccuracy will break the sequence eventually, so I used the full float precision of 2 * numpy.log2(3).

xnor

Posted 2019-09-24T21:49:09.453

Reputation: 115 687

1

This gives two extra terms after 665: ..., 665, (1995), (4655), 8286, ... Try it online!

– Οurous – 2019-09-25T01:31:50.017

@Οurous Yeah, that's pretty much inevitable sooner or later for any language without infinite precision, though I'm surprised it popped up so early with 32-bit floats as Python uses. I'll wait for the challenge writer to clarify on how far answers need to work. – xnor – 2019-09-25T07:03:39.783

wouldn't it be fewer characters to use 2 * numpy.log2(3) rather than the fully spelled out number? (Or even better, numpy.log2(9)) – JDL – 2019-09-25T13:28:05.007

@JDL that would require this code: from numpy import* and log2(9). – Jonathan Allan – 2019-09-25T13:34:27.340

ah, that is what I get for assuming python works like R and you can write package::function without loading package first! – JDL – 2019-09-25T13:35:30.963

2

Clean, 128 111 108 bytes

import StdEnv
c=ln 3.0/ln 2.0
?d=abs(toReal(toInt(c*d))/d-c)
$i=take i(iterate(\d=until((>)(?d)o?)inc d)1.0)

Try it online!

Should work up to the limits of Real's 64-bit double precision type.

Οurous

Posted 2019-09-24T21:49:09.453

Reputation: 7 916

2

Perl 5 (-MPOSIX=log2 -M5.01 -n), 73, 78, 71 bytes

Fixed following comment, may be improved...

-7 bytes thanks to Grimy

$o=abs$d-(0|.5+($d=log2 3)*++$;)/$;;$@=$o,$_-=say$;if!$@|$o<$@;$_&&redo

Try it online!

Nahuel Fouilleul

Posted 2019-09-24T21:49:09.453

Reputation: 5 582

here's 71 – Grimmy – 2019-09-27T15:36:13.307

2

MATL, 27 25 bytes

1`@:@/Q3Zl-|X<hY<tdzG-}df

Try it online!

Explanation

1       % Push 1. This initiallizes the vector of distances
  `     % Do...while
  @:    %   Range [1, 2, ..., k], where k is the iteration index, staring at 1
  @/    %   Divide by k, element-wise. Gives [1/k, 2/k, ..., 1]
  Q     %   Add 1, element-wise. Gives [(k+1/k, (k+2)/k, ..., 2]
  3Zl   %   Push log2(3)
  -|    %   Absolute difference, element-wise
  X<    %   Minimum
  h     %   Concatenate with vector of previous distances
  Y<    %   Cumulative minimum
  t     %   Duplicate
  dz    %   Consecutive differences, number of nonzeros. This tells how many
        %   times the cumulative minimum has decreased
  G-    %   Subtract input n. This is the loop condition. 0 means we are done
}       % Finally (execute on loop exit)
  d     %   Consecutive differences (of the vector of cumulative differences)
  f     %   Indices of nonzeros. This is the final result
        % End. A new iteration is executed if the top of the stack is nonzero
        % Implicit display

Luis Mendo

Posted 2019-09-24T21:49:09.453

Reputation: 87 464