คำนวณความน่าจะเป็นอย่างแน่นอนและรวดเร็ว


10

[นี่เป็นคำถามจากคู่ค้าเพื่อคำนวณความน่าจะเป็น ]

งานนี้เป็นเรื่องเกี่ยวกับการเขียนโค้ดเพื่อคำนวณความน่าจะเป็นตรงและรวดเร็ว ผลลัพธ์ควรเป็นความน่าจะเป็นที่แม่นยำที่เขียนเป็นเศษส่วนในรูปแบบที่ลดลงมากที่สุด นั่นคือไม่ควรเอาท์พุท4/8แต่จะดี1/2กว่า

สำหรับจำนวนเต็มบวกnพิจารณาสตริงสุ่มอย่างสม่ำเสมอของ 1s และ -1s ของความยาวnและเรียกมันว่า A ตอนนี้เชื่อมต่อกับAค่าแรก นั่นคือA[1] = A[n+1]ถ้าการจัดทำดัชนีจาก 1. ในขณะนี้มีความยาวA n+1ทีนี้ลองพิจารณาความยาวสตริงที่สองแบบสุ่มnซึ่งมีnค่าแรกคือ -1, 0 หรือ 1 ที่มีความน่าจะเป็น 1/4, 1/2, 1/4 แต่ละอันและเรียกมันว่า B

ตอนนี้พิจารณาผลิตภัณฑ์ภายในA[1,...,n]และBและสินค้าภายในของและA[2,...,n+1]B

n=3ตัวอย่างเช่นพิจารณา ค่าที่เป็นไปได้AและBอาจจะเป็นและA = [-1,1,1,-1] B=[0,1,-1]ในกรณีนี้ผลิตภัณฑ์ทั้งสองด้านมีและ02

รหัสของคุณจะต้องแสดงผลความน่าจะเป็นที่ผลิตภัณฑ์ทั้งสองนั้นมีค่าเป็นศูนย์

คัดลอกตารางที่ผลิตโดย Martin Büttnerเรามีผลลัพธ์ตัวอย่างต่อไปนี้

n   P(n)
1   1/2
2   3/8
3   7/32
4   89/512
5   269/2048
6   903/8192
7   3035/32768
8   169801/2097152

ภาษาและห้องสมุด

คุณสามารถใช้ภาษาและไลบรารี่ที่คุณต้องการได้ฟรี ฉันจะต้องสามารถเรียกใช้รหัสของคุณดังนั้นโปรดรวมคำอธิบายแบบเต็มสำหรับวิธีการเรียกใช้ / รวบรวมรหัสของคุณใน linux ถ้าเป็นไปได้ทั้งหมด

งาน

รหัสของคุณจะต้องเริ่มต้นด้วยn=1และให้ผลลัพธ์ที่ถูกต้องสำหรับการเพิ่ม n ในแต่ละบรรทัด ควรหยุดหลังจาก 10 วินาที

คะแนน

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

สารบัญ

  • n = 64ในหลาม รุ่นที่ 1 โดย Mitch Schwartz
  • n = 106ในหลาม รุ่น 11 มิถุนายน 2558 โดย Mitch Schwartz
  • n = 151ในภาษา C ++ คำตอบของ Port of Mitch Schwartz โดย kirbyfan64sos
  • n = 165ในหลาม รุ่น 11 มิถุนายน 2015 ว่า "การตัดแต่งกิ่ง" N_MAX = 165รุ่นโดยมิทช์ชวาร์ตซ์ด้วย
  • n = 945ในPythonโดย Min_25 โดยใช้สูตรที่แน่นอน ! ที่น่าตื่นตาตื่นใจ
  • n = 1228ในPythonโดย Mitch Schwartz ใช้สูตรที่แน่นอนอื่น (ตามคำตอบก่อนหน้าของ Min_25)
  • n = 2761ในPythonโดย Mitch Schwartz โดยใช้สูตรที่แน่นอนกว่าเดิมอย่างรวดเร็ว
  • n = 3250ในPythonโดยใช้Pypyโดย Mitch Schwartz โดยใช้การดำเนินการเดียวกัน คะแนนนี้ต้องpypy MitchSchwartz-faster.py |tailหลีกเลี่ยงค่าใช้จ่ายในการเลื่อนคอนโซล

ฉันสงสัยว่าโซลูชัน numpy จะทำงานเร็วกว่า Boost C ++ หรือไม่
qwr

@qwr ฉันคิดว่า numpy, numba และ cython น่าสนใจเพราะมันเก็บไว้ในตระกูล Python

2
ฉันชอบที่จะเห็นปัญหารหัสที่เร็วที่สุดเหล่านี้
qwr

@qwr เป็นคำถามที่ฉันชื่นชอบ ... ขอบคุณ! ความท้าทายคือการหาสิ่งที่ไม่เพียง แต่เกี่ยวข้องกับการเข้ารหัสอัลกอริทึมเดียวกันในภาษาระดับต่ำสุดที่คุณสามารถหาได้

คุณกำลังเขียนผลลัพธ์ไปยังคอนโซลหรือไฟล์หรือไม่? การใช้ pypy และการเขียนไปยังไฟล์นั้นเร็วที่สุดสำหรับฉัน คอนโซลทำงานช้าลงอย่างมาก
gnibbler

คำตอบ:


24

หลาม

สูตรปิดของp(n)คือ

ป้อนคำอธิบายรูปภาพที่นี่

ฟังก์ชันสร้างเลขชี้กำลังของp(n)คือ

ป้อนคำอธิบายรูปภาพที่นี่

โดยที่I_0(x)ฟังก์ชัน Bessel ที่ได้รับการแก้ไขเป็นชนิดแรก

แก้ไขเมื่อวันที่ 2015-06-11:
- อัปเดตรหัส Python

แก้ไขเมื่อวันที่ 2015-06-2556:
- เพิ่มการพิสูจน์สูตรข้างต้น
- time_limitคงที่
- เพิ่มรหัส PARI / GP

หลาม

def solve():
  # straightforward implementation

  from time import time
  from itertools import count

  def binom(n, r):
    return facts[n] // (facts[r] * facts[n - r])

  def p(N):
    ans = 0
    for i in range(1 + N // 2):
      t = binom(2 * (N - 2 * i), N - 2 * i)
      t *= binom(N, 2 * i)
      t *= binom(4 * i, 2 * i)
      ans += t
    e = (ans & -ans).bit_length() - 1
    numer = ans >> e
    denom = 1 << (3 * N - 1 - e)
    return numer, denom

  facts = [1]
  time_limit = 10.0 + time()

  for i in count(1):
    facts.append(facts[-1] * (2 * i - 1))
    facts.append(facts[-1] * (2 * i))

    n, d = p(i)

    if time() > time_limit:
      break

    print("%d %d/%d" % (i, n, d))

solve()

PARI / GP

p(n) = polcoeff( (exp(x/2) + 1) * besseli(0, x/4) ^ 2, n) * n!;

หลักฐาน:
ปัญหานี้คล้ายกับปัญหาการเดินแบบสุ่ม 2 มิติ (ถูก จำกัด )

ถ้าA[i] = A[i+1]เราสามารถย้ายจาก(x, y)ไป(x+1, y+1)[1 วิธี], (x, y)[2 วิธี] หรือ(x-1, y-1)[1 วิธี]

ถ้าA[i] != A[i+1]เราสามารถย้ายจาก(x, y)ไป(x-1, y+1)[1 วิธี], (x, y)[2 วิธี] หรือ(x+1, y-1)[1 วิธี]

ให้a(n, m) = [x^m]((x+1)^n + (x-1)^n), b(n) = [x^n](1+x)^{2n}และc(n)จะมีหลายวิธีที่จะย้ายจาก(0, 0)ที่จะ(0, 0)มีnขั้นตอน

จากนั้น c(n) = \sum_{i=0}^n a(n, i) * b(i) * b(n-i).

เนื่องจากp(n) = c(n) / 8^nเราสามารถรับสูตรแบบปิดด้านบนได้


1
นี่คือ .. ดี .. น่าทึ่ง! คุณคำนวณสูตรที่แน่นอนได้อย่างไรบนโลก?

1
ว้าว! แบบฟอร์มปิดเรียบร้อยเสมอ!
qwr

1
@ Lembik: ฉันได้เพิ่มข้อพิสูจน์ (หยาบ)
Min_25

1
@qwr: ขอบคุณ ผมคิดว่าอย่างนั้นเหมือนกัน !
Min_25

1
@ mbomb007: ใช่ แต่มันเป็นงานที่ใช้งานมากกว่างานคอมพิวเตอร์ ดังนั้นฉันจะไม่รหัสใน C ++
Min_25

9

หลาม

หมายเหตุ:ขอแสดงความยินดีกับ Min_25 สำหรับการค้นหาโซลูชันแบบปิด!

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

รหัสนี้มาถึงN=39ภายใน 10 วินาทีสำหรับแล็ปท็อปเครื่องเก่าที่ใช้ Python 2.7.5

from time import*
from fractions import*
from collections import*

X={(1,0,0,0):1,(-1,0,0,0):1}

T=time()
N=0

while 1:
    Y=defaultdict(lambda:0)
    n=d=0
    for a,b,s,t in X:
        c=X[(a,b,s,t)]
        for A in ( (1,-1) if N else [a] ):
            for B in 1,0,0,-1:
                n+=c*(s+A*B==0==t+A*b+a*B)
                d+=c
                Y[(a,B,s+A*B,t+A*b)]+=c
    if time()>T+10: break
    N+=1
    print N,Fraction(n,d)
    X=Y

สำหรับ tuple (a,b,s,t): aเป็นองค์ประกอบแรกของA, bเป็นองค์ประกอบสุดท้ายของB, sเป็นผลิตภัณฑ์ภายในA[:-1]และBและtเป็นผลิตภัณฑ์ภายในA[1:-1]และB[:-1]โดยใช้สัญกรณ์ชิ้นหลาม รหัสของฉันไม่ได้เก็บอาร์เรย์AหรือBที่ใดก็ได้ดังนั้นฉันใช้ตัวอักษรเหล่านั้นเพื่ออ้างถึงองค์ประกอบต่อไปที่จะเพิ่มAและBตามลำดับ ตัวเลือกการตั้งชื่อตัวแปรนี้ทำให้คำอธิบายค่อนข้างอึดอัดใจ แต่ช่วยให้ดูดีA*b+a*Bในรหัสของตัวเอง โปรดทราบว่าองค์ประกอบที่เพิ่มเข้ามาAนั้นเป็นองค์ประกอบสุดท้ายเนื่องจากองค์ประกอบสุดท้ายมักจะเหมือนกับองค์ประกอบแรกเสมอ ฉันใช้เคล็ดลับของ Martin Büttnerในการรวม0สองครั้งBผู้สมัครเพื่อรับการกระจายความน่าจะเป็นที่เหมาะสม พจนานุกรมX(ซึ่งตั้งชื่อYตามN+1) ติดตามการนับอาร์เรย์ที่เป็นไปได้ทั้งหมดตามค่าของ tuple ตัวแปรnและdขาตั้งสำหรับเศษและส่วนซึ่งเป็นเหตุผลที่ผมเปลี่ยนชื่อของคำสั่งปัญหาเป็นnN

ส่วนสำคัญของตรรกะคือคุณสามารถอัปเดตจากNเป็นN+1ใช้ค่าใน tuple ผลิตภัณฑ์ภายในสองที่ระบุไว้ในคำถามที่จะได้รับจากและs+A*B t+A*b+a*Bชัดเจนถ้าคุณตรวจสอบคำจำกัดความเล็กน้อย โปรดทราบว่า[A,a]และ[b,B]เป็นสององค์ประกอบสุดท้ายของอาร์เรย์AและBตามลำดับ

โปรดทราบว่าsและtมีขนาดเล็กและมีขอบเขตตามNและเพื่อการใช้งานที่รวดเร็วในภาษาที่รวดเร็วเราสามารถหลีกเลี่ยงพจนานุกรมเพื่อสนับสนุนอาร์เรย์ได้

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

หมายเหตุ 1 : ขนาดของพจนานุกรมเพิ่มขึ้นเป็นสองเท่าNโดยที่ขนาดหมายถึงจำนวนคู่ของคีย์ - ค่า

หมายเหตุ 2 : ถ้าเรากำหนดขอบเขตบนNแล้วเราสามารถตัด tuples ที่และในทำนองเดียวกันสำหรับN_MAX - N <= |s| tสิ่งนี้สามารถทำได้โดยการระบุสถานะการดูดซับหรือโดยปริยายกับตัวแปรที่จะนับจำนวนของการตัดรัฐ (ซึ่งจะต้องคูณด้วย 8 ในแต่ละรอบซ้ำ)

อัปเดต : รุ่นนี้เร็วกว่า:

from time import*
from fractions import*
from collections import*

N_MAX=115

def main():
    T=time()

    N=1
    Y={(1,0,0,0):1,(1,1,1,0):1}
    n=1
    thresh=N_MAX

    while time() <= T+10:
        print('%d %s'%(N,Fraction(n,8**N/4)))

        N+=1
        X=Y
        Y=defaultdict(lambda:0)
        n=0

        if thresh<2:
            print('reached MAX_N with %.2f seconds remaining'%(T+10-time()))
            return

        for a,b,s,t in X:
            if not abs(s)<thresh>=abs(t):
                continue

            c=X[(a,b,s,t)]

            # 1,1

            if not s+1 and not t+b+a: n+=c
            Y[(a,1,s+1,t+b)]+=c

            # -1,1

            if not s-1 and not t-b+a: n+=c
            Y[(a,1,s-1,t-b)]+=c

            # 1,-1

            if not s-1 and not t+b-a: n+=c
            Y[(a,-1,s-1,t+b)]+=c

            # -1,-1

            if not s+1 and not t-b-a: n+=c
            Y[(a,-1,s+1,t-b)]+=c

            # 1,0

            c+=c

            if not s and not t+b: n+=c
            Y[(a,0,s,t+b)]+=c

            # -1,0

            if not s and not t-b: n+=c
            Y[(a,0,s,t-b)]+=c

        thresh-=1

main()

มีการใช้งานการเพิ่มประสิทธิภาพ:

  • ใส่ทุกอย่างลงในmain()- การเข้าถึงตัวแปรท้องถิ่นเร็วกว่าทั่วโลก
  • จัดการN=1อย่างชัดเจนเพื่อหลีกเลี่ยงการตรวจสอบ(1,-1) if N else [a](ซึ่งบังคับให้องค์ประกอบแรกใน tuple สอดคล้องกันเมื่อเพิ่มองค์ประกอบที่จะAเริ่มต้นจากรายการที่ว่างเปล่า)
  • คลี่วงในซึ่งช่วยกำจัดการคูณ
  • เพิ่มจำนวนเป็นสองเท่าcสำหรับการเพิ่ม0ไปยังBแทนที่จะทำการดำเนินการสองครั้ง
  • ตัวส่วนเป็นเสมอ8^Nดังนั้นเราไม่จำเป็นต้องติดตามมัน
  • ขณะนี้การสมมาตรเข้าบัญชี: เราสามารถแก้ไของค์ประกอบแรกของAเป็น1และแบ่งส่วนโดย2ตั้งแต่คู่ที่ถูกต้อง(A,B)ด้วยA[1]=1และผู้ที่มีสามารถใส่ลงในแบบหนึ่งต่อหนึ่งการติดต่อโดยกวนA[1]=-1 Aในทำนองเดียวกันเราสามารถแก้ไของค์ประกอบแรกของBที่ไม่เป็นลบ
  • ตอนนี้ด้วยการตัดแต่งกิ่ง คุณจะต้องเล่นซอN_MAXเพื่อดูว่าคะแนนที่ได้บนเครื่องของคุณ มันสามารถเขียนใหม่เพื่อค้นหาที่เหมาะสมN_MAXโดยอัตโนมัติโดยการค้นหาแบบไบนารี แต่ดูเหมือนว่าไม่จำเป็น? หมายเหตุ: เราไม่จำเป็นต้องตรวจสอบสภาพการตัดจนกว่าจะถึงรอบ ๆN_MAX / 2ดังนั้นเราสามารถเร่งความเร็วได้เล็กน้อยโดยทำซ้ำในสองขั้นตอน แต่ฉันตัดสินใจว่าจะไม่ใช้ความเรียบง่ายและความสะอาดของรหัส

1
นี่เป็นคำตอบที่ยอดเยี่ยมจริงๆ! คุณช่วยอธิบายสิ่งที่คุณทำเมื่อเร่งความเร็วได้หรือไม่?

@Lembik ขอบคุณ :) เพิ่มคำอธิบายรวมถึงการเพิ่มประสิทธิภาพเล็ก ๆ อีกหนึ่งอย่างและทำให้เป็นไปตาม Python3
Mitch Schwartz

ในคอมพิวเตอร์ของฉันฉันได้N=57รุ่นแรกและรุ่นN=75ที่สอง
kirbyfan64sos

คำตอบของคุณยอดเยี่ยมมาก เป็นเพียงคำตอบของ Min_25 ที่ยิ่งใหญ่กว่า :)

5

หลาม

ด้วยความคิดของ Min_25 ในการเดินแบบสุ่มฉันสามารถไปถึงสูตรอื่นได้:

p (n) = \ start {คดี} \ frac {\ sum _ {i = 0} ^ {\ lfloor n / 2 \ rfloor} \ binom {2i} {i} ^ 2 \ binom {n} {2i} 4 ^ {n-2i}} {8 ^ n} & n \ text {คี่} \ \ frac {\ binom {n} {n / 2} ^ 2 + \ sum _ {i = 0} ^ {\ lfloor n / 2 \ rfloor} \ binom {2i} {i} ^ 2 \ binom {n} {2i} 4 ^ {n-2i}} {8 ^ n} & n \ text {แม้แต่} \ \ end {คดี}

นี่คือการใช้งาน Python โดยอิงจาก Min_25:

from time import*
from itertools import*

def main():
    def binom(n, k):
        return facts[n]/(facts[k]*facts[n-k])

    def p(n):
        numer=0
        for i in range(n/2+1):
            t=binom(2*i,i)
            t*=t
            t*=binom(n,2*i)
            t<<=2*(n-2*i)
            numer+=t
        if not n&1:
            numer+=t
        e=(numer&-numer).bit_length()-1
        numer>>=e
        denom=1<<(3*n-e)
        return numer, denom

    facts=[1]
    time_limit=time()+10

    for i in count(1):
        facts.append(facts[-1]*i)

        n,d=p(i)

        if time()>time_limit:
            break

        print("%d %d/%d"%(i,n,d))

main()

คำอธิบาย / หลักฐาน:

ครั้งแรกที่เราแก้ปัญหาการนับที่เกี่ยวข้องที่เราอนุญาตให้A[n+1] = -A[1]; นั่นคือองค์ประกอบเพิ่มเติมที่ต่อกันAสามารถเป็น1หรือ-1ไม่คำนึงถึงองค์ประกอบแรก ดังนั้นเราไม่จำเป็นต้องติดตามจำนวนครั้งที่A[i] = A[i+1]เกิดขึ้น เรามีการเดินแบบสุ่มต่อไปนี้:

จากที่(x,y)เราสามารถย้ายไปที่(x+1,y+1)[1 ทาง], (x+1,y-1)[1 ทาง], (x-1,y+1)[1 ทาง], (x-1,y-1)[1 ทาง], (x,y)[4 วิธี]

ที่xและyยืนสำหรับผลิตภัณฑ์ทั้งสองจุดและเราจะนับจำนวนของวิธีการที่จะย้ายจาก(0,0)ไป(0,0)ในnขั้นตอน นับว่านั้นก็จะถูกคูณด้วย2การบัญชีสำหรับข้อเท็จจริงที่ว่าAสามารถเริ่มต้นด้วยหรือ1-1

เราหมายถึงการเข้าพักที่(x,y)เป็นย้ายศูนย์

เราย้ำกว่าจำนวนที่ไม่ใช่ศูนย์การเคลื่อนไหวซึ่งจะต้องมีแม้กระทั่งในการสั่งซื้อที่จะได้รับกลับไปi (0,0)แนวนอนและแนวตั้งการเคลื่อนไหวทำขึ้นสองอิสระหนึ่งมิติเดินสุ่มซึ่งสามารถนับจากC(i,i/2)^2ที่C(n,k)เป็นค่าสัมประสิทธิ์ทวินาม (สำหรับการเดินด้วยkก้าวซ้ายและkก้าวขวามีC(2k,k)วิธีเลือกลำดับขั้นตอน) นอกจากนี้ยังมีC(n,i)วิธีการวางการเคลื่อนไหวและ4^(n-i)วิธีการเลือกการเคลื่อนที่แบบศูนย์ ดังนั้นเราจึงได้รับ:

a(n) = 2 * sum_{i in (0,2,4,...,n)} C(i/2,i)^2 * C(n,i) * 4^(n-i)

ตอนนี้เราต้องหันกลับไปใช้ปัญหาเดิม กำหนดคู่ที่อนุญาต(A,B)ให้เปลี่ยนแปลงได้หากBมีศูนย์ กำหนดคู่(A,B)ที่จะยอมให้ทำได้หากA[n+1] = -A[1]ทั้งสองจุดของผลิตภัณฑ์เป็นศูนย์ทั้งคู่

บทแทรก:สำหรับnคู่หนึ่งคู่ที่ยอมให้เกือบจะอยู่ในการติดต่อแบบหนึ่งต่อหนึ่งกับคู่ที่เปลี่ยนแปลงได้

เราสามารถ (พลิกกลับ) แปลงคู่แปลงสภาพ(A,B)เป็นคู่ที่อนุญาตเกือบ(A',B')โดยกวนA[m+1:]และB[m+1:]ที่เป็นดัชนีของศูนย์สุดท้ายในm Bการตรวจสอบนี้ตรงไปตรงมา: หากองค์ประกอบสุดท้ายของBเป็นศูนย์เราไม่จำเป็นต้องทำอะไรเลย มิฉะนั้นเมื่อเราลบล้างองค์ประกอบสุดท้ายของAเราสามารถลบล้างองค์ประกอบสุดท้ายของBเพื่อรักษาคำสุดท้ายของผลิตภัณฑ์ dot shifted Aแต่ขัดแย้งนี้ค่าสุดท้ายของการไม่ขยับตัวคูณจุดเพื่อให้เราแก้ไขปัญหานี้โดยการกวนองค์ประกอบที่สองไปสุดท้ายของ Bแต่แล้วนี้พ่นออกค่าที่สองไปสุดท้ายของผลิตภัณฑ์เปลี่ยนเพื่อให้เราแก้องค์ประกอบที่สองไปสุดท้ายของ และอื่น ๆ Bจนกว่าจะถึงองค์ประกอบในศูนย์

ตอนนี้เราเพียงแค่ต้องแสดงให้เห็นว่าไม่มีคู่ที่ยอมได้เกือบซึ่งBไม่มีศูนย์ เพื่อให้ผลิตภัณฑ์ดอทมีค่าเท่ากับศูนย์เราต้องมีจำนวน1และ-1เงื่อนไขเท่ากันในการยกเลิก แต่ละ-1ระยะประกอบด้วยหรือ(1,-1) (-1,1)ดังนั้นความเท่าเทียมกันของจำนวนของที่เกิดขึ้นได้รับการแก้ไขตาม-1 nหากองค์ประกอบแรกและสุดท้ายของAสัญญาณที่แตกต่างกันเราจะเปลี่ยนความเท่าเทียมกันดังนั้นนี่เป็นไปไม่ได้

ดังนั้นเราจึงได้รับ

c(n) = a(n)/2 if n is odd, else a(n)/2 + C(n,n/2)^2

p(n) = c(n) / 8^n

ซึ่งให้สูตรข้างต้น (ทำดัชนีใหม่ด้วยi' = i/2)

อัปเดต:นี่คือรุ่นที่เร็วกว่าโดยใช้สูตรเดียวกัน:

from time import*
from itertools import*

def main():
    time_limit=time()+10

    binoms=[1]
    cb2s=[1]
    cb=1

    for n in count(1):
        if n&1:
            binoms=[a+b for a,b in zip([0]+binoms,binoms)]
        else:
            binoms=[a+b for a,b in zip([0]+binoms,binoms+[binoms[-1]])]
            cb=(cb<<2)-(cb+cb)/(n/2)
            cb2s.append(cb*cb)

        numer=0
        for i in xrange(n/2+1):
            t=cb2s[i]*binoms[min(2*i,n-2*i)]
            t<<=2*(n-2*i)
            numer+=t
        if not n&1:
            numer+=t
        e=(numer&-numer).bit_length()-1
        numer>>=e
        denom=1<<(3*n-e)

        if time()>time_limit:
            break

        print("%d %d/%d"%(n,numer,denom))

main()

มีการใช้งานการเพิ่มประสิทธิภาพ:

  • ฟังก์ชั่นแบบอินไลน์ p(n)
  • ใช้การเกิดซ้ำสำหรับค่าสัมประสิทธิ์ทวินามC(n,k)กับk <= n/2
  • ใช้การเกิดซ้ำสำหรับค่าสัมประสิทธิ์ทวินามส่วนกลาง

เพื่อให้คุณรู้ว่าp(n)ไม่จำเป็นต้องมีฟังก์ชั่นเป็นชิ้น ๆ โดยทั่วไปหากf(n) == {g(n) : n is odd; h(n) : n is even}แล้วคุณสามารถเขียนf(n) == (n-2*floor(n/2))*g(n) + ((n+1)-2*(floor((n+1)/2)))*h(n)หรือใช้แทนn mod 2 (n-2*floor(n/2))ดูที่นี่
mbomb007

1
@ mbomb007 นั่นชัดเจนและไม่น่าสนใจ
Mitch Schwartz

3

การอธิบายสูตรของ Min_25

Min_25 โพสต์หลักฐานที่ดี แต่ใช้เวลาสักครู่ในการติดตาม นี่เป็นคำอธิบายเล็กน้อยในการเติมระหว่างบรรทัด

a (n, m) หมายถึงจำนวนวิธีที่จะเลือก A เช่นที่ [i] = A [i + 1] m ครั้ง สูตรสำหรับ (n, m) เทียบเท่ากับ a (n, m) = {2 * (n เลือก m) สำหรับ nm สม่ำเสมอ; 0 สำหรับ nm คี่} อนุญาตเพียงพาริตี้เดียวเนื่องจาก A [i]! = A [i + 1] จะต้องเกิดขึ้นเป็นจำนวนเท่าตัวดังนั้น A [0] = A [n] ตัวคูณของ 2 เกิดจากตัวเลือกเริ่มต้น A [0] = 1 หรือ A [0] = -1

เมื่อจำนวน (A [i]! = A [i + 1]) ถูกกำหนดให้เป็น q (ชื่อ i ในสูตร c (n)) มันจะแยกเป็น 1D สุ่มเดินความยาว q และ nq b (m) คือจำนวนวิธีที่จะใช้การเดินแบบสุ่มหนึ่งมิติของขั้นตอน m ที่สิ้นสุดที่เดียวกับที่มันเริ่มต้นและมีโอกาส 25% ที่จะเคลื่อนที่ไปทางซ้ายโอกาส 50% ที่จะอยู่นิ่งและโอกาส 25% ของ เลื่อนไปทางขวา วิธีที่ชัดเจนกว่าในการระบุฟังก์ชั่นการสร้างคือ [x ^ m] (1 + 2x + x ^ 2) ^ n โดยที่ 1, 2x และ x ^ 2 เป็นตัวแทนของซ้ายไม่มีการเคลื่อนไหวและถูกต้องตามลำดับ แต่แล้ว 1 + 2x + x ^ 2 = (x + 1) ^ 2


อีกเหตุผลที่จะรัก PPCG! ขอบคุณ.

2

C ++

เป็นเพียงแค่พอร์ตของคำตอบ Python (ยอดเยี่ยม) โดย Mitch Schwartz ความแตกต่างหลักคือฉันใช้2เพื่อเป็นตัวแทน-1ของaตัวแปรและทำสิ่งที่คล้ายกันbซึ่งทำให้ฉันใช้อาร์เรย์ ฉันใช้ Intel C ++ กับ-O3ฉันได้N=141! N=140รุ่นแรกของฉันมี

สิ่งนี้ใช้ Boost ฉันลองรุ่นขนาน แต่พบปัญหาบางอย่าง

#include <boost/multiprecision/gmp.hpp>
#include <boost/typeof/typeof.hpp>
#include <boost/rational.hpp>
#include <boost/chrono.hpp>
#include <boost/array.hpp>
#include <iostream>
#include <utility>
#include <map>

typedef boost::multiprecision::mpz_int integer;
typedef boost::array<boost::array<std::map<int, std::map<int, integer> >, 3>, 2> array;
typedef boost::rational<integer> rational;

int main() {
    BOOST_AUTO(T, boost::chrono::high_resolution_clock::now());

    int N = 1;
    integer n = 1;
    array* Y = new array, *X = NULL;
    (*Y)[1][0][0][0] = 1;
    (*Y)[1][1][1][0] = 1;

    while (boost::chrono::high_resolution_clock::now() < T+boost::chrono::seconds(10)) {
        std::cout << N << " " << rational(n, boost::multiprecision::pow(integer(8), N)/4) << std::endl;
        ++N;
        delete X;
        X = Y;
        Y = new array;
        n = 0;

        for (int a=0; a<2; ++a)
            for (int b=0; b<3; ++b)
                for (BOOST_AUTO(s, (*X)[a][b].begin()); s != (*X)[a][b].end(); ++s)
                    for (BOOST_AUTO(t, s->second.begin()); t != s->second.end(); ++t) {
                        integer c = t->second;
                        int d = b&2 ? -1 : b, e = a == 0 ? -1 : a;

                        if (s->first == -1 && t->first+d+e == 0) n += c;
                        (*Y)[a][1][s->first+1][t->first+d] += c;

                        if (s->first == 1 && t->first-d+e == 0) n += c;
                        (*Y)[a][1][s->first-1][t->first-d] += c;

                        if (s->first == 1 && t->first+d-e == 0) n += c;
                        (*Y)[a][2][s->first-1][t->first+d] += c;

                        if (s->first == -1 && t->first-d-e == 0) n += c;
                        (*Y)[a][2][s->first+1][t->first-d] += c;

                        c *= 2;

                        if (s->first == 0 && t->first+d == 0) n += c;
                        (*Y)[a][0][s->first][t->first+d] += c;

                        if (s->first == 0 && t->first-d == 0) n += c;
                        (*Y)[a][0][s->first][t->first-d] += c;
                    }
    }

    delete X;
    delete Y;
}

สิ่งนี้ต้องg++ -O3 kirbyfan64sos.cpp -o kirbyfan64sos -lboost_system -lboost_timer -lboost_chrono -lrt -lgmpรวบรวม (ขอบคุณ aditsu)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.