Stern-Brocot 트리에서 분수의 위치 찾기


11

스턴 Brocot 트리 각 분획이 상기 레벨에 인접하는 두개의 분획의 분자와 분모를 가산함으로써 획득되는 분획의 이진 트리이다.

"끝점 분수"로 시작하여 시작하여 여기에서 다음 0/11/0같이 분수의 분자와 분모를 함께 추가하여 연속 된 각 분수 쌍 사이에 하나의 분수를 배치하여 반복됩니다.

0.  0/1                                                             1/0
1.  0/1                             1/1                             1/0
2.  0/1             1/2             1/1             2/1             1/0
3.  0/1     1/3     1/2     2/3     1/1     3/2     2/1     3/1     1/0
4.  0/1 1/4 1/3 2/5 1/2 3/5 2/3 3/4 1/1 4/3 3/2 5/3 2/1 5/2 3/1 4/1 1/0

선미 Brocot 트리 (의 각 반복에서 n회 반복)가있다 2^n + 1우리로부터 분획을 돌리는 수 있도록 시퀀스의 요소 0/2^n로는 2^n/2^n. 각각의 새로운 반복은 단순히 각각의 연속 분수 쌍 사이에 하나의 분수 "하프 웨이"를 삽입합니다.

이로 인해 Stern-Brocot 트리는 양의 유리수와 0과 1 사이의 이진 분수 사이의 일대일 매핑이되므로 두 세트의 카디널리티가 동일하다는 증거로도 사용됩니다.

당신의 임무는 가장 낮은 용어로 양의 유리수의 분자와 분모가 주어지면 Stern-Brocot 트리에서 그 분수의 위치에 해당하는 이진 분수를 결정하는 프로그램이나 함수를 작성하는 것입니다.

입력 및 출력의 예는 다음과 같습니다.

2/3 -> 3/8   (4th number in iteration 3)
4/7 -> 9/32  (between 1/2 and 3/5 in the chart above)
1/1 -> 1/2   (middle number in the first iteration)

지원할 필요는 없지만 참조 용으로 포함 된 입력 :

0/1 -> 0/1   (0/1 is considered the left number)
1/0 -> 1/1   (1/0 is considered the rightmost number)

이 목표를 달성하기 위해 어떤 언어로든 가장 짧은 프로그램이 이깁니다.


입 / 출력 요구 사항이 있습니까? 예를 들어, 참조 솔루션 에서처럼 기능이 충분합니까, 아니면 독립형 프로그램이어야합니까? 분수 출력 형식이 중요합니까?
대런 스톤

기능이면 충분합니다. 문제 설명에서 더 명확하게 설명하겠습니다.
Joe Z.

그것에 대해 생각하기에는 조금 늦었습니다. 아마 내일 그것을 명확히하려고 노력할 것입니다.
Joe Z.

2
좋아, 당신이 생각하는 bijection은 왼쪽에서 상수 분모 2 ^ (depth + 1)와 분자 1, 3, 5, 7, ...를 트리의 각 깊이에 할당하는 것입니다.
피터 테일러

1
그것을 구성하는 또 다른 방법은 1에서 폭 우선의 시작에서 트리의 노드 (즉, 첫 번째 숫자이다 1/1 => 1, 1/2 => 2, 2/1 => 3, 1/3 => 4, 등). 노드에 대해 이렇게 생성 된 숫자가 n인 경우 2^lg n(이진 로그)는에서 설정된 가장 높은 비트 n이며 원하는 이진 비율은 (2*(n - 2^lg n) + 1) / 2^(lg n + 1)입니다. get-highest-set-bit를 가진 명령어 세트에서 어셈블러 솔루션을 시도하는 사람은 아마도이 접근법을 사용하고 싶을 것입니다.
피터 테일러

답변:


1

GolfScript ( 49 48 46 자)

{0\@{}{@2*2$2$>!+@@{{\}3$)*}:j~1$-j}/\)\,?}:f;

또는

{0:x;\{}{.2$<!2x*+:x){\}*1$-{\}x)*}/x@)@,?}:g;

둘 다 스택에서 분자와 분모를 취하고 스택에 분자와 분모를 남겨 두는 함수입니다. 온라인 데모 .

핵심 아이디어는 Concrete Mathematics 섹션 4.5 (내 판에서는 p122)의 의사 코드로 표현됩니다 .

while m != n do
    if m < n then (output(L); n := n - m)
             else (output(R); m := m - n)

Ls 및 Rs의 문자열이 L = 0 및 R = 1 인 이진 값으로 해석되면 해당 값의 두 배에 1을 더한 값이 분자이고 분모가 1 비트 더 깁니다.

Golfscripters에게 관심의 대상으로, 이것은 내가 유용한 것으로 밝혀진 드문 경우 중 하나입니다. (OK, 루프 카운터로만 사용하지만 아무것도 아닌 것보다 낫습니다).


1

매쓰, 130 개 (114) 111 문자

f=#~g~0&;0~g~q_=q;p_~g~q_:=g[#,(Sign[p-#]+q)/2]&@FromContinuedFraction[ContinuedFraction@p/.{x___,n_}:>{x,n-1}]

예:

f[2/3]

3/8

f[4/7]

9/32

f[1]

1/2


1

루비, 132 , 125

@JoeZ의 레퍼런스 솔루션을 Rubied & golfed했습니다.

def t(n,d)u=k=0;v,j,f,g,b=[1,]*5;c=2
while(z=(f*d).<=>(g*n))!=0;z>0?(j,k=f,g):(u,v=f,g);b=b*2-z;f,g=u+j,v+k;c*=2;end
[b,c]end

사용 예 :

>> t(2,3)
=> [3, 8]
>> t(4,7)
=> [9, 32]
>> t(1,1)
=> [1, 2]

1

루비 (69 문자) CoffeeScript (59 문자)

이것은 분자와 분모를 인수로 취하고 궤사 후에 분자와 분모를 포함하는 배열을 반환하는 함수입니다.

g=(a,b,x=0,y=1)->c=a>=b;a&&g(a-b*c,b-a*!c,2*x+c,2*y)||[x,y]

온라인 데모

위의 GolfScript 솔루션과 동일한 접근 방식을 사용하지만 복싱 및 배열 해제에 대한 걱정없이 4 개의 변수를 사용할 수 있기 때문에 훨씬 더 읽기 쉽습니다. CoffeeScript는 변수 앞에 접두사 $(예 : PHP를 통해 20 문자 저장)가 없으며 기본 매개 변수 값을 허용하는 짧은 함수 정의 구문이 있으므로 ( f(a,b,x,y)함수 로 줄 바꿈 할 필요가 없음 g(a,b) = f(a,b,0,1)) 부울을 정수로 사용할 수 있기 때문에 유용한 값을 가진 표현. 모르는 사람들을 위해 CoffeeScript에는 표준 C 스타일 삼항 연산자 ( C?P:Q) 가 없지만 결코 거짓이 없으므로 C&&P||Q여기 를 대체 할 수 P있습니다.

논란의 여지가 있지만 우아하지만 논란의 여지가 적은 대안은 반복되는 빼기를 나누기와 모듈로 대체하는 것입니다.

f=(a,b,x=0,y=1,p=0)->a&&f(b%a,a,(x+p<<b/a)-p,y<<b/a,1-p)||[x+p,y]

(65 자; 온라인 데모 ). 이 방법으로 작성하면 Euclid 알고리즘과의 관계가 노출됩니다.


괄호가 필요하지 않으므로 a<b한 문자를 절약 할 수 있습니다. 인라인 c은 또 다른 두 가지를 제공합니다. f=->a,b,x=0,y=1{...}더 짧은 정의를위한 구문 을 고려할 수도 있습니다 .
Howard

@Howard, 사용중인 Ruby 버전을 모르지만 IDEOne에서 괄호를 제거하거나 해당 함수 구문을 사용하려고하면 구문 오류가 발생합니다.
피터 테일러

c=a<b ?이후에 추가 공간을 사용해보십시오 b. 그렇지 않으면 물음표는의 일부로 취급됩니다 b.
Howard

0

파이썬-531

마지막 참조 솔루션으로 사용하기위한 Python의 ungolfed 솔루션 :

def sbtree(n, d): 
    ufrac = [0, 1]
    lfrac = [1, 0]
    frac = [1, 1]
    bfrac = [1, 2]
    while(frac[0] * d != frac[1] * n): 
        if(frac[0] * d > frac[1] * n): 
            # push it towards lfrac
            lfrac[0] = frac[0]
            lfrac[1] = frac[1]
            bfrac[0] = bfrac[0] * 2 - 1 
        elif(frac[0] * d < frac[1] * n): 
            # push it towards ufrac
            ufrac[0] = frac[0]
            ufrac[1] = frac[1]
            bfrac[0] = bfrac[0] * 2 + 1 
        frac[0] = ufrac[0] + lfrac[0]
        frac[1] = ufrac[1] + lfrac[1]
        bfrac[1] *= 2
    return bfrac

두 분수 의 중간 값이 항상 두 분수의 값 사이에 있다는 사실을 이용하여 분수 사이의 이진 검색을 수행합니다 .


0

GolfScript, 54 자

'/'/n*~][2,.-1%]{[{.~3$~@+@@+[\]\}*].2$?0<}do.@?'/'@,(

태스크에 지정된 양식으로 STDIN에 입력해야합니다. 온라인으로 코드를 사용해보십시오 .

> 4/7
9/32

> 9/7
35/64

> 5/1
31/32

0

매스 매 티카 138

alephalpha의 절차만큼 간소화되지는 않았지만, 지금까지 내가 생산할 수 있었던 최고였습니다.

q_~r~k_:=Nest[#+Sign@k/(2Denominator@# )&,q,Abs@k]  
g@d_:=
Module[{l=ContinuedFraction@d,p=-1},
l[[-1]]-=1;
(p=-p;# p)&/@l]
h[q_]:=Fold[r,1/2,g@q]

테스팅

h[2/3]
h[4/7]
h[1]

3/8
9/32
1/2


0

자바 스크립트 186

f=(p1,q1,p2,q2)=>{if(p1*q2+1==p2*q1){return{p:p1+p2,q:q1+q2}}let p,q,pl=0,ql=1,ph=1,qh=0;for(;;){p=pl+ph;q=ql+qh;if(p*q1<=q*p1){pl=p;ql=q}else if(p2*q<=q2*p){ph=p;qh=q}else return{p,q}}}

더 적을 수 있지만 읽기 쉬운 골프를 좋아합니다


0

하스켈 , 125 바이트

n((a,b):(c,d):r)=(a,b):(a+c,b+d):n((c,d):r)
n a=a
z=zip[0..]
t x=[(j,2^i)|(i,r)<-z$iterate n[(0,1),(1,0)],(j,y)<-z r,x==y]!!0

온라인으로 사용해보십시오!

한 쌍의 형태로 입력 및 출력 (n,d).

간단한 설명 :

n각 분수 쌍을보고 첫 번째와 재귀 사이에 새로운 것을 삽입하여 이전 행에서 다음 행을 구성합니다 (두 번째 분수를 거기에 배치합니다). 기본 사례는 기본적으로 ID 함수이기 때문에 매우 간단합니다. 이 t함수는 두 개의 경계 분수만으로 초기 상태에 따라 해당 함수를 무기한 반복합니다. t그런 다음 각 행 ( i)과 행 ( )의 각 항목 을 인덱싱 j하고 찾고있는 것과 일치하는 첫 번째 분수를 찾습니다. 발견되면 j분자와 2^i분모로 나타납니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.