당신은 얼마나 높이 갈 수 있습니까? (코딩 + 알고리즘 도전)


34

이제 모든 사람들 이 파이썬이 얼마나 느리게 작동하는지에 대한 저급 코딩 전문 지식을 개발했습니다 . (또는 귀하의 언어는 얼마나 빠릅니까?) 그리고 파이썬이 얼마나 느리게 진행됩니까 (파트 II)? 알고리즘을 향상시킬 수있는 능력을 넓힐 수있는 도전의 시간입니다.

다음 코드는 길이 9의 목록을 계산합니다. 목록의 위치 는 및 사이의 내부 제품을 계산할 때 i최소 i연속 0이 발견 된 횟수를 계산합니다 . 바로이 작업을 수행하기 위해서는 가능한 모든 목록을 반복 할 길이 및 목록 길이 .FSFnSn+m-1

#!/usr/bin/python
import itertools
import operator

n=8
m=n+1
leadingzerocounts = [0]*m
for S in itertools.product([-1,1], repeat = n+m-1):
    for F in itertools.product([-1,1], repeat = n):
        i = 0
        while (i<m and sum(map(operator.mul, F, S[i:i+n])) == 0):
            leadingzerocounts[i] +=1
            i+=1
print leadingzerocounts

출력은

[4587520, 1254400, 347648, 95488, 27264, 9536, 4512, 2128, 1064]

이 코드를 사용하여 n을 10,12,14,16,18,20으로 늘리면 매우 빠르게 느려집니다.

규칙

  • 문제는 가능한 큰 n에 대해 올바른 출력을 제공하는 것입니다. n의 짝수 값만 관련이 있습니다.
  • 동점이 있다면, 승리는 내 컴퓨터에서 가장 큰 n에 대한 가장 빠른 코드로 진행됩니다.
  • 10 분 이상 걸리는 코드를 테스트하지 않을 권리가 있습니다.
  • 올바른 출력을 제공하는 한 원하는 방식으로 알고리즘을 변경할 수 있습니다. 사실 당신은해야 할 것이다 경력으로 어떤 점잖은 진행 상황을 확인하기 위해 알고리즘을 변경합니다.
  • 수상자에게는 질문이 설정된 후 1 주일이 수여됩니다.
  • 바운티는 기한이 지났을 때 수여되며, 이는 수상자가 수여 될 때 조금 후에 이루어집니다.

내 컴퓨터 타이밍이 내 컴퓨터에서 실행됩니다. 이것은 AMD FX-8350 8 코어 프로세서에 표준 우분투 설치입니다. 이것은 또한 코드를 실행할 수 있어야 함을 의미합니다. 결과적으로 쉽게 구할 수있는 무료 소프트웨어 만 사용하고 코드를 컴파일하고 실행하는 방법에 대한 전체 지침을 포함하십시오.

상태 .

  • C . @Fors의 n = 12 (49 초)
  • 자바 . @PeterTaylor에 의해 3:07의 n = 16
  • C ++ . @ilmale의 2:21 : n = 16
  • Rpython . @primo의 3:11 : n = 22
  • 자바 . @PeterTaylor의 6:56 : n = 22
  • 님로드 . @ReimerBehrends에 의해 9:28 초에서 n = 24

우승자는 Reimer Behrends였습니다. Nimrod에 입장했습니다!

확인으로, n = 22의 출력은이어야합니다 [12410090985684467712, 2087229562810269696, 351473149499408384, 59178309967151104, 9975110458933248, 1682628717576192, 284866824372224, 48558946385920, 8416739196928, 1518499004416, 301448822784, 71620493312, 22100246528, 8676573184, 3897278464, 1860960256, 911646720, 451520512, 224785920, 112198656, 56062720, 28031360, 14015680].


경쟁은 이제 끝났지 만 ... 점수 가 다 떨어질 때까지 n을 2 씩 (컴퓨터에서 10 분 이내) 증가시키는 모든 제출에 대해 200 포인트 를 제공 할 것입니다. 이 제안은 영원히 열려 있습니다 .


1
"몇 분 이상 걸리는 코드를 테스트하지 않을 권리가 있습니다." > 기계에 정확한 시간을 지정해야합니다. 그렇지 않으면이 질문에 객관적인 승리 기준이 없습니다.
ace_HongKongIndependence

14
나는 이러한 "내 속도 향상"과제를 좋아 합니다. 이것들과 함께 상용 제품을 만드는 경우, 빠른 제품의 지옥을 갖게 될 것이며, 또한 악한 천재 입니다.
Rainbolt

1
아마도 더 유익한 제목이 이것에 주목할 것입니까?
TheDoctor

8
우리가 이런 종류의 도전을 계속한다면 적어도 다른 문제를 해결하여 흥미를 유지하려고 노력해야한다고 생각합니다 (추가 사양으로 동일한 문제를 변형시키지 않음).
grovesNL

2
@Claudiu의 CPU에는 8 개의 물리적 코어가 있지만 페치 / 디코딩 및 FPU 장치는 코어간에 공유됩니다. 따라서 병목 현상이 해당 영역 중 하나에있을 때 쿼드 코어처럼 동작합니다. 정수 논리를 남용하고 큰 코드 크기를 피하면 8 코어와 비슷합니다.
Stefan

답변:


20

님로드 (N = 22)

import math, locks

const
  N = 20
  M = N + 1
  FSize = (1 shl N)
  FMax = FSize - 1
  SStep = 1 shl (N-1)
  numThreads = 16

type
  ZeroCounter = array[0..M-1, int]
  ComputeThread = TThread[int]

var
  leadingZeros: ZeroCounter
  lock: TLock
  innerProductTable: array[0..FMax, int8]

proc initInnerProductTable =
  for i in 0..FMax:
    innerProductTable[i] = int8(countBits32(int32(i)) - N div 2)

initInnerProductTable()

proc zeroInnerProduct(i: int): bool =
  innerProductTable[i] == 0

proc search2(lz: var ZeroCounter, s, f, i: int) =
  if zeroInnerProduct(s xor f) and i < M:
    lz[i] += 1 shl (M - i - 1)
    search2(lz, (s shr 1) + 0, f, i+1)
    search2(lz, (s shr 1) + SStep, f, i+1)

when defined(gcc):
  const
    unrollDepth = 1
else:
  const
    unrollDepth = 4

template search(lz: var ZeroCounter, s, f, i: int) =
  when i < unrollDepth:
    if zeroInnerProduct(s xor f) and i < M:
      lz[i] += 1 shl (M - i - 1)
      search(lz, (s shr 1) + 0, f, i+1)
      search(lz, (s shr 1) + SStep, f, i+1)
  else:
    search2(lz, s, f, i)

proc worker(base: int) {.thread.} =
  var lz: ZeroCounter
  for f in countup(base, FMax div 2, numThreads):
    for s in 0..FMax:
      search(lz, s, f, 0)
  acquire(lock)
  for i in 0..M-1:
    leadingZeros[i] += lz[i]*2
  release(lock)

proc main =
  var threads: array[numThreads, ComputeThread]
  for i in 0 .. numThreads-1:
    createThread(threads[i], worker, i)
  for i in 0 .. numThreads-1:
    joinThread(threads[i])

initLock(lock)
main()
echo(@leadingZeros)

와 컴파일

nimrod cc --threads:on -d:release count.nim

(Nimrod는 여기에서 다운로드 할 수 있습니다 .)

이것은 n = 20에 대해 할당 된 시간에 실행됩니다 (단일 스레드 만 사용하는 경우 n = 18에 대해서는 후자의 경우 약 2 분 소요).

이 알고리즘은 재귀 검색을 사용하여 0이 아닌 내부 제품이 발견 될 때마다 검색 트리를 제거합니다. 또한 한 쌍의 벡터에 (F, -F)대해 하나만 고려 하면된다는 사실을 관찰함으로써 검색 공간을 반으로 줄였습니다 S.

이 구현은 Nimrod의 메타 프로그래밍 기능을 사용하여 재귀 검색의 처음 몇 단계를 풀거나 인라인합니다. 이렇게하면 gcc 4.8 및 4.9를 Nimrod의 백엔드로 사용하고 상당한 양의 clang을 사용할 때 시간이 절약됩니다.

F의 선택과 짝수의 첫 번째 N 위치가 다른 S의 값만 고려하면된다는 사실을 관찰함으로써 검색 공간을 추가로 정리할 수있었습니다. 이 경우 루프 바디를 완전히 건너 뛴 경우 N

내부 제품이 0 인 곳을 표로 작성하는 것이 루프에서 비트 카운팅 기능을 사용하는 것보다 빠릅니다. 분명히 테이블에 액세스하면 꽤 좋은 지역성이 있습니다.

재귀 검색의 작동 방식을 고려할 때 문제는 동적 프로그래밍에 적합 해야하는 것처럼 보이지만 합리적인 양의 메모리로이를 수행하는 확실한 방법은 없습니다.

출력 예 :

N = 16 :

@[55276229099520, 10855179878400, 2137070108672, 420578918400, 83074121728, 16540581888, 3394347008, 739659776, 183838720, 57447424, 23398912, 10749184, 5223040, 2584896, 1291424, 645200, 322600]

N = 18 :

@[3341140958904320, 619683355033600, 115151552380928, 21392898654208, 3982886961152, 744128512000, 141108051968, 27588886528, 5800263680, 1408761856, 438001664, 174358528, 78848000, 38050816, 18762752, 9346816, 4666496, 2333248, 1166624]

N = 20 :

@[203141370301382656, 35792910586740736, 6316057966936064, 1114358247587840, 196906665902080, 34848574013440, 6211866460160, 1125329141760, 213330821120, 44175523840, 11014471680, 3520839680, 1431592960, 655872000, 317675520, 156820480, 78077440, 39005440, 19501440, 9750080, 4875040]

알고리즘을 다른 구현과 비교하기 위해 단일 스레드를 사용하는 경우 N = 16은 내 컴퓨터에서 약 7.9 초, 4 개의 코어를 사용하는 경우 2.3 초가 걸립니다.

N = 22는 Nimrod의 백엔드로 gcc 4.4.6을 사용하는 64 코어 시스템에서 약 15 분이 걸리고 64 비트 정수가 오버플로됩니다 leadingZeros[0](서명되지 않은 정수일 수 있음).


업데이트 : 몇 가지 개선 사항이 더 있습니다. 먼저의 지정된 값에 F대해 해당 S벡터 의 처음 16 개 항목은 정확히 다른 위치에서 달라야하므로 정확하게 열거 할 수 N/2있습니다. 우리는 크기의 비트 벡터의 목록을 미리 계산 그래서 NN/2비트 세트와의 초기 부분을 도출하기 위해 이러한 사용 S에서을 F.

둘째, F[N]비트 값에서 MSB가 0이므로 항상 값을 알고 있음을 관찰하여 재귀 검색을 개선 할 수 있습니다 . 이를 통해 우리는 내부 제품에서 어떤 분기로 재귀하는지 정확하게 예측할 수 있습니다. 실제로 전체 검색을 재귀 루프로 바꿀 수는 있지만 실제로 분기 예측을 약간 어렵게 만들므로 최상위 수준을 원래 형태로 유지합니다. 우리는 주로 우리가하고있는 분기의 양을 줄임으로써 여전히 시간을 절약합니다.

일부 정리를 위해 코드는 이제 부호없는 정수를 사용하고 64 비트에서 수정합니다 (누군가 32 비트 아키텍처에서이를 실행하려는 경우).

전반적인 속도 향상은 x3와 x4 사이입니다. N = 22는 여전히 10 분 미만으로 실행하려면 8 개 이상의 코어가 필요하지만 64 코어 시스템에서는 약 4 분으로 줄어 듭니다 ( numThreads그에 따라 충돌). 그래도 다른 알고리즘이 없다면 개선의 여지가 훨씬 더 없다고 생각합니다.

N = 22 :

@[12410090985684467712, 2087229562810269696, 351473149499408384, 59178309967151104, 9975110458933248, 1682628717576192, 284866824372224, 48558946385920, 8416739196928, 1518499004416, 301448822784, 71620493312, 22100246528, 8676573184, 3897278464, 1860960256, 911646720, 451520512, 224785920, 112198656, 56062720, 28031360, 14015680]

검색 공간을 더욱 줄일 수 있도록 다시 업데이트되었습니다. 쿼드 코어 머신에서 N = 22에 대해 약 9:49 분 안에 실행됩니다.

최종 업데이트 (제 생각에). F 선택에 대한 더 나은 동등성 클래스, 내 컴퓨터 에서 N = 22의 런타임을 3:19 분 57 초로 줄입니다 (편집 : 실수로 하나의 스레드로 실행했습니다).

이 변경은 벡터가 회전하여 다른 벡터로 변환 될 수있는 경우 한 쌍의 벡터가 동일한 선행 0을 생성한다는 사실을 이용합니다. 불행히도, 매우 중요한 저수준 최적화는 비트 표현에서 F의 최상위 비트가 항상 동일해야하며,이 동등성을 사용하면 검색 공간이 상당히 줄어들고 다른 상태 공간을 사용하여 런타임이 약 1/4 감소합니다. F에 대한 감소, 저수준 최적화를 상쇄하는 것 이상의 오버 헤드. 그러나, 서로 역인 F도 동등하다는 사실을 고려함으로써이 문제를 제거 할 수 있음이 밝혀졌다. 이것은 동등성 클래스 계산의 복잡성을 조금 더 추가했지만 앞서 언급 한 저수준 최적화를 유지하여 약 x3의 속도 향상을 가져 왔습니다.

누적 된 데이터에 대해 128 비트 정수를 지원하는 업데이트가 하나 더 있습니다. 128 개 비트 정수로 컴파일하려면 다음이 필요합니다 longint.nim에서 여기 와 함께 컴파일 -d:use128bit. N = 24는 여전히 10 분 이상이 걸리지 만 관심있는 사람들을 위해 아래 결과를 포함 시켰습니다.

N = 24 :

@[761152247121980686336, 122682715414070296576, 19793870419291799552, 3193295704340561920, 515628872377565184, 83289931274780672, 13484616786640896, 2191103969198080, 359662314586112, 60521536552960, 10893677035520, 2293940617216, 631498735616, 230983794688, 102068682752, 48748969984, 23993655296, 11932487680, 5955725312, 2975736832, 1487591936, 743737600, 371864192, 185931328, 92965664]

import math, locks, unsigned

when defined(use128bit):
  import longint
else:
  type int128 = uint64 # Fallback on unsupported architectures
  template toInt128(x: expr): expr = uint64(x)

const
  N = 22
  M = N + 1
  FSize = (1 shl N)
  FMax = FSize - 1
  SStep = 1 shl (N-1)
  numThreads = 16

type
  ZeroCounter = array[0..M-1, uint64]
  ZeroCounterLong = array[0..M-1, int128]
  ComputeThread = TThread[int]
  Pair = tuple[value, weight: int32]

var
  leadingZeros: ZeroCounterLong
  lock: TLock
  innerProductTable: array[0..FMax, int8]
  zeroInnerProductList = newSeq[int32]()
  equiv: array[0..FMax, int32]
  fTable = newSeq[Pair]()

proc initInnerProductTables =
  for i in 0..FMax:
    innerProductTable[i] = int8(countBits32(int32(i)) - N div 2)
    if innerProductTable[i] == 0:
      if (i and 1) == 0:
        add(zeroInnerProductList, int32(i))

initInnerProductTables()

proc ror1(x: int): int {.inline.} =
  ((x shr 1) or (x shl (N-1))) and FMax

proc initEquivClasses =
  add(fTable, (0'i32, 1'i32))
  for i in 1..FMax:
    var r = i
    var found = false
    block loop:
      for j in 0..N-1:
        for m in [0, FMax]:
          if equiv[r xor m] != 0:
            fTable[equiv[r xor m]-1].weight += 1
            found = true
            break loop
        r = ror1(r)
    if not found:
      equiv[i] = int32(len(fTable)+1)
      add(fTable, (int32(i), 1'i32))

initEquivClasses()

when defined(gcc):
  const unrollDepth = 4
else:
  const unrollDepth = 4

proc search2(lz: var ZeroCounter, s0, f, w: int) =
  var s = s0
  for i in unrollDepth..M-1:
    lz[i] = lz[i] + uint64(w)
    s = s shr 1
    case innerProductTable[s xor f]
    of 0:
      # s = s + 0
    of -1:
      s = s + SStep
    else:
      return

template search(lz: var ZeroCounter, s, f, w, i: int) =
  when i < unrollDepth:
    lz[i] = lz[i] + uint64(w)
    if i < M-1:
      let s2 = s shr 1
      case innerProductTable[s2 xor f]
      of 0:
        search(lz, s2 + 0, f, w, i+1)
      of -1:
        search(lz, s2 + SStep, f, w, i+1)
      else:
        discard
  else:
    search2(lz, s, f, w)

proc worker(base: int) {.thread.} =
  var lz: ZeroCounter
  for fi in countup(base, len(fTable)-1, numThreads):
    let (fp, w) = fTable[fi]
    let f = if (fp and (FSize div 2)) == 0: fp else: fp xor FMax
    for sp in zeroInnerProductList:
      let s = f xor sp
      search(lz, s, f, w, 0)
  acquire(lock)
  for i in 0..M-1:
    let t = lz[i].toInt128 shl (M-i).toInt128
    leadingZeros[i] = leadingZeros[i] + t
  release(lock)

proc main =
  var threads: array[numThreads, ComputeThread]
  for i in 0 .. numThreads-1:
    createThread(threads[i], worker, i)
  for i in 0 .. numThreads-1:
    joinThread(threads[i])

initLock(lock)
main()
echo(@leadingZeros)

N = 22의 결과는 12410090985684467712이며 63.42 비트를 사용하므로 부호없는 64 비트에 적합합니다.
Stefan

2
당신은 확실히 막대를 매우 인상적으로 올렸습니다.

1
님로드를 사용하는 사람을 만나서 반갑습니다. :)
cjfaure

@Stefan 어쩌면 코딩 마법사 가이 방법을 N = 22에 대해 10 분 미만으로 얻을 수 있습니까?

몇 시간 후에 종료 된 N = 22를 시도했습니다. 그러나 그것은 나에게 [-6036653088025083904, 2087229562810269696, 351473149499408384, 59178309967151104, 9975110458933248, 1682628717576192, 284866824372224, 48558946385920, 8416739196928, 1518499004416, 301448822784, 71620493312, 225602468258279를 눌렀다. 14015680]는 오버플로 오류 인 것 같습니다. 나는 nimrod를 알지 못하지만이 문제를 해결하기 위해 부호없는 정수를 사용할 수 있습니까?

11

자바 ( n=22?)

나는 n=16비슷한 접근법을 사용하는 것보다 더 나은 대답을 대부분 생각 하지만, 활용하는 대칭과 작업을 스레드간에 나누는 방식이 다릅니다.

질문에 정의 된 벡터는 비트 문자열로 대체 될 수 있으며 내부 윈도우는 겹치는 창을 XOR하고 n/2비트 세트 가 정확하게 (따라서 n/2비트가 지워짐) 확인됩니다. 비트 세트 가있는 n! / ((n/2)!)(중심 이항 계수) n비트 문자열 n/2( 균형 문자열이라고 함)이 있으므로 주어진 F값에 대해 많은 S내부 창에 제로 내부 제품이 제공됩니다. 또한 S하나를 따라 슬라이딩 하고 내부 제품이 제로가되는 들어오는 비트를 여전히 찾을 수 있는지 확인 하는 작업은 노드가 창이고 가장자리 u가 노드 v를 첫 번째 n-1비트 의 노드에 연결하는 그래프에서 가장자리를 찾는 것에 해당합니다. 마지막이야n-1비트 u.

예를 들어 다음 n=6F=001001같은 그래프를 얻습니다.

F = 001001에 대한 그래프

그리고 F=001011우리는이 그래프를 얻습니다 :

F = 001011에 대한 그래프

그런 다음 우리는 각각 계산 할 필요가 i에서 0n얼마나 많은 경로 길이의 i모든에 대한 그래프를 통해 합산,있다 F. 우리 대부분은 깊이 우선 검색을 사용하고 있다고 생각합니다.

그래프는 희소합니다. 각 노드가 최대 1도, 최대 1도를 가지고 있음을 쉽게 증명할 수 있습니다. 또한 가능한 유일한 구조는 간단한 체인과 간단한 루프입니다. 이것은 DFS를 약간 단순화합니다.

균형 잡힌 문자열은 비트 역 ( ~ALGOL 제품군의 여러 언어로 작업)과 비트 회전에서 닫히므로 두 가지 대칭을 이용 하므로 F이러한 작업과 관련된 값을 그룹화 하고 DFS 만 수행 할 수 있습니다 일단.

public class CodeGolf26459v8D implements Runnable {
    private static final int NUM_THREADS = 8;

    public static void main(String[] args) {
        v8D(22);
    }

    private static void v8D(int n) {
        int[] bk = new int[1 << n];
        int off = 0;
        for (int i = 0; i < bk.length; i++) {
            bk[i] = Integer.bitCount(i) == n/2 ? off++ : -1;
        }

        int[] fwd = new int[off];
        for (int i = 0; i < bk.length; i++) {
            if (bk[i] >= 0) fwd[bk[i]] = i;
        }

        CodeGolf26459v8D[] runners = new CodeGolf26459v8D[NUM_THREADS];
        Thread[] threads = new Thread[runners.length];
        for (int i = 0; i < runners.length; i++) {
            runners[i] = new CodeGolf26459v8D(n, i, runners.length, bk, fwd);
            threads[i] = new Thread(runners[i]);
            threads[i].start();
        }

        try {
            for (int i = 0; i < threads.length; i++) threads[i].join();
        }
        catch (InterruptedException ie) {
            throw new RuntimeException("This shouldn't be reachable", ie);
        }

        long surviving = ((long)fwd.length) << (n - 1);
        for (int i = 0; i <= n; i++) {
            for (CodeGolf26459v8D runner : runners) surviving -= runner.survival[i];
            System.out.print(i == 0 ? "[" : ", ");
            java.math.BigInteger result = new java.math.BigInteger(Long.toString(surviving));
            System.out.print(result.shiftLeft(n + 1 - i));
        }
        System.out.println("]");
    }

    public final int n;
    protected final int id;
    protected final int numRunners;
    private final int[] bk;
    private final int[] fwd;

    public long[] survival;

    public CodeGolf26459v8D(int n, int id, int numRunners, int[] bk, int[] fwd) {
        this.n = n;
        this.id = id;
        this.numRunners = numRunners;

        this.bk = bk;
        this.fwd = fwd;
    }

    private int dfs2(int[] graphShape, int flip, int i) {
        if (graphShape[i] != 0) return graphShape[i];

        int succ = flip ^ (fwd[i] << 1);
        if (succ >= bk.length) succ ^= bk.length + 1;

        int j = bk[succ];
        if (j == -1) return graphShape[i] = 1;

        graphShape[i] = n + 1; // To detect cycles
        return graphShape[i] = dfs2(graphShape, flip, j) + 1;
    }

    @Override
    public void run() {
        int n = this.n;
        int[] bk = this.bk;
        int[] fwd = this.fwd;

        // NB The initial count is approx 2^(2n - 1.33 - 0.5 lg n)
        // For n=18 we overflow 32-bit
        // 64-bit is good up to n=32.
        long[] survival = new long[n + 1];
        boolean[] visited = new boolean[1 << (n - 1)];
        int th = 0;
        for (int f = 0; f < visited.length; f++) {
            if (visited[f]) continue;

            int m = 1, g = f;
            while (true) {
                visited[g] = true;
                int ng = g << 1;
                if ((ng >> (n - 1)) != 0) ng ^= (1 << n) - 1;
                if (ng == f) break;
                m++;
                g = ng;
            }

            if (th++ % numRunners != id) continue;

            int[] graphShape = new int[fwd.length];
            int flip = (f << 1) ^ f;
            for (int i = 0; i < graphShape.length; i++) {
                int life = dfs2(graphShape, flip, i);
                if (life <= n) survival[life] += m;
            }
        }

        this.survival = survival;
    }
}

2.5GHz 코어 2에서

# n=18
$ javac CodeGolf26459v8D.java && time java CodeGolf26459v8D
[3341140958904320, 619683355033600, 115151552380928, 21392898654208, 3982886961152, 744128512000, 141108051968, 27588886528, 5800263680, 1408761856, 438001664, 174358528, 78848000, 38050816, 18762752, 9346816, 4666496, 2333248, 1166624]

real    0m3.131s
user    0m10.133s
sys     0m0.380s

# n=20
$ javac CodeGolf26459v8D.java && time java CodeGolf26459v8D
[203141370301382656, 35792910586740736, 6316057966936064, 1114358247587840, 196906665902080, 34848574013440, 6211866460160, 1125329141760, 213330821120, 44175523840, 11014471680, 3520839680, 1431592960, 655872000, 317675520, 156820480, 78077440, 39005440, 19501440, 9750080, 4875040]

real    1m8.706s
user    4m20.980s
sys     0m0.564s

# n=22
$ javac CodeGolf26459v8D.java && time java CodeGolf26459v8D
[12410090985684467712, 2087229562810269696, 351473149499408384, 59178309967151104, 9975110458933248, 1682628717576192, 284866824372224, 48558946385920, 8416739196928, 1518499004416, 301448822784, 71620493312, 22100246528, 8676573184, 3897278464, 1860960256, 911646720, 451520512, 224785920, 112198656, 56062720, 28031360, 14015680]

real    20m10.654s
user    76m53.880s
sys     0m6.852s

Lembik의 컴퓨터에는 8 개의 코어가 있고 이전의 단일 스레드 프로그램을 내 속도보다 2 배 빠르게 실행했기 n=22때문에 8 분 이내에 실행되는 것이 낙관적입니다 .


7:17! 아주 좋아요 방법을 조금 더 설명해 주시겠습니까?

6

기음

기본적으로 문제의 알고리즘을 약간 최적화 된 것입니다. n=12제한 시간 내에 관리 할 수 ​​있습니다 .

#include <stdio.h>
#include <inttypes.h>

#define n 12
#define m (n + 1)

int main() {
    int i;
    uint64_t S, F, o[m] = {0};
    for (S = 0; S < (1LLU << (n + m - 1)); S += 2)
        for (F = 0; F < (1 << (n - 1)); F++)
            for (i = 0; i < m; i++)
                if (__builtin_popcount(((S >> i) & ((1 << n) - 1)) ^ F) == n >> 1)
                    o[i] += 4;
                else
                    break;
    for (i = 0; i < m; i++)
        printf("%" PRIu64 " ", o[i]);
    return 0;
}

n=12컴파일을 포함한에 대한 테스트 실행 :

$ clang -O3 -march=native -fstrict-aliasing -ftree-vectorize -Wall fast.c
$ time ./a.out 
15502147584 3497066496 792854528 179535872 41181184 9826304 2603008 883712 381952 177920 85504 42560 21280 
real    0m53.266s
user    0m53.042s
sys     0m0.068s
$

코멘트 : 나는 방금 뇌를 켜고 간단한 조합을 사용하여 첫 번째 값이 항상임을 계산했습니다 n! / ((n / 2)!)^2 * 2^(n + m - 1). 이 문제에 대한 대수적 해결책이 있어야한다고 생각합니다.


이것을 컴파일 할 때 많은 경고가 발생합니다. gcc가 -Wall -Wextra Fors.c -o하기 Fors 시도

이전 반복에서 잊어 버린 두 개의 사용되지 않은 변수가 있었지만 적어도 두 개의 경고가 사라 졌으므로 제거했습니다. 현재 GCC를 사용할 수 없으며 (Clang 만 해당) Clang은 (사용하지 않는 변수를 제거한 후) 현재 경고를 표시하지 않습니다. 그리고 경고에 관해서는 Clang이 일반적으로 더 엄격하기 때문에 나는 경고가 있다고 조금 놀랐습니다.
for

Fors.c : 13 : 17에 대해 불평합니다 : 경고 : '&'[-Wparentheses] (두 번)의 피연산자에서 '-'주위에 괄호를 제안하고 경고 : 형식 '% llu'는 'long long unsigned int 유형의 인수를 예상합니다 '이지만 인수 2의 유형은'uint64_t '[-Wformat =]입니다. 실제로 clang은 나에게도 printf 문에 대해 불평합니다.

최신 변경 사항으로 GCC는 경고 메시지를 표시하지 않아야합니다.
Fors

그것은 여전히 ​​Fors.c : 13 : 49에 대해 불평합니다 : 경고 : '^'의 피연산자에서 산술 주위의 괄호를 제안하십시오. [-Wparentheses] 그러나 나쁜 소식은 ... 내 컴퓨터에서 10 분 이상 걸립니다.

5

자바, n=16

임의의 주어진 값에 대해 F존재 \binom{n}{n/2}함께 제로 내적이 벡터. 따라서 정점이 벡터와 일치하고 모서리가의 이동에 해당하는 그래프를 만들 수 있습니다. 그래서 그래프 S에서 길이의 경로를 세면됩니다 n.

조건부를 비트 단위 연산으로 대체하여 미세 최적화를 시도하지는 않았지만 n실행 시간이 각각 두 배씩 증가 하면 약 16 배가 증가하므로 임계 값에 가깝지 않으면 차이를 충분히 만들지 못합니다. 내 컴퓨터에서는 그렇지 않습니다.

public class CodeGolf26459 {

    public static void main(String[] args) {
        v3(16);
    }

    // Order of 2^(2n-1) * n ops
    private static void v3(int n) {
        long[] counts = new long[n+1];
        int mask = (1 << n) - 1;
        for (int f = 0; f < (1 << (n-1)); f++) {
            // Find adjacencies
            long[] subcounts = new long[1 << n];
            for (int g = 0; g < (1 << n); g++) {
                subcounts[g] = Integer.bitCount(f ^ g) == n/2 ? 2 : -1;
            }

            for (int round = 0; round <= n; round++) {
                long count = 0;
                // Extend one bit.
                long[] next = new long[1 << n];
                for (int i = 0; i < (1 << n); i++) {
                    long s = subcounts[i];
                    if (s == -1) next[i] = -1;
                    else {
                        count += s;
                        int j = (i << 1) & mask;
                        if (subcounts[j] >= 0) next[j] += s;
                        if (subcounts[j + 1] >= 0) next[j + 1] += s;
                    }
                }
                counts[round] += count << (n - round);
                subcounts = next;
            }
        }

        System.out.print("[");
        for (long count : counts) System.out.print(count+", ");
        System.out.println("]");
    }
}

2.5GHz 코어 2에서

$ javac CodeGolf26459.java && time java -server CodeGolf26459 
[55276229099520, 10855179878400, 2137070108672, 420578918400, 83074121728, 16540581888, 3394347008, 739659776, 183838720, 57447424, 23398912, 10749184, 5223040, 2584896, 1291424, 645200, 322600, ]

real    6m2.663s
user    6m4.631s
sys     0m1.580s

지금 내 솔루션을 구현하고 싶지 않기 때문에 피기 백. 각 정점에는 최대 하나의 후속 작업이 있으므로 실제로 배열이 필요하지 않습니다. f정점 조합 과 시작 정점을 효율적으로 반복하려면 f_xor_g정확하게 n/2설정된 비트를 사용하여 전체 를 반복하십시오 . 이들 각각에 대해 전체를 반복하고을 f가져 옵니다 g = f ^ f_xor_g.
David Eisenstat

@David, 나는 알고 있으며 내 버전 7은 1 분 안에 Atom 넷북에서 n = 18을 수행하지만 휴가에서 돌아올 때까지 게시 할 수 없습니다.
피터 테일러

4

RPython, N = 22 ~ 3 : 23

스택리스 재귀 강하를 사용하는 멀티 스레드. 프로그램은 N과 작업자 스레드 수라는 두 가지 명령 줄 인수를 허용합니다.

from time import sleep

from rpython.rlib.rthread import start_new_thread, allocate_lock
from rpython.rlib.rarithmetic import r_int64, build_int, widen
from rpython.rlib.rbigint import rbigint

r_int8 = build_int('r_char', True, 8)

class ThreadEnv:
  __slots__ = ['n', 'counts', 'num_threads',
               'v_range', 'v_num', 'running', 'lock']

  def __init__(self):
    self.n = 0
    self.counts = [rbigint.fromint(0)]
    self.num_threads = 0
    self.v_range = [0]
    self.v_num = 0
    self.running = 0
    self.lock = None

env = ThreadEnv()

bt_bits = 12
bt_mask = (1<<bt_bits)-1
# computed compile time
bit_table = [r_int8(0)]
for i in xrange(1,1<<bt_bits):
  bit_table += [((i&1)<<1) + bit_table[i>>1]]

def main(argv):
  argc = len(argv)
  if argc < 2 or argc > 3:
    print 'Usage: %s N [NUM_THREADS=2]'%argv[0]
    return 1

  if argc == 3:
    env.num_threads = int(argv[2])
  else:
    env.num_threads = 2

  env.n = int(argv[1])
  env.counts = [rbigint.fromint(0)]*env.n
  env.lock = allocate_lock()

  v_range = []
  v_max = 1<<(env.n-1)
  v_num = 0
  v = (1<<(env.n>>1))-1
  while v < v_max:
    v_num += 1
    v_range += [v]
    if v&1:
      # special case odd v
      s = (v+1)&-v
      v ^= s|(s>>1)
    else:
      s = v&-v
      r = v+s
      # s is at least 2, skip two iterations
      i = 3
      s >>= 2
      while s:
        i += 1
        s >>= 1
      v = r|((v^r)>>i)
  env.v_range = v_range
  env.v_num = v_num

  for i in xrange(env.num_threads-1):
    start_new_thread(run,())

  # use the main process as a worker
  run()

  # wait for any laggers
  while env.running:
    sleep(0.05)

  result = []
  for i in range(env.n):
    result += [env.counts[i].lshift(env.n-i+3).str()]
  result += [env.counts[env.n-1].lshift(3).str()]
  print result
  return 0

def run():
  with env.lock:
    v_start = env.running
    env.running += 1

  n = env.n
  counts = [r_int64(0)]*n
  mask = (1<<n)-1
  v_range = env.v_range
  v_num = env.v_num
  z_count = 1<<(n-2)

  for i in xrange(v_start, v_num, env.num_threads):
    v = v_range[i]
    counts[0] += z_count
    counts[1] += v_num
    r = v^(v<<1)
    for w in v_range:
      # unroll counts[2] for speed
      # ideally, we could loop over x directly,
      # rather than over all v, only to throw the majority away
      # there's a 2x-3x speed improvement to be had here...
      x = w^r
      if widen(bit_table[x>>bt_bits]) + widen(bit_table[x&bt_mask]) == n:
        counts[2] += 1
        x, y = v, x
        o, k = 2, 3
        while k < n:
          # x = F ^ S
          # y = F ^ (S<<1)
          o = k
          z = (((x^y)<<1)^y)&mask
          # z is now F ^ (S<<2), possibly xor 1
          # what S and F actually are is of no consequence

          # the compiler hint `widen` let's the translator know
          # to store the result as a native int, rather than a signed char
          bt_high = widen(bit_table[z>>bt_bits])
          if bt_high + widen(bit_table[z&bt_mask]) == n:
            counts[k] += 1
            x, y = y, z
            k += 1
          elif bt_high + widen(bit_table[(z^1)&bt_mask]) == n:
            counts[k] += 1
            x, y = y, z^1
            k += 1
          else: k = n

  with env.lock:
    for i in xrange(n):
      env.counts[i] = env.counts[i].add(rbigint.fromrarith_int(counts[i]))
    env.running -= 1

def target(*args):
  return main, None

컴파일하기

수은, 자식 또는 원하는 것을 사용하여 PyPy 저장소 의 로컬 복제본을 만듭니다 . 다음 스크립트를 입력하십시오 (위의 스크립트 이름이이라고 가정 convolution-high.py).

$ pypy %PYPY_REPO%/rpython/bin/rpython --thread convolution-high.py

여기서 %PYPY_REPO%방금 복제 한 저장소를 가리키는 환경 변수를 나타냅니다. 컴파일에는 약 1 분이 걸립니다.


샘플 타이밍

N = 16, 4 스레드 :

$ timeit convolution-high-c 16 4
[55276229099520, 10855179878400, 2137070108672, 420578918400, 83074121728, 16540581888, 3394347008, 739659776, 183838720, 57447424, 23398912, 10749184, 5223040, 2584896, 1291424, 645200, 322600]
Elapsed Time:     0:00:00.109
Process Time:     0:00:00.390

N = 18, 4 실 :

$ timeit convolution-high-c 18 4
[3341140958904320, 619683355033600, 115151552380928, 21392898654208, 3982886961152, 744128512000, 141108051968, 27588886528, 5800263680, 1408761856, 438001664, 174358528, 78848000, 38050816, 18762752, 9346816, 4666496, 2333248, 1166624]
Elapsed Time:     0:00:01.250
Process Time:     0:00:04.937

N = 20, 4 스레드 :

$ timeit convolution-high-c 20 4
[203141370301382656, 35792910586740736, 6316057966936064, 1114358247587840, 196906665902080, 34848574013440, 6211866460160, 1125329141760, 213330821120, 44175523840, 11014471680, 3520839680, 1431592960, 655872000, 317675520, 156820480, 78077440, 39005440, 19501440, 9750080, 4875040]
Elapsed Time:     0:00:15.531
Process Time:     0:01:01.328

N = 22, 4 스레드 :

$ timeit convolution-high-c 22 4
[12410090985684467712, 2087229562810269696, 351473149499408384, 59178309967151104, 9975110458933248, 1682628717576192, 284866824372224, 48558946385920, 8416739196928, 1518499004416, 301448822784, 71620493312, 22100246528, 8676573184, 3897278464, 1860960256, 911646720, 451520512, 224785920, 112198656, 56062720, 28031360, 14015680]
Elapsed Time:     0:03:23.156
Process Time:     0:13:25.437

9:26. 22 승무원에 오신 것을 환영합니다 :)

왜 그런지 잘 모르겠지만 새 버전이 더 빠르지 않습니다. 내가 시간을 할 때 아직도 약 9:30 ./primo-c 22 8.

@Lembik은 분할이 평균 3 개의 오른쪽 이동만큼 평균적으로 빠르면 의미가 있습니다 (3 = Sum {(n + 1) / (2 ^ n)}, n = 1..infty). certian 아키텍처의 경우에는 그럴 수 있지만 내 부서에서는 눈에 띄게 느립니다. 시간을내어 시험해 주셔서 감사합니다 :)
primo

3

파이썬 3.3, N = 20, 3.5 분

면책 조항 : 내가 사용하는 알고리즘은 primo의 RPython 솔루션 에서 뻔뻔한 포트 일 뿐이므로 이것을 내 자신의 답변으로 게시 하지 않을 의도입니다 . 여기서의 목적은 NumpyNumba 모듈 의 마법을 결합한 경우 Python에서 수행 할 수있는 작업 만 보여주는 것 입니다.

Numba는 간단히 설명했습니다.

Numba는 주석이 달린 Python 및 NumPy 코드를 데코레이터를 통해 LLVM으로 컴파일하는 JIT (Just-In-Time) 전문 컴파일러입니다. http://numba.pydata.org/

업데이트 1 : 주위 숫자를 던진 후에 일부 숫자를 완전히 건너 뛸 수 있음을 알았습니다. 이제 maxf(1 << n) // 2가 되고 maxsmaxf 2 **가됩니다. 이렇게하면 프로세스 속도가 약간 빨라집니다. n = 16은 이제 ~ 48 초만 소요됩니다 (4,5 분에서 감소). 또한 시도해보고 조금 더 빨리 갈 수 있는지 알아볼 또 다른 아이디어가 있습니다.

업데이트 2 : 변경된 알고리즘 (프리모 솔루션). 내 포트는 아직 멀티 스레딩을 지원하지 않지만 추가하기는 쉽지 않습니다. Numba 및 ctypes를 사용하여 CPython GIL을 릴리스 할 수도 있습니다. 그러나이 솔루션은 단일 코어에서도 매우 빠르게 실행됩니다!

import numpy as np
import numba as nb

bt_bits = 11
bt_mask = (1 << bt_bits) - 1
bit_table = np.zeros(1 << bt_bits, np.int32)

for i in range(0, 1 << bt_bits):
    bit_table[i] = ((i & 1) << 1) + bit_table[i >> 1]

@nb.njit("void(int32, int32, int32, int32, int64[:], int64[:])")
def run(n, m, start, re, counts, result):
    mask = (1 << n) - 1

    v_max = (1 << n) // 2
    rr = v_max // 2

    v = (1 << (n >> 1)) - 1
    while v < v_max:
        s = start

        while s < rr:
            f = v ^ s
            counts[0] += 8
            t = s << 1
            o, j = 0, 1

            while o < j and j < m:
                o = j
                w = (t ^ f) & mask
                bt_high = bit_table[w >> bt_bits]

                if bt_high + bit_table[w & bt_mask] == n:
                    counts[j] += 8
                    t <<= 1
                    j += 1
                elif bt_high + bit_table[(w ^ 1) & bt_mask] == n:
                    counts[j] += 8
                    t = (t | 1) << 1
                    j += 1
                    s += re

            s = v & -v
            r = v + s
            o = v ^ r
            o = (o >> 2) // s
            v = r | o

    for e in range(m):
        result[e] += counts[e] << (n - e)

그리고 마지막으로:

if __name__ == "__main__":
    n = 20
    m = n + 1

    result = np.zeros(m, np.int64)
    counts = np.zeros(m, np.int64)

    s1 = time.time() * 1000
    run(n, m, 0, 1, counts, result)
    s2 = time.time() * 1000

    print(result)
    print("{0}ms".format(s2 - s1))

이것은 212688ms 또는 ~ 3.5 분 안에 내 컴퓨터에서 실행됩니다.


감사. 이제 n = 18은 어떻습니까? :)

n = 18을 사용하여 프로그램을 시작한 이래로 거의 20 분이 지났습니다.이 특정 알고리즘을 사용하여 Numba에서도 시간이 지나도 파이썬이이 문제를 해결할 수 없다고 말하는 것이 안전하다고 생각합니다.
Anna Jokela

더 나은 알고리즘이 존재한다고 낙관합니다.

pip install numba를 시도했지만 llvmpy를 찾을 수 없다고 말합니다. sudo pip install llvmpy를 시도했지만 버전 관리자를 찾을 수 없다고 말합니다. sudo pip install versioneer를 사용해 보았지만 이미 가지고 있다고 말합니다.

아직 numba가 작동하지는 않지만 (결국 아나콘다를 설치해야한다고 생각합니다) 나는 이것에 깊은 인상을 받았습니다. 문제는 nimrod와 비슷한 방법을 사용하여 N = 22를 풀 수 있습니까?

2

C ++ N = 16

원자가있는 EEEPC를 테스트하고 있습니다. 제 시간은 말이되지 않습니다. : D
원자는 34 초 안에 n = 14를 풉니 다. 그리고 20 분에 n = 16이다. OP pc에서 n = 16을 테스트하고 싶습니다. 나는 낙관적입니다.

아이디어는 주어진 F에 대한 해를 찾을 때마다 2 ^ i 해를 찾았다는 것입니다. 왜냐하면 S의 하단 부분을 변경하여 동일한 결과를 얻을 수 있기 때문입니다.

#include <stdio.h>
#include <cinttypes>
#include <cstring>

int main()
{
   const int n = 16;
   const int m = n + 1;
   const uint64_t maxS = 1ULL << (2*n);
   const uint64_t maxF = 1ULL << n;
   const uint64_t mask = (1ULL << n)-1;
   uint64_t out[m]={0};
   uint64_t temp[m] = {0};
   for( uint64_t F = 0; F < maxF; ++F )
   {
      for( uint64_t S = 0; S < maxS; ++S )
      {
         int numSolution = 1;
         for( int i = n; i >= 0; --i )
         {
            const uint64_t window = S >> i;
            if( __builtin_popcount( mask & (window ^ F) ) == (n / 2) )
            {
               temp[i] += 1;
            } else {
               numSolution = 1 << i;
               S += numSolution - 1;
               break;
            }
         }
         for( int i = n; i >= 0; --i )
         {
            out[i] += temp[i]*numSolution;
            temp[i] = 0;
         }
      }
   }
   for( int i = n; i >= 0; --i )
   {
      uint64_t x = out[i];
      printf( "%lu ", x );
   }
   return 0;
}

컴파일하기:

gcc 26459.cpp -std = c ++ 11 -O3 -march = 네이티브 -falias-aliasing -ftree-vectorize-벽 -pedantic -o 26459


1
대단하다. 나는 실제로 더 큰 n에 대해 그것을 해결하는 방법에 대한 반 구운 아이디어를 가지고 있습니다. 당신이 그들을 듣고 싶거나 경쟁을 망칠 수 있습니까?

2

자바 n : 12

내 컴퓨터에서는 231.242 초가 걸렸습니다. 데모에서는 브라우저 작동을 방지하기 위해 웹 워커를 사용하고 있습니다. 병행 작업자의 경우 이러한 확신을 더욱 향상시킬 수 있습니다. JS가이 도전에서 우연이 아니라는 것을 알고 있지만 재미있게 해냈습니다!

온라인 데모를 실행하려면 클릭

var n = 8;        
var m = n + 1;
var o = [];
var popCount = function(bits) {
  var SK5  = 0x55555555,
      SK3  = 0x33333333,
      SKF0 = 0x0f0f0f0f,
      SKFF = 0xff00ff;

  bits -= (bits >> 1) & SK5;
  bits  = (bits & SK3) + ((bits >> 2) & SK3);
  bits  = (bits & SKF0) + ((bits >> 4) & SKF0);
  bits += bits >> 8;

  return (bits + (bits >> 15)) & 63;
};
for(var S = 0; S < (1 << n + m - 1); S += 2){
  for(var F = 0; F < (1 << n - 1); F += 1){
    for (var i = 0; i < m; i++){
      var c = popCount(((S >> i) & ((1 << n) - 1)) ^ F);
      if(c == n >> 1){
        if(!o[i]) o[i] = 0;
        o[i] += 4;
      } else break;
    }
  }
}
return o;

새로운 고속 자바 스크립트 엔진 중 하나는 어떻습니까? 그것들을 사용할 수 있습니까?

당신은 다트 같은 것을 의미 합니까?
rafaelcastrocouto

1
실제로 나는 틀렸다. 파이어 폭스와 크롬을 모두 시도해보십시오. asm.js로 작성하고 싶지 않다면 :)

1
도전은 받아 들일 것이다…
rafaelcastrocouto

1
이것을 시도하고 n=22 [235388928,86292480,19031048,5020640,1657928,783920,545408,481256,463832,460256,459744,459744,459744,459744,459744,459744,459744,459744,459744,459744,459744,459744] i.imgur.com/FIJa2Ch.png
Spedwards

1

포트란 : n = 12

방금 포트란에서 빠른 버전을 만들었고 OpenMP 이외의 최적화는 없었습니다. OP 기기에서 n = 12 인 경우 10 분 미만으로 꽉 쥐어 야합니다. 내 컴퓨터에서는 10:39가 걸리므로 속도가 느립니다.

64 비트 정수는 실제로 성능에 부정적인 영향을 미칩니다. 이 알고리즘이 훨씬 빠르기 위해서는 전체 알고리즘을 다시 생각해야 할 것 같습니다. 내가 귀찮게할지 모르겠다. 오히려 내 자신의 취향에 더 좋은 도전을 생각하는 데 시간을 할애한다고 생각한다. 다른 사람이 이것을 가지고 그것을 가지고 가고 싶다면, 계속하십시오 :)

program golf
use iso_fortran_env
implicit none
integer, parameter ::  n=12
integer :: F(n), S(2*n)
integer(int64) :: leadingzerocounts(n+1)
integer :: k
integer(int64) :: i,j,bindec,enc

leadingzerocounts=0

!$OMP parallel do private(i,enc,j,bindec,S,F,k) reduction(+:leadingzerocounts) schedule(dynamic)
do i=0,2**(2*n)-1
  enc=i
  ! Short loop to convert i into the array S with -1s and 1s
  do j=2*n,1,-1
    bindec=2**(j-1)
    if (enc-bindec .ge. 0) then
      S(j)=1
      enc=enc-bindec
    else
      S(j)=-1
    endif
  end do
  do j=0,2**(n)-1
    ! Convert j into the array F with -1s and 1s
    enc=j
    do k=n,1,-1
      bindec=2**(k-1)
      if (enc-bindec .ge. 0) then
        F(k)=1
        enc=enc-bindec
      else
        F(k)=-1
      endif
    end do
    ! Compute dot product   
    do k=1,n+1
      if (dot_product(F,S(k:k+n-1)) /= 0) exit
      leadingzerocounts(k)=leadingzerocounts(k)+1
    end do
  end do
end do
!$OMP end parallel do

print *, leadingzerocounts

end

1

루아 : n = 16

면책 조항 : 내가 사용하는 알고리즘이 Anna Jokela의 영리한 답변 에서 훔치지 않았기 때문에 내 의도는 이것을 내 자신의 답변으로 게시하지 않을 입니다. ilmale의 영리한 답변 에서 뻔뻔스럽게 도난당했습니다 .

게다가, 그것은 심지어 유효하지 않습니다-그것은 부동 소수점 숫자로 인한 부정확성을 가지고 있습니다 (Lua가 64 비트 정수를 지원한다면 더 좋을 것입니다). 그러나이 솔루션의 속도를 보여주기 위해 여전히 업로드 중입니다. 동적 프로그래밍 언어이지만 합리적인 시간 (800MHz CPU에서 1 분)으로 n = 16을 계산할 수 있습니다.

LuaJIT으로 실행하면 표준 인터프리터가 너무 느립니다.

local bit = require "bit"
local band = bit.band
local bor = bit.bor
local bxor = bit.bxor
local lshift = bit.lshift
local rshift = bit.rshift

-- http://stackoverflow.com/a/11283689/736054
local function pop_count(w)
    local b1 = 1431655765
    local b2 = 858993459
    local b3 = 252645135
    local b7 = 63

    w = band(rshift(w, 1), b1) + band(w, b1)
    w = band(rshift(w, 2), b2) + band(w, b2)
    w = band(w + rshift(w, 4), b3)
    return band(rshift(w, 24) + rshift(w, 16) + rshift(w, 8) + w, b7)
end

local function gen_array(n, value)
    value = value or 0
    array = {}
    for i = 1, n do
        array[i] = value
    end
    return array
end

local n = 16
local u = math.floor(n / 2)
local m = n + 1
local maxf = math.floor(lshift(1, n) / 2)
local maxs = maxf ^ 2
local mask = lshift(1, n) - 1

local out = gen_array(m, 0)
local temp = gen_array(m, 0)


for f = 0, maxf do
    local s = 0
    while s <= maxs do
        local num_solution = 1

        for i = n, 0, -1 do
            if pop_count(band(mask, bxor(rshift(s, i), f))) == u then
                temp[i + 1] = temp[i + 1] + 8
            else
                num_solution = lshift(1, i)
                s = s + num_solution - 1
                break
            end
        end

        for i = 1, m do
            out[i] = out[i] + temp[i] * num_solution
            temp[i] = 0
        end

        s = s + 1
    end
end

for i = m, 1, -1 do
    print(out[i])
end

고맙습니다. 최근 루아 버전은 long long int를 사용한다고 생각합니다 .64 비트 시스템에서 64 비트이어야합니다. 에서 "lua_integer"을 참조하십시오 lua.org/work/doc/manual.html .

@Lembik : 흥미 롭습니다. 어느 쪽이든, 그것은 LuaJIT 가 long long아니라 표준 Lua입니다 ( double컴파일 설정 대신 이미 지원 합니다).
Konrad Borowski

나는 어쨌든 lurtit으로 잘못되었다고 생각합니다. 존재하지 않는 5.3이 필요합니다. 루아 사람들이 줄 수있는 가장 좋은 조언은 "5.33 workx"입니다.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.