Pypy ve pp: n = 15 kullanarak 3 dakikada python 2
Ayrıca basit bir kaba kuvvet. İlginçti, neredeyse C ++ ile kuroi neko ile aynı hızı elde ediyorum. Kodum n = 12
yaklaşık 5 dakika içinde ulaşabilir. Ve sadece bir sanal çekirdek üzerinde çalıştırıyorum.
edit: Arama alanını bir faktör kadar azaltır n
Döngüsüz bir vektör vektörünün A*
, tekrarladığımda A
orijinal vektörle aynı sayıları (aynı sayılar) ürettiğini fark ettim . EG vektör vektörlerinin her biriyle aynı olasılıkları vardır , , , ve rasgele seçme . Bu nedenle bu 6 vektörlerin her yineleme zorunda, ama sadece yaklaşık 1 ve değiştirmeyin ile .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
count[i] += 1
count[i] += cycle_number
Bu karmaşıklığı azaltır Theta(n) = 6^n
için Theta(n) = 6^n / n
. Bu nedenle n = 13
önceki sürümümden yaklaşık 13 kat daha hızlı. n = 13
Yaklaşık 2 dakika 20 saniye içinde hesaplar . Çünkü n = 14
hala biraz fazla yavaş. Yaklaşık 13 dakika sürer.
değiştir 2: Çok çekirdekli programlama
Bir sonraki gelişmeden gerçekten memnun değil. Programımı birden çok çekirdek üzerinde yürütmeye de karar verdim. 2 + 2 çekirdeklerimde şimdi n = 14
yaklaşık 7 dakika içinde hesaplayabilirim . Sadece 2 gelişme faktörü.
Kod bu github deposunda kullanılabilir: Link . Çok çekirdekli programlama biraz çirkin.
edit 3: A
Vektörler ve B
vektörler için arama alanını azaltma
A
Kuroi neko'nun yaptığı gibi vektörler için aynı ayna simetrisini fark ettim . Hala neden işe yaradığından emin değilim (ve her biri için işe yarayıp yaramadığını n
).
B
Vektörler için arama alanının azaltılması biraz daha zekidir. Vektörlerin ( itertools.product
) neslini kendi fonksiyonuyla değiştirdim. Temel olarak, boş bir listeyle başlıyorum ve bir yığına koyuyorum. Yığın boş olana kadar, bir listeyi kaldırırım, eğer uzunluğu ile aynı değilse, n
başka 3 liste (-1, 0, 1 ekleyerek) oluşturur ve bunları yığının üzerine iterim. Bir liste ile aynı uzunlukta n
olan toplamları değerlendirebilirim.
Şimdi vektörleri kendim ürettiğime göre, toplam = 0'a ulaşıp ulaşamayacağımıza bağlı olarak bunları filtreleyebilirim. Benim vektör eğer Ör A
olduğunu (1, 1, 1, 0, 0)
ve benim vektör B
görünüyor (1, 1, ?, ?, ?)
, biliyorum, ben doldurmak olamaz ?
böylece, değerlerle A*B = 0
. Bu yüzden B
, formun bu 6 vektörünü yinelemek zorunda değilim (1, 1, ?, ?, ?)
.
Biz değerleri için, söz konusu belirtildiği gibi 1. değerlerini göz ardı edersek biz bu konuda artırabilir i = 1
dizisi olan A081671 . Bunları hesaplamanın birçok yolu vardır. Basit tekrarını seç: a(n) = (4*(2*n-1)*a(n-1) - 12*(n-1)*a(n-2)) / n
. i = 1
Temelde hiçbir zaman hesaplayamadığımız için daha fazla vektörü filtreleyebiliriz B
. Örneğin A = (0, 1, 0, 1, 1)
ve B = (1, -1, ?, ?, ?)
. İlkleri vektörleri görmezden gelebiliriz ? = 1
, çünkü A * cycled(B) > 0
tüm bu vektörler için. Umarım takip edebilirsin. Muhtemelen en iyi örnek değil.
Bununla n = 15
6 dakika içinde hesaplayabilirim .
düzenleme 4:
Çabuk, söylüyor kuroi neko harika bir fikir, hayata B
ve -B
aynı sonuçlar üretir. Hızlanma x2. Ancak uygulama sadece hızlı bir saldırıdır. n = 15
3 dakika içinde.
Kod:
Kodun tamamı için Github'u ziyaret edin . Aşağıdaki kod sadece ana özelliklerin bir gösterimidir. İthalatları, çok çekirdekli programlamayı, sonuçları yazdırmayı, ...
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
Kullanımı:
Pypy yüklemeniz gerekiyor (Python 2 için !!!). Paralel python modülü Python 3 için taşınmaz . Sonra paralel python modülünü pp-1.6.4.zip kurmanız gerekir . cd
Klasöre ayıklayın ve arayın pypy setup.py install
.
Sonra programımı
pypy you-do-the-math.py 15
Otomatik olarak cpu sayısını belirler. Programı bitirdikten sonra bazı hata mesajları olabilir, sadece dikkate almayın. n = 16
makinenizde mümkün olmalıdır.
Çıktı:
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%
Notlar ve fikirler:
- 2 çekirdekli ve 4 yivli i7-4600m işlemcim var. 2 veya 4 iş parçacığı kullanırsam önemli değil. İşlemci kullanımı 2 iş parçacığı ile% 50 ve 4 iş parçacığı ile% 100'dür, ancak yine de aynı miktarda zaman alır. Neden bilmiyorum. 4 iş parçacığı olduğunda, her iş parçacığı sadece veri yarıya kadar olduğunu kontrol ettim, sonuçları kontrol, ...
- Çok fazla liste kullanıyorum. Python depolamakta oldukça verimli değil, birçok listeyi kopyalamak zorundayım, ... Bunun yerine bir tamsayı kullanmayı düşündüm. A vektöründe 00 (0 için) ve 11 (1 için) bitlerini ve B vektöründe 10 (-1 için), 00 (0 için) ve 01 (1 için) bitlerini kullanabilirim. Ürün için A ve B, sadece
A & B
01 ve 10 blokları hesaplamak ve saymak zorunda kalacaktım . Bisiklete binme vektörü değiştirerek ve maskeleri kullanarak yapılabilir, ... Aslında tüm bunları uyguladım, Github'daki bazı eski taahhütlerimde bulabilirsiniz. Ancak, listelerden daha yavaş olduğu ortaya çıktı. Sanırım pypy liste işlemlerini gerçekten optimize ediyor.