pypy 및 pp를 사용하는 Python 2 : 3 분 안에 n = 15
또한 단순한 무차별 대입. 흥미롭게도 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 : 멀티 코어 프로그래밍
다음 개선에 정말로 만족하지 않습니다. 또한 여러 코어에서 프로그램을 실행하려고했습니다. 내 2 + 2 코어에서 이제 n = 14약 7 분 안에 계산할 수 있습니다 . 단지 2 개선 요소입니다.
코드는이 github repo : Link 에서 사용 가능 합니다. 멀티 코어 프로그래밍은 약간 추악합니다.
편집 3 : A벡터 및 B벡터의 검색 공간 줄이기
Akuroi neko와 같은 벡터에 대해 동일한 대칭 대칭을 발견했습니다 . 아직도 이것이 왜 작동하는지 (그리고 그것이 각각 작동하는 경우) 확실하지 않습니다 n.
B벡터 의 검색 공간을 줄이는 것이 약간 더 영리합니다. 벡터 생성 ( itertools.product)을 자체 함수 로 대체했습니다 . 기본적으로 빈 목록으로 시작하여 스택에 넣습니다. 스택이 비어있을 때까지 목록과 동일한 길이가 아닌 경우 목록을 제거하고 n(-1, 0, 1을 추가하여) 3 개의 다른 목록을 생성하고 스택에 밀어 넣습니다. 목록의 길이가과 (와 n) 같 으며 합계를 평가할 수 있습니다.
이제 벡터를 직접 생성 했으므로 sum = 0에 도달 할 수 있는지 여부에 따라 필터링 할 수 있습니다. 예를 들어 내 벡터 A가 (1, 1, 1, 0, 0)이고 내 벡터가 B보이는 (1, 1, ?, ?, ?)경우 ?값으로 채울 수 없다는 것을 알고 있습니다 A*B = 0. 그래서 나는 B형태의 벡터 6 개를 모두 반복 할 필요가 없습니다 (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 :
신속하게 말한다 쿠 로이 네코의 좋은 아이디어 구현 B과 -B같은 결과를 얻을 수 있습니다. 스피드 업 x2. 그러나 구현은 빠른 해킹 일뿐입니다. n = 153 분 안에
암호:
전체 코드를 보려면 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 !!!). 병렬 python 모듈은 Python 3 용으로 포팅되지 않았습니다. 그런 다음 병렬 python 모듈 pp-1.6.4.zip 을 설치해야합니다 . cd폴더에 압축을 풀고를 호출하십시오 pypy setup.py install.
그럼 당신은 내 프로그램을 호출 할 수 있습니다
pypy you-do-the-math.py 15
CPU 수를 자동으로 결정합니다. 프로그램을 마친 후 오류 메시지가 나타날 수 있습니다. 무시하십시오. 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%
노트와 아이디어 :
- 2 개의 코어와 4 개의 스레드가있는 i7-4600m 프로세서가 있습니다. 2 개 또는 4 개의 스레드를 사용하더라도 중요하지 않습니다. CPU 사용량은 2 스레드에서는 50 %, 4 스레드에서는 100 %이지만 여전히 같은 시간이 걸립니다. 이유를 모르겠습니다. 나는 4 개의 스레드가있을 때 각 스레드가 절반의 데이터 양만 가지고 있는지 확인하고 결과를 확인했습니다 ...
- 나는 많은 목록을 사용합니다. 파이썬은 저장하는 데 효율적이지 않으므로 많은 목록을 복사해야합니다 ... 그래서 정수를 대신 사용하려고 생각했습니다. 벡터 A에서 비트 00 (0의 경우) 및 11 (1의 경우)을 사용하고 벡터 B의 비트 10 (-1의 경우), 00 (0의 경우) 및 01 (1의 경우)을 사용할 수 있습니다. A와 B의 경우,
A & B01과 10 블록 만 계산 하고 계산하면됩니다. 사이클링은 벡터를 이동하고 마스크를 사용하여 수행 할 수 있습니다 ... 실제로이 모든 것을 구현했습니다 .Github의 이전 커밋 중 일부에서 찾을 수 있습니다. 그러나 목록보다 느립니다. pypy는 실제로 목록 작업을 최적화합니다.