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
벡터의 검색 공간 줄이기
A
kuroi 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 = 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 !!!). 병렬 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 & B
01과 10 블록 만 계산 하고 계산하면됩니다. 사이클링은 벡터를 이동하고 마스크를 사용하여 수행 할 수 있습니다 ... 실제로이 모든 것을 구현했습니다 .Github의 이전 커밋 중 일부에서 찾을 수 있습니다. 그러나 목록보다 느립니다. pypy는 실제로 목록 작업을 최적화합니다.