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] += 1
count[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 สารสกัดจากมันลงในโฟลเดอร์และโทรcd
pypy 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 เพิ่มประสิทธิภาพการดำเนินรายการอย่างแท้จริง