คำนวณฟังก์ชันไบนารีที่มีประสิทธิภาพที่สุด


13

วันนี้เราจะคำนวณฟังก์ชันไบนารีที่มีประสิทธิภาพที่สุด โดยเฉพาะอย่างยิ่งเราจะคำนวณฟังก์ชันซึ่งเมื่อนิพจน์ถูกสร้างขึ้นจากการใช้ฟังก์ชันกับอินพุตคงที่ 0 หรือเอาท์พุทของตัวเองสามารถแสดงจำนวนเต็มบวกทั้งหมดด้วยนิพจน์ที่สั้นที่สุดที่เป็นไปได้วางลำดับความสำคัญสูงกว่า

ฟังก์ชั่นนี้สร้างขึ้นดังนี้

สำหรับแต่ละจำนวนเต็มเริ่มต้นที่ 1 และขึ้นไปเลือกนิพจน์ที่สั้นที่สุดซึ่งเรายังไม่ได้กำหนดเอาท์พุทและทำให้จำนวนเต็มนั้นเป็นเอาท์พุทของนิพจน์นั้น ความสัมพันธ์ในความยาวของการแสดงออกจะถูกทำลายโดยอาร์กิวเมนต์ซ้ายที่เล็กกว่าและจากนั้นด้วยอาร์กิวเมนต์ที่เล็กลง นี่คือวิธีการทำงาน:

  • เริ่มแรกมีการมอบหมาย 1 รายการ นิพจน์ที่ไม่ได้กำหนดที่สั้นที่สุดคือf(0, 0)ดังนั้นเราจะตั้งค่าเป็น 1

  • ตอนนี้ 2 ยังไม่ได้กำหนด ที่สั้นที่สุดการแสดงออกที่ไม่ได้กำหนดเป็นf(f(0, 0), 0)= f(1, 0)และ=f(0, f(0, 0)) ความสัมพันธ์จะถูกแบ่งไปทางซ้ายอาร์กิวเมนต์ที่มีขนาดเล็กดังนั้นf(0, 1)f(0, 1) = 2

  • การแสดงออกที่ไม่ได้กำหนดที่สั้นที่สุดที่เหลือf(f(0, 0), 0)= ดังนั้นf(1, 0)f(1, 0) = 3

  • ตอนนี้เราไม่ได้แสดงออกเพียง 2 fวินาทีและ 3 0วินาทีดังนั้นเราจะต้องเพิ่มอีกหนึ่งรายการ ทำลายความสัมพันธ์โดยอาร์กิวเมนต์ซ้ายแล้วโต้แย้งสิทธิที่เราจะได้รับตั้งแต่f(0, 2) = 4f(0, f(0, f(0, 0))) = f(0, f(0, 1)) = f(0, 2)

  • อย่างต่อเนื่องเรามีf(0, 3) = 5, f(1, 1) = 6, f(2, 0) = 7, f(3, 0) = 8, f(0, 4) = 9...

นี่คือตารางที่ฉันกรอกสำหรับค่าแรก ๆ :

    0  1  2  3  4  5  6  7  8
 /---------------------------
0|  1  2  4  5  9 10 11 12 13
1|  3  6 14 15 37 38 39 40 41
2|  7 16 42 43
3|  8 17 44 45
4| 18 46
5| 19 47
6| 20 48
7| 21 49
8| 22 50

อีกวิธีในการดูคือแต่ละเอาต์พุตมีขนาดเท่ากับผลรวมของขนาดของอินพุตบวกหนึ่ง ตารางจะถูกเติมตามลำดับของการเพิ่มขนาดของเอาต์พุตความสัมพันธ์ที่ขาดโดยการย่อขนาดอินพุตซ้ายแล้วป้อนอินพุตให้ถูกต้อง

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


ดูเหมือนว่าA072766แต่จะแตกต่างจาก f (3, 1)
kennytm

2
นี่เป็นความท้าทายครั้งแรกในขณะที่ฉันค่อนข้างจะคำนวณอย่างมีประสิทธิภาพ ฉันเชื่อว่าบางสิ่งเป็นไปได้ด้วยตัวเลขคาตาลัน แต่ไม่สามารถคิดวิธีแก้ปัญหาได้ทันที อืม ...
orlp

2
เอาล่ะฉันไม่คิดว่ามันจะเป็นคำตอบที่ดีนักกอล์ฟ แต่สิ่งที่คุณสามารถทำได้เพื่อให้มีประสิทธิภาพพอสมควรคือการลบตัวเลขคาตาลันซ้ำ ๆ จากการโต้แย้งฟังก์ชั่นจนกว่าพวกมันจะเล็กกว่าหมายเลขคาตาลันต่อไป จากนั้นคุณก็พบความยาวของการแสดงออกของพวกเขา จากนั้นคุณสามารถใช้ฟังก์ชั่นการจัดอันดับ / ยกเลิกการจัดเรียงจากบทความนี้พร้อมการแก้ไขเพื่อคำนวณผลลัพธ์ บางทีหลังจากทำทุกอย่างที่เป็นไปได้ในการ 'ยกเลิก' บิตของรหัสที่อยู่ตรงกลางและหาทางออกที่สง่างามพอสมควร
orlp

ที่จริงแล้วแนวทางจากความคิดเห็นก่อนหน้าของฉันไม่ทำงาน ((0, (0, (0, 0))), 0)มีขนาดเล็กกว่าพจนานุกรม(((0, 0), 0), (0, 0))แต่หลังมีขนาดเล็กกว่าด้านซ้ายมือ
orlp

คำตอบ:


6

Haskell, 110 ไบต์

f q=head[i|let c=[(-1,0)]:[[(f a,f b)|n<-[0..k],a<-c!!n,b<-c!!(k-n)]|k<-[0..]],(p,i)<-zip(concat c)[0..],p==q]

อาร์กิวเมนต์ที่นี่จะนำไปเป็น (x,y)tuple ค่อนข้างคล้ายกับคำตอบข้างต้น แต่รายการค้นหามีดัชนีด้านซ้ายและขวาเพียงคู่แทนต้นไม้


1
คำตอบที่ดี! head[...]เป็น[...]!!0และสามารถลงไป(p,i)<-zip(concat c)[0..] (i,p)<-zip[0..]$id=<<c
Laikoni

ขอบคุณสำหรับการปรับปรุง! เพิ่มid=<<เพลงเข้าไปแน่นอน:)
halfflat

5

Python 3, 154 ไบต์

b=lambda n:[(l,r)for k in range(1,n)for l in b(k)for r in b(n-k)]+[0]*(n<2)
def f(x,y):r=sum((b(n)for n in range(1,x+y+3)),[]);return r.index((r[x],r[y]))

มันไม่เร็วมากหรือไม่ค่อยเล่นกอล์ฟ แต่เป็นการเริ่มต้น


5

ว้าว! ฉันจัดการเพื่อสร้างอัลกอริทึมการคำนวณที่มีประสิทธิภาพ ฉันไม่ได้คาดหวังสิ่งนี้ในตอนแรก การแก้ปัญหาค่อนข้างสง่างาม มันซ้ำแล้วซ้ำอีกฉงนฉงายมากขึ้นและมากขึ้นแล้ว recurses ทุกทางลงไปที่กรณีฐานของ 0 ในคำตอบนี้ C (n) ฟังก์ชั่นหมายถึงตัวเลขคาตาลัน

ขั้นตอนแรกที่สำคัญคือการยอมรับว่ามี C (0) = 1 ค่าของความยาวเป็นศูนย์ (คือ 0 ตัวเอง), C (1) = 1 ค่าของความยาวหนึ่ง (คือ f (0, 0)), C (2) = 2 ค่าของความยาวสอง (f (0, f (0, 0)) และ f (f (0, 0), 0))

นี่หมายความว่าหากเรากำลังมองหานิพจน์ที่ n และเราพบว่า k ที่ใหญ่ที่สุดที่ C (0) + C (1) + ... + C (k) <= n จากนั้นเรารู้ว่า n มีความยาว k .

แต่ตอนนี้เราสามารถทำต่อไปได้! เนื่องจากนิพจน์ที่เรามองหาคือนิพจน์ n - C (0) - C (1) - ... - C (k) th ในคลาสความยาว

ตอนนี้เราสามารถใช้กลอุบายที่คล้ายกันเพื่อหาความยาวของเซกเมนต์ด้านซ้ายและอันดับในส่วนย่อยนั้น และจากนั้นเราก็พบว่าอันดับของเราลดลง

พบว่า f (5030, 3749) = 1542317211 ในพริบตา

Python การไม่เข้าร่วม

def C(n):
    r = 1
    for i in range(n):
        r *= 2*n - i
        r //= i + 1
    return r//(n+1)

def unrank(n):
    if n == 0: return 0

    l = 0
    while C(l) <= n:
        n -= C(l)
        l += 1

    right_l = l - 1
    while right_l and n >= C(l - 1 - right_l) * C(right_l):
        n -= C(l - 1 - right_l) * C(right_l)
        right_l -= 1

    right_num = C(right_l)

    r_rank = n % right_num
    l_rank = n // right_num

    for sz in range(l - 1 - right_l): l_rank += C(sz)
    for sz in range(right_l): r_rank += C(sz)

    return (unrank(l_rank), unrank(r_rank))

def rank(e):
    if e == 0: return 0
    left, right = e

    l = str(e).count("(")
    left_l = str(left).count("(")
    right_l = str(right).count("(")
    right_num = C(right_l)

    n = sum(C(sz) for sz in range(l))
    n += sum(C(sz)*C(l - 1 - sz) for sz in range(left_l))

    n += (rank(left) - sum(C(sz) for sz in range(left_l))) * C(right_l)
    n += rank(right) - sum(C(sz) for sz in range(right_l))

    return n

def f(x, y):
    return rank((unrank(x), unrank(y)))

ฉันค่อนข้างแน่ใจว่าฉันกำลังทำการคำนวณที่ไม่จำเป็นและขั้นตอนกลางจำนวนมากสามารถลบออกได้

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