Python 2 ใช้ pypy และ pp: n = 15 ใน 3 นาที
นอกจากนี้ยังเป็นเพียงกำลังดุร้ายที่เรียบง่าย น่าสนใจที่จะเห็นว่าฉันเกือบจะได้ความเร็วเดียวกันกับ kuroi neko กับ C ++ รหัสของฉันสามารถเข้าถึงได้n = 12ในเวลาประมาณ 5 นาที และฉันจะรันมันบนคอร์เสมือนหนึ่งแกนเท่านั้น
แก้ไข: ลดพื้นที่การค้นหาด้วยปัจจัย n
ผมสังเกตเห็นว่าเวกเตอร์กรณืA*การAผลิตตัวเลขเดียวกับความน่าจะเป็น (หมายเลขเดียวกัน) เป็นเวกเตอร์เดิมเมื่อผมย้ำไปA Bเช่นเวกเตอร์(1, 1, 0, 1, 0, 0)มีความน่าจะเป็นเช่นเดียวกับแต่ละเวกเตอร์(1, 0, 1, 0, 0, 1), (0, 1, 0, 0, 1, 1), (1, 0, 0, 1, 1, 0), (0, 0, 1, 1, 0, 1)และเมื่อเลือกแบบสุ่ม(0, 1, 1, 0, 1, 0) Bดังนั้นผมจึงไม่ได้มีการย้ำไปแต่ละเหล่านี้ 6 เวกเตอร์ แต่เพียงประมาณ 1 และแทนที่ด้วยcount[i] += 1count[i] += cycle_number
ซึ่งจะช่วยลดความซับซ้อนจากการTheta(n) = 6^n Theta(n) = 6^n / nดังนั้นn = 13มันเร็วกว่ารุ่นก่อนหน้าประมาณ 13 เท่า มันจะคำนวณn = 13ในประมาณ 2 นาที 20 วินาที เพราะn = 14มันยังช้าไปหน่อย ใช้เวลาประมาณ 13 นาที
แก้ไข 2: Multi-core-programming
ไม่ค่อยพอใจกับการปรับปรุงครั้งต่อไป ฉันตัดสินใจลองใช้งานโปรแกรมของฉันด้วยหลายคอร์ ในแกน 2 + 2 ของฉันตอนนี้ฉันสามารถคำนวณได้n = 14ในเวลาประมาณ 7 นาที เพียงปัจจัยของการปรับปรุง 2
รหัสที่สามารถใช้ได้ใน repo GitHub นี้: การเชื่อมโยง การเขียนโปรแกรมแบบมัลติคอร์ทำให้น่าเกลียดเล็กน้อย
แก้ไข 3: การลดพื้นที่การค้นหาสำหรับAเวกเตอร์และBเวกเตอร์
ฉันสังเกตเห็นความสมมาตรของกระจกแบบเดียวกันกับเวกเตอร์ที่Aเหมือนกันกับคุโรอิเนโกะ ยังไม่แน่ใจว่าทำไมจึงใช้งานได้ (และหากใช้งานได้กับแต่ละรายการn)
การลดพื้นที่การค้นหาสำหรับBเวกเตอร์เป็นเรื่องที่ฉลาดขึ้นเล็กน้อย ฉันแทนที่รุ่นเวกเตอร์ ( itertools.product) ด้วยฟังก์ชันของตัวเอง โดยทั่วไปฉันเริ่มต้นด้วยรายการที่ว่างเปล่าและวางมันลงบนสแต็ก จนกว่าสแต็กจะว่างเปล่าฉันจะลบรายการถ้ามันไม่ได้มีความยาวเท่าnกันฉันจะสร้าง 3 รายการอื่น ๆ (โดยการต่อท้าย -1, 0, 1) และผลักมันลงบนสแต็ก ฉันมีรายการที่มีความยาวเท่าnกันฉันสามารถประเมินผลรวม
ตอนนี้ฉันสร้างเวกเตอร์ด้วยตัวเองฉันสามารถกรองมันขึ้นอยู่กับว่าฉันสามารถเข้าถึงผลรวม = 0 หรือไม่ เช่นถ้าเวกเตอร์ของฉันAเป็น(1, 1, 1, 0, 0)และเวกเตอร์ของฉันBรูปลักษณ์ที่(1, 1, ?, ?, ?)ฉันรู้ว่าฉันไม่สามารถเติมเต็มด้วยค่าเพื่อให้? A*B = 0ดังนั้นฉันไม่ต้องทำซ้ำเวกเตอร์ทั้ง 6 Bตัว(1, 1, ?, ?, ?)นั้น
เราสามารถปรับปรุงเกี่ยวกับเรื่องนี้ถ้าเราไม่สนใจค่าสำหรับ 1. ตามที่ระบุไว้ในคำถามสำหรับค่าสำหรับi = 1เป็นลำดับA081671 มีหลายวิธีในการคำนวณเหล่านั้น a(n) = (4*(2*n-1)*a(n-1) - 12*(n-1)*a(n-2)) / nฉันเลือกที่กำเริบง่าย: เนื่องจากเราสามารถคำนวณในพื้นไม่มีเวลาที่เราสามารถกรองพาหะมากขึ้นสำหรับi = 1 Bเช่นและA = (0, 1, 0, 1, 1) B = (1, -1, ?, ?, ?)เราสามารถละเว้นเวกเตอร์ได้โดยที่แรก? = 1เพราะA * cycled(B) > 0สำหรับเวกเตอร์ทั้งหมดเหล่านี้ ฉันหวังว่าคุณสามารถติดตาม มันอาจไม่ใช่ตัวอย่างที่ดีที่สุด
ด้วยวิธีนี้ฉันสามารถคำนวณn = 15ใน 6 นาที
แก้ไข 4:
นำความคิดที่ยอดเยี่ยมของ kuroi neko ไปใช้อย่างรวดเร็วซึ่งกล่าวว่าสิ่งนั้นBและ-Bให้ผลลัพธ์ที่เหมือนกัน เร่งความเร็ว x2 การติดตั้งใช้งานนั้นเป็นเพียงการแฮ็คข้อมูลอย่างรวดเร็วเท่านั้น n = 15ใน 3 นาที
รหัส:
สำหรับการเดินทางเยือนรหัสสมบูรณ์Github รหัสต่อไปนี้เป็นเพียงการแสดงคุณสมบัติหลัก ฉันออกจากการนำเข้าการเขียนโปรแกรมแบบมัลติคอร์พิมพ์ผล ...
count = [0] * n
count[0] = oeis_A081671(n)
#generating all important vector A
visited = set(); todo = dict()
for A in product((0, 1), repeat=n):
if A not in visited:
# generate all vectors, which have the same probability
# mirrored and cycled vectors
same_probability_set = set()
for i in range(n):
tmp = [A[(i+j) % n] for j in range(n)]
same_probability_set.add(tuple(tmp))
same_probability_set.add(tuple(tmp[::-1]))
visited.update(same_probability_set)
todo[A] = len(same_probability_set)
# for each vector A, create all possible vectors B
stack = []
for A, cycled_count in dict_A.iteritems():
ones = [sum(A[i:]) for i in range(n)] + [0]
# + [0], so that later ones[n] doesn't throw a exception
stack.append(([0] * n, 0, 0, 0, False))
while stack:
B, index, sum1, sum2, used_negative = stack.pop()
if index < n:
# fill vector B[index] in all possible ways,
# so that it's still possible to reach 0.
if used_negative:
for v in (-1, 0, 1):
sum1_new = sum1 + v * A[index]
sum2_new = sum2 + v * A[index - 1 if index else n - 1]
if abs(sum1_new) <= ones[index+1]:
if abs(sum2_new) <= ones[index] - A[n-1]:
C = B[:]
C[index] = v
stack.append((C, index + 1, sum1_new, sum2_new, True))
else:
for v in (0, 1):
sum1_new = sum1 + v * A[index]
sum2_new = sum2 + v * A[index - 1 if index else n - 1]
if abs(sum1_new) <= ones[index+1]:
if abs(sum2_new) <= ones[index] - A[n-1]:
C = B[:]
C[index] = v
stack.append((C, index + 1, sum1_new, sum2_new, v == 1))
else:
# B is complete, calculate the sums
count[1] += cycled_count # we know that the sum = 0 for i = 1
for i in range(2, n):
sum_prod = 0
for j in range(n-i):
sum_prod += A[j] * B[i+j]
for j in range(i):
sum_prod += A[n-i+j] * B[j]
if sum_prod:
break
else:
if used_negative:
count[i] += 2*cycled_count
else:
count[i] += cycled_count
การใช้งาน:
คุณต้องติดตั้ง pypy (สำหรับ Python 2 !!!) โมดูลขนานหลามไม่ได้รังเพลิงหลาม 3. จากนั้นคุณต้องติดตั้งขนานโมดูลหลามpp-1.6.4.zip สารสกัดจากมันลงในโฟลเดอร์และโทรcdpypy setup.py install
จากนั้นคุณสามารถเรียกโปรแกรมของฉันด้วย
pypy you-do-the-math.py 15
มันจะกำหนดจำนวนของซีพียูโดยอัตโนมัติ อาจมีข้อความผิดพลาดหลังจากเสร็จสิ้นโปรแกรมเพียงแค่ละเว้นพวกเขา n = 16ควรเป็นไปได้ในเครื่องของคุณ
เอาท์พุท:
Calculation for n = 15 took 2:50 minutes
1 83940771168 / 470184984576 17.85%
2 17379109692 / 470184984576 3.70%
3 3805906050 / 470184984576 0.81%
4 887959110 / 470184984576 0.19%
5 223260870 / 470184984576 0.05%
6 67664580 / 470184984576 0.01%
7 30019950 / 470184984576 0.01%
8 20720730 / 470184984576 0.00%
9 18352740 / 470184984576 0.00%
10 17730480 / 470184984576 0.00%
11 17566920 / 470184984576 0.00%
12 17521470 / 470184984576 0.00%
13 17510280 / 470184984576 0.00%
14 17507100 / 470184984576 0.00%
15 17506680 / 470184984576 0.00%
หมายเหตุและความคิด:
- ฉันมีโปรเซสเซอร์ i7-4600m ที่มี 2 คอร์และ 4 เธรด ไม่เป็นไรถ้าฉันใช้ 2 หรือ 4 เธรด การใช้งาน cpu คือ 50% พร้อม 2 เธรดและ 100% พร้อม 4 เธรด แต่ยังคงใช้เวลาเท่ากัน ฉันไม่รู้ว่าทำไม ฉันตรวจสอบแล้วว่าแต่ละเธรดมีเพียงครึ่งหนึ่งของข้อมูลเมื่อมี 4 เธรดตรวจสอบผลลัพธ์ ...
- ฉันใช้รายการจำนวนมาก Python ไม่ค่อยมีประสิทธิภาพในการจัดเก็บฉันต้องคัดลอกรายการจำนวนมาก ... ดังนั้นฉันคิดว่าจะใช้จำนวนเต็มแทน ฉันสามารถใช้บิต 00 (สำหรับ 0) และ 11 (สำหรับ 1) ในเวกเตอร์ A และบิต 10 (สำหรับ -1), 00 (สำหรับ 0) และ 01 (สำหรับ 1) ในเวกเตอร์ B สำหรับผลิตภัณฑ์ ของ A และ B ฉันจะต้องคำนวณ
A & Bและนับบล็อก 01 และ 10 เท่านั้น การปั่นจักรยานสามารถทำได้ด้วยการขยับเวกเตอร์และใช้มาสก์ ... ฉันใช้งานทั้งหมดนี้ไปแล้ว แต่มันกลับกลายเป็นว่าช้ากว่ารายการ ฉันเดาว่า pypy เพิ่มประสิทธิภาพการดำเนินรายการอย่างแท้จริง