Знайдіть позицію дробу на дереві Штерн-Брокот


11

Дерево Штерна-Броко є бінарним деревом фракцій , де кожна фракція набувається шляхом додавання чисельнику і знаменника двох фракцій сусідніх його в зазначених вище рівнях.

Він генерується, починаючи з , 0/1і в 1/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

У кожній ітерації дерева Стерн-Броко (The nй ітерації), є 2^n + 1елементи в послідовності, до якої можна приписати фракції від 0/2^nдо 2^n/2^n. Кожна нова ітерація просто вставляє один дріб "на половину" між кожною парою послідовних дробів.

Це робить дерево Стерна-Брокота однозначним відображенням між позитивними раціональними числами та двійковими дробами між 0 і 1, тим самим слугуючи доказом того, що два набори мають однакову кардинальність.

Ваше завдання - написати програму або функцію, яка, враховуючи чисельник і знаменник додатного раціонального числа в найнижчих значеннях, визначає двійковий дріб, який відповідає позиції цього дробу на дереві Штерна-Брокота.

Приклади входів і виходів наведені нижче:

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)

Виграє найкоротша програма будь-якої мови для досягнення цієї мети.


Чи є вимоги до вводу / виводу? Наприклад, чи достатня лише функція, як у вашому довідковому рішенні, чи вона повинна бути окремою програмою? Чи має значення вихідний формат дробу?
Даррен Стоун

Функції достатньо. Я зрозумію це в описі проблеми.
Джо З.

Мені трохи пізно про це думати; Я, мабуть, спробую уточнити це завтра.
Джо З.

2
Гаразд, я думаю, що бісекція, яку ви маєте на увазі, - призначити кожній глибині дерева постійний знаменник 2 ^ (глибина + 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-high-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;

Обидві - це функції, які беруть чисельник і знаменник на стеку і залишають чисельник і знаменник на стеку. Інтернет демо .

Основна ідея виражена в псевдокоді в розділі 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

Математика, 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.

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

Ruby (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, оскільки він не має префіксальних змінних $(20 символів збережено, наприклад, PHP), має синтаксис короткого визначення функції, який дозволяє значення параметрів за замовчуванням (тому немає необхідності 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 символів; демонстрація в Інтернеті ). Написавши це таким чином, викривається взаємозв'язок з алгоритмом Евкліда.


Вам не потрібні круглі дужки, навколо a<bяких економиться одна картка. Вкладиш cдає ще два. Ви також можете розглянути синтаксис f=->a,b,x=0,y=1{...}для ще коротшого визначення.
Говард

@Howard, я не знаю, яку версію Ruby ви використовуєте, але на IDEOne я отримую помилку синтаксису, якщо спробую видалити ці круглі дужки або скористатися цим синтаксисом функції.
Пітер Тейлор

Спробуйте c=a<b ?з додатковим місцем після b. В іншому випадку знак запитання розглядається як частина b.
Говард

0

Пітон - 531

Недозволене рішення в Python, яке служить опорним рішенням останнього місця:

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

Не настільки обтічна, як процедура алфальфи, але це було найкраще, що я міг зробити досі.

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

JavaScript 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

Haskell , 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будує наступний рядок із попереднього, переглядаючи кожну пару дробів і вставляючи новий між першим і рекурсією (що поставить другий дріб прямо туди). Базовий випадок дуже простий, оскільки це в основному лише функція ідентичності. tФункція перебирає цю функцію на невизначений час, засновану від початкового стану з допомогою всього двох граничних фракцій. tпотім індексує кожен рядок ( i) та кожен елемент у рядку ( j) та шукає перший дріб, який відповідає тому, що ми шукаємо. Коли він знаходить, він виходить jяк чисельник і 2^iяк знаменник.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.