ค้นหาตำแหน่งของเศษส่วนในต้นไม้ Stern-Brocot


11

ต้นไม้สเติร์น-Brocotเป็นต้นไม้ไบนารีของเศษส่วนที่แต่ละส่วนจะได้รับโดยการเพิ่ม numerators และ denominators ของทั้งสองเศษส่วนเพื่อนบ้านในระดับดังกล่าวข้างต้น

มันถูกสร้างขึ้นโดยเริ่มต้นด้วย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

ในการทำซ้ำของต้นไม้สเติร์น-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)

โปรแกรมที่สั้นที่สุดในภาษาใด ๆ เพื่อให้บรรลุเป้าหมายนี้ชนะ


มีข้อกำหนดด้านอินพุต / เอาต์พุตหรือไม่? เช่นเป็นเพียงฟังก์ชั่นเพียงพอในการแก้ปัญหาอ้างอิงของคุณหรือมันจะต้องเป็นโปรแกรมแบบสแตนด์อะโลน? รูปแบบเอาต์พุตเศษส่วนมีความสำคัญหรือไม่
Darren Stone

ฟังก์ชั่นเพียงพอ ฉันจะทำให้ชัดเจนขึ้นในคำอธิบายปัญหา
Joe Z.

มันช้าไปหน่อยที่ฉันจะคิดเกี่ยวกับมัน ฉันอาจจะพยายามชี้แจงในวันพรุ่งนี้
Joe Z.

2
ตกลงฉันคิดว่าสิ่งที่คุณมีอยู่ในใจคือการมอบหมายให้แต่ละส่วนลึกของต้นไม้เป็นตัวหารคงที่ 2 ^ (ความลึก + 1) และตัวเศษ 1, 3, 5, 7, ... จากทางซ้าย
Peter Taylor

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)(ใครก็ตามที่พยายามใช้โซลูชันแอสเซมเบลอร์ในชุดคำสั่งที่มีการตั้งค่าบิตสูงสุดอาจต้องการใช้วิธีนี้)
Peter Taylor

คำตอบ:


1

GolfScript ( 49 48 46 ตัวอักษร)

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

หรือ

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

ทั้งสองเป็นฟังก์ชั่นที่ใช้ตัวเศษและตัวส่วนบนสแต็กและปล่อยให้ตัวเศษและตัวส่วนบนสแต็ก สาธิตออนไลน์

แนวคิดหลักแสดงใน pseudocode ในConcrete Mathematics Section 4.5 (p122 ใน edition ของฉัน):

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 ดังนั้นสองครั้งที่ค่าบวกหนึ่งคือตัวเศษและส่วนนั้นยาวกว่าหนึ่งบิต

ในฐานะที่เป็นจุดสนใจของ Golfscripters นี่เป็นหนึ่งในโอกาสที่หายากเหล่านั้นเมื่อฉันพบว่ามีประโยชน์ (โอเคฉันใช้มันเป็นตัวนับลูปเท่านั้น แต่ก็ดีกว่าไม่มีอะไร)


1

Mathematica, 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

Rubied & golfed โซลูชันอ้างอิงจาก @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 ตัวโดยไม่ต้องกังวลเกี่ยวกับการชกมวยและ unboxing ในอาร์เรย์ ฉันเลือก CoffeeScript เพราะมันไม่ได้นำหน้าตัวแปรด้วย$(20 ตัวอักษรที่บันทึกไว้เช่น PHP) มีไวยากรณ์นิยามฟังก์ชันสั้น ๆ ซึ่งอนุญาตให้ค่าพารามิเตอร์เริ่มต้น (ดังนั้นจึงไม่จำเป็นต้องห่อf(a,b,x,y)ในฟังก์ชั่นg(a,b) = f(a,b,0,1)) และให้ฉันใช้ Booleans เป็นจำนวนเต็ม นิพจน์ที่มีค่าที่มีประโยชน์ สำหรับผู้ที่ไม่ทราบ 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 chars; online demo ) การเขียนด้วยวิธีนี้จะเปิดเผยความสัมพันธ์กับอัลกอริทึมของ Euclid


คุณไม่จำเป็นต้องใช้วงเล็บa<bซึ่งช่วยให้คุณประหยัดหนึ่งอักขระ การทำ Inline cให้อีกสองอัน คุณอาจพิจารณาไวยากรณ์f=->a,b,x=0,y=1{...}สำหรับคำจำกัดความที่สั้นลง
Howard

@Howard ฉันไม่ทราบว่าคุณใช้ Ruby รุ่นใด แต่ใน IDEOne ฉันได้รับข้อผิดพลาดทางไวยากรณ์ถ้าฉันลองลบวงเล็บเหล่านั้นหรือใช้ไวยากรณ์ของฟังก์ชันนั้น
Peter Taylor

ลองกับพื้นที่พิเศษหลังจากc=a<b ? bมิฉะนั้น questionmark bจะถือว่าเป็นส่วนหนึ่งของ
Howard

0

Python - 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

มันก็ไม่ได้ค้นหาแบบทวิภาคระหว่างเศษส่วนการใช้ประโยชน์จากความจริงที่ว่าmediantของสองเศษส่วนจะเสมอระหว่างค่าของทั้งสองเศษส่วน


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

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สร้างแถวถัดไปจากแถวก่อนหน้าโดยดูที่เศษส่วนแต่ละคู่และแทรกแถวใหม่ระหว่างแถวแรกและแถวเรียกซ้ำ (ซึ่งจะทำให้ส่วนที่สองอยู่ตรงนั้น) เคสพื้นฐานนั้นง่ายมากเนื่องจากเป็นเพียงฟังก์ชั่นระบุตัวตน titerates ฟังก์ชั่นที่ฟังก์ชั่นตามไปเรื่อย ๆ ปิดสถานะเริ่มต้นที่มีเพียงสองเศษส่วนเขตแดน tจากนั้นสร้างดัชนีแต่ละแถว ( i) และแต่ละรายการในแถว ( j) และค้นหาเศษส่วนแรกที่ตรงกับสิ่งที่เรากำลังมองหา เมื่อพบว่ามันให้ผลjเป็นเศษและ2^iส่วน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.