Làm thế nào cao, bạn có thể đi? (Thử thách mã hóa + thuật toán)


34

Bây giờ mọi người đã phát triển chuyên môn mã hóa cấp thấp (thường là tuyệt vời) của họ cho Python thực sự chậm như thế nào? (Hoặc ngôn ngữ của bạn nhanh như thế nào?)Python thực sự chậm như thế nào (Phần II)? đã đến lúc thử thách cũng sẽ kéo dài khả năng cải thiện thuật toán của bạn.

Đoạn mã sau tính toán một danh sách có độ dài 9. Vị trí itrong danh sách đếm số lần ít nhất các isố 0 liên tiếp được tìm thấy khi tính toán các sản phẩm bên trong giữa FS. Để làm điều này một cách chính xác, nó lặp đi lặp lại trên tất cả các danh sách có thể Fvề chiều dài nvà danh sách Sđộ dài n+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

Đầu ra là

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

Nếu bạn tăng n lên 10,12,14,16,18,20 bằng cách sử dụng mã này, nó sẽ rất nhanh trở nên quá chậm.

Quy tắc

  • Thách thức là đưa ra đầu ra chính xác cho càng lớn n càng tốt. Chỉ các giá trị của n là có liên quan.
  • Nếu có hòa, phần thắng sẽ thuộc về mã nhanh nhất trên máy của tôi cho số n lớn nhất.
  • Tôi bảo lưu quyền không kiểm tra mã mất hơn 10 phút.
  • Bạn có thể thay đổi thuật toán theo bất kỳ cách nào bạn muốn miễn là nó cho đầu ra chính xác. Trong thực tế, bạn sẽ phải thay đổi thuật toán để đạt được bất kỳ tiến bộ tốt nào để giành chiến thắng.
  • Người chiến thắng sẽ được trao một tuần kể từ khi câu hỏi được đặt ra.
  • Tiền thưởng sẽ được trao khi đến hạn, một chút sau khi người chiến thắng sẽ được trao.

Máy của tôi Thời gian sẽ được chạy trên máy của tôi. Đây là bản cài đặt Ubuntu tiêu chuẩn trên Bộ xử lý tám lõi AMD FX-8350. Điều này cũng có nghĩa là tôi cần để có thể chạy mã của bạn. Do đó, chỉ sử dụng phần mềm miễn phí có sẵn dễ dàng và vui lòng bao gồm các hướng dẫn đầy đủ về cách biên dịch và chạy mã của bạn.

Tình trạng .

  • C . n = 12 trong 49 giây bởi @Fors
  • Java . n = 16 trong 3:07 bởi @PeterTaylor
  • C ++ . n = 16 trong 2:21 bởi @ilmale
  • Rpython . n = 22 trong 3:11 bởi @primo
  • Java . n = 22 vào 6:56 bởi @PeterTaylor
  • Nimrod . n = 24 trong 9:28 giây bởi @ReimerBehrends

Người chiến thắng là Reimer Behrends với một mục trong Nimrod!

Khi kiểm tra, đầu ra cho n = 22 phải là [12410090985684467712, 2087229562810269696, 351473149499408384, 59178309967151104, 9975110458933248, 1682628717576192, 284866824372224, 48558946385920, 8416739196928, 1518499004416, 301448822784, 71620493312, 22100246528, 8676573184, 3897278464, 1860960256, 911646720, 451520512, 224785920, 112198656, 56062720, 28031360, 14015680].


Cuộc thi đã kết thúc nhưng ... tôi sẽ cung cấp 200 điểm cho mỗi lần gửi tăng 2 lần (trong vòng 10 phút trên máy tính của tôi) cho đến khi tôi hết điểm. Ưu đãi này được mở mãi mãi .


1
"Tôi bảo lưu quyền không kiểm tra mã mất hơn một vài phút." > Bạn nên chỉ định thời gian chính xác trên máy của mình, nếu không câu hỏi này sẽ thiếu tiêu chí chiến thắng khách quan.
pastebin.com chém 0mr8spkT

14
Tôi thích những thử thách "tăng tốc độ của tôi". Nếu bạn đang xây dựng một sản phẩm thương mại với những sản phẩm này, bạn sẽ có một địa ngục của một sản phẩm nhanh, và bạn cũng là một thiên tài xấu xa .
Rainbolt

1
Có lẽ một tiêu đề nhiều thông tin hơn sẽ thu hút sự chú ý về điều này?
TheDoctor

8
Nếu chúng ta tiếp tục thực hiện loại thử thách này, tôi nghĩ ít nhất chúng ta nên cố gắng giải quyết một vấn đề khác để giữ cho nó thú vị (không phải là một biến thể của cùng một vấn đề với thông số kỹ thuật bổ sung).
GrovesNL

2
@Claudiu CPU của anh ta có 8 lõi vật lý, nhưng các đơn vị tìm nạp / giải mã và FPU được chia sẻ giữa các lõi. Vì vậy, khi nút cổ chai nằm trên một trong những khu vực đó, nó hoạt động giống như một hình tứ giác. Lạm dụng logic số nguyên và tránh các kích thước mã lớn và nó giống như một lõi 8.
Stefan

Câu trả lời:


20

Nimrod (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)

Biên dịch với

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

(Nimrod có thể được tải xuống ở đây .)

Điều này chạy trong thời gian quy định cho n = 20 (và cho n = 18 khi chỉ sử dụng một luồng duy nhất, mất khoảng 2 phút trong trường hợp sau).

Thuật toán sử dụng tìm kiếm đệ quy, cắt tỉa cây tìm kiếm bất cứ khi nào gặp sản phẩm bên trong khác không. Chúng tôi cũng cắt giảm một nửa không gian tìm kiếm bằng cách quan sát rằng đối với bất kỳ cặp vectơ nào, (F, -F)chúng tôi chỉ cần xem xét một vì các cái khác tạo ra cùng một bộ sản phẩm bên trong chính xác (bằng cách phủ địnhS cũng).

Việc triển khai sử dụng các phương tiện siêu lập trình của Nimrod để hủy đăng ký / nội tuyến trong một vài cấp độ đầu tiên của tìm kiếm đệ quy. Điều này giúp tiết kiệm một ít thời gian khi sử dụng gcc 4.8 và 4.9 làm phần phụ trợ của Nimrod và một số tiền hợp lý cho tiếng kêu.

Không gian tìm kiếm có thể được cắt tỉa thêm bằng cách quan sát rằng chúng ta chỉ cần xem xét các giá trị của S khác nhau về số lượng vị trí N đầu tiên so với lựa chọn F. Tuy nhiên, sự phức tạp hoặc nhu cầu bộ nhớ của nó không mở rộng cho các giá trị lớn của N, cho rằng thân vòng lặp hoàn toàn bị bỏ qua trong những trường hợp đó.

Việc lập bảng trong đó sản phẩm bên trong bằng 0 dường như nhanh hơn việc sử dụng bất kỳ chức năng đếm bit nào trong vòng lặp. Rõ ràng truy cập vào bảng có địa phương khá tốt.

Có vẻ như vấn đề cần phải tuân theo đối với lập trình động, xem xét cách tìm kiếm đệ quy hoạt động, nhưng không có cách nào rõ ràng để làm điều đó với một lượng bộ nhớ hợp lý.

Kết quả ví dụ:

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]

Đối với mục đích so sánh thuật toán với các triển khai khác, N = 16 mất khoảng 7,9 giây trên máy của tôi khi sử dụng một luồng và 2,3 giây khi sử dụng bốn lõi.

N = 22 mất khoảng 15 phút trên máy 64 lõi với gcc 4.4.6 làm phụ trợ của Nimrod và tràn số nguyên 64 bit vào leadingZeros[0](có thể không phải là không dấu, không nhìn vào nó).


Cập nhật: Tôi đã tìm thấy phòng cho một vài cải tiến. Đầu tiên, với một giá trị nhất định F, chúng ta có thể liệt kê Schính xác 16 mục nhập của các vectơ tương ứng , vì chúng phải khác nhau ở N/2những vị trí chính xác . Vì vậy, chúng tôi tính toán trước một danh sách các vectơ bit có kích thước NN/2các bit được đặt và sử dụng chúng để lấy phần ban đầu Stừ F.

Thứ hai, chúng ta có thể cải thiện tìm kiếm đệ quy bằng cách quan sát rằng chúng ta luôn biết giá trị của F[N](vì MSB bằng 0 trong biểu diễn bit). Điều này cho phép chúng tôi dự đoán chính xác nhánh nào chúng tôi thu hồi từ sản phẩm bên trong. Mặc dù điều đó thực sự sẽ cho phép chúng ta biến toàn bộ tìm kiếm thành một vòng lặp đệ quy, nhưng điều đó thực sự xảy ra để làm hỏng dự đoán nhánh khá nhiều, vì vậy chúng tôi giữ các mức cao nhất ở dạng ban đầu. Chúng tôi vẫn tiết kiệm thời gian, chủ yếu bằng cách giảm số lượng phân nhánh chúng tôi đang làm.

Đối với một số dọn dẹp, mã hiện đang sử dụng các số nguyên không dấu và sửa chúng ở 64 bit (chỉ trong trường hợp ai đó muốn chạy mã này trên kiến ​​trúc 32 bit).

Tăng tốc tổng thể là giữa một yếu tố của x3 và x4. N = 22 vẫn cần nhiều hơn tám lõi để chạy trong vòng dưới 10 phút, nhưng trên máy 64 lõi, giờ chỉ còn khoảng bốn phút (vớinumThreads va chạm tương ứng). Tuy nhiên, tôi không nghĩ có nhiều chỗ để cải tiến hơn nếu không có thuật toán khác.

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]

Cập nhật lại, sử dụng các mức giảm có thể hơn nữa trong không gian tìm kiếm. Chạy trong khoảng 9:49 phút cho N = 22 trên máy quadcore của tôi.

Cập nhật cuối cùng (tôi nghĩ). Các lớp tương đương tốt hơn cho các lựa chọn F, cắt thời gian chạy cho N = 22 xuống còn 3:19 phút 57 giây (chỉnh sửa: Tôi đã vô tình chạy nó chỉ với một luồng) trên máy của mình.

Sự thay đổi này sử dụng thực tế là một cặp vectơ tạo ra các số 0 đứng đầu giống nhau nếu có thể biến đổi thành một số khác bằng cách xoay nó. Thật không may, tối ưu hóa mức độ khá quan trọng đòi hỏi bit F trên cùng trong biểu diễn bit luôn giống nhau và trong khi sử dụng tính tương đương này đã làm giảm không gian tìm kiếm khá nhiều và giảm khoảng một phần tư thời gian sử dụng một không gian trạng thái khác giảm trên F, chi phí từ việc loại bỏ tối ưu hóa mức thấp hơn là bù lại. Tuy nhiên, hóa ra vấn đề này có thể được loại bỏ bằng cách xem xét thực tế rằng F là nghịch đảo của nhau cũng tương đương. Mặc dù điều này làm tăng thêm tính phức tạp của việc tính toán các lớp tương đương một chút, nhưng nó cũng cho phép tôi giữ lại tối ưu hóa mức thấp đã nói ở trên, dẫn đến tăng tốc khoảng x3.

Thêm một bản cập nhật để hỗ trợ số nguyên 128 bit cho dữ liệu tích lũy. Để biên dịch với số nguyên 128 bit, bạn sẽ cần longint.nimtừ đây và biên dịch với -d:use128bit. N = 24 vẫn mất hơn 10 phút, nhưng tôi đã bao gồm kết quả bên dưới cho những người quan tâm.

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)

Kết quả với N = 22 là 12410090985684467712, lấy 63,42 bit và do đó phù hợp với 64 bit không dấu.
Stefan

2
Bạn chắc chắn đã nâng thanh rất ấn tượng.

1
Thật tốt khi thấy ai đó sử dụng Nimrod. :)
cjfaure

@Stefan Có lẽ thuật sĩ mã hóa của bạn có thể có được phương pháp này dưới 10 phút với N = 22?

Tôi đã thử N = 22 đã chấm dứt sau vài giờ. Tuy nhiên nó mang lại cho tôi [-6036653088025083904, 2087229562810269696, 351473149499408384, 59178309967151104, 9975110458933248, 1682628717576192, 284866824372224, 48558946385920, 8416739196928, 1518499004416, 301.448.822.784, 71620493312, 22100246528, 8676573184, 3897278464, 1860960256, 911.646.720, 451.520.512, 224.785.920, 112.198.656, 56.062.720, 28.031.360, 14015680] dường như là một lỗi tràn. Tôi biết không có nimrod nhưng có thể sử dụng ints không dấu để giải quyết điều này?

11

Java (n=22 ?)

Tôi nghĩ rằng hầu hết các câu trả lời làm tốt hơn n=16 sử dụng một cách tiếp cận tương tự như vậy, mặc dù chúng khác nhau về các đối xứng mà chúng khai thác và cách chúng phân chia nhiệm vụ giữa các luồng.

Các vectơ được xác định trong câu hỏi có thể được thay thế bằng các chuỗi bit và sản phẩm bên trong với XORing cửa sổ chồng chéo và kiểm tra xem có chính xác n/2các bit được đặt không (và do đó n/2các bit bị xóa). Có các n! / ((n/2)!)chuỗi (hệ số nhị thức trung tâm) của các nbit với n/2các bit được đặt (mà tôi gọi là các chuỗi cân bằng ), do đó, đối với bất kỳ trường hợp nào, Fcó rất nhiều cửa sổ Scung cấp một sản phẩm bên trong bằng không. Hơn nữa, hành động trượt Sdọc theo một và kiểm tra xem chúng ta vẫn có thể tìm thấy một bit đến không cho sản phẩm bên trong bằng 0 tương ứng với việc tìm kiếm một cạnh trong đồ thị có các nút là các cửa sổ và các cạnh của nó liên kết một nút uvới một nút vcó các bit đầu tiên của .n-1 các bit là cái cuối cùngn-1u

Ví dụ: với n=6F=001001chúng tôi nhận được biểu đồ này:

Đồ thị cho F = 001001

và cho F=001011chúng tôi có được biểu đồ này:

Đồ thị cho F = 001011

Sau đó, chúng ta cần tính cho mỗi itừ 0nbao nhiêu đường dẫn icó chiều dài , tổng hợp trên các biểu đồ cho mỗi F. Tôi nghĩ rằng hầu hết chúng ta đang sử dụng tìm kiếm theo chiều sâu.

Lưu ý rằng các biểu đồ rất thưa thớt: thật dễ dàng để chứng minh rằng mỗi nút có mức độ nhiều nhất là 1 và ngoài mức độ nhiều nhất là một. Điều đó cũng có nghĩa là các cấu trúc duy nhất có thể là các chuỗi đơn giản và các vòng lặp đơn giản. Điều này đơn giản hóa DFS một chút.

Tôi khai thác một vài đối xứng: các chuỗi cân bằng được đóng dưới nghịch đảo bit ( ~hoạt động trong nhiều ngôn ngữ từ họ ALGOL) và theo xoay vòng bit, vì vậy chúng ta có thể nhóm các giá trị Fliên quan đến nhau bởi các hoạt động này và chỉ thực hiện DFS Một lần.

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;
    }
}

Trên Core 2 tốc độ 2,5 GHz của tôi, tôi nhận được

# 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

Vì máy tính của Lembik có 8 lõi và thực hiện chương trình đơn luồng trước đó của tôi nhanh gấp đôi so với máy tính của tôi, tôi rất lạc quan rằng nó sẽ thực thi n=22trong vòng chưa đầy 8 phút.


7:17! Rất đẹp. Bạn có phiền giải thích phương pháp của bạn hơn một chút không?

6

C

Về cơ bản, đây chỉ là một triển khai tối ưu hóa thuật toán trong câu hỏi. Nó có thể quản lý n=12trong thời gian giới hạn.

#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;
}

Chạy thử n=12, bao gồm cả biên dịch:

$ 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
$

Nhận xét: Tôi chỉ cần bật bộ não của mình lên và sử dụng một số tổ hợp đơn giản để tính toán rằng giá trị đầu tiên sẽ luôn là n! / ((n / 2)!)^2 * 2^(n + m - 1). Dường như với tôi rằng phải có một giải pháp đại số hoàn toàn cho vấn đề này.


Tôi nhận được rất nhiều cảnh báo khi tôi biên dịch nó. Hãy thử gcc -Wall -Wextra Fors.c -o Fors

Có một vài biến không được sử dụng bị quên từ một lần lặp trước đó, nhưng tôi đã loại bỏ chúng để ít nhất một vài cảnh báo sẽ biến mất. Hiện tại tôi không có sẵn GCC (chỉ Clang) và Clang không đưa ra bất kỳ cảnh báo nào vào lúc này (sau khi xóa các biến không sử dụng). Và vì Clang thường nghiêm khắc hơn khi đưa ra cảnh báo, tôi phải nói rằng tôi hơi ngạc nhiên khi bạn nhận được bất kỳ cảnh báo nào.
Dành cho

Nó phàn nàn về Fors.c: 13: 17: cảnh báo: đề xuất dấu ngoặc đơn xung quanh '-' trong toán hạng của '&' [-Wparentheses] (hai lần) và cũng cảnh báo: định dạng '% llu' mong muốn đối số của loại 'dài không dấu ', nhưng đối số 2 có loại' uint64_t '[-Wformat =]. Trong thực tế,

Với những thay đổi mới nhất, GCC không nên ném bất kỳ thông điệp cảnh báo nào.
Dành cho

Nó vẫn phàn nàn về Fors.c: 13: 49: cảnh báo: đề xuất dấu ngoặc đơn xung quanh số học trong toán hạng của '^' [-Wparentheses] Nhưng trong tin tức tồi tệ hơn ... phải mất hơn 10 phút trên máy của tôi.

5

Java, n=16

Đối với bất kỳ giá trị đã cho nào, F\binom{n}{n/2}các vectơ có sản phẩm bên trong bằng 0. Vì vậy, chúng ta có thể xây dựng một biểu đồ có các đỉnh là các vectơ phù hợp và các cạnh của nó tương ứng với sự dịch chuyển của S, và sau đó chúng ta chỉ cần đếm các đường dẫn có chiều dài lên đến ntrong biểu đồ.

Tôi đã không thử vi mô hóa điều này bằng cách thay thế các điều kiện bằng các thao tác bitwise, nhưng mỗi lần tăng gấp đôi nthời gian chạy khoảng 16 lần, do đó, điều đó sẽ không tạo ra đủ sự khác biệt trừ khi tôi khá gần với ngưỡng. Trên máy của tôi, tôi thì không.

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("]");
    }
}

Trên Core 2 tốc độ 2,5 GHz của tôi, tôi nhận được

$ 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

Cõng vì tôi không muốn thực hiện giải pháp của riêng mình ngay bây giờ. Mỗi đỉnh có nhiều nhất một kế, vì vậy bạn không thực sự cần mảng. Để lặp lại hiệu quả qua các kết hợp fvà bắt đầu các đỉnh, lặp lại tất cả f_xor_gvới n/2các bit được đặt chính xác . Đối với mỗi trong số này, lặp đi lặp lại tất cả fvà lấy g = f ^ f_xor_g.
David Eisenstat

@David, tôi biết, và phiên bản 7 của tôi có n = 18 trong một phút trên netbook Atom của tôi, nhưng tôi không thể đăng nó cho đến khi tôi trở về từ kỳ nghỉ.
Peter Taylor

4

RPython, N = 22 ~ 3: 23

Đa luồng, sử dụng một đệ quy đệ quy stackless. Chương trình chấp nhận hai đối số dòng lệnh: N và số lượng luồng công nhâ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

Để biên dịch

Tạo một bản sao cục bộ của kho lưu trữ PyPy bằng cách sử dụng đồng bóng, git hoặc bất cứ thứ gì bạn thích. Nhập câu thần chú sau (giả sử tập lệnh trên được đặt tên convolution-high.py):

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

trong đó %PYPY_REPO%đại diện cho một biến môi trường trỏ đến kho lưu trữ mà bạn vừa nhân bản. Quá trình biên dịch mất khoảng một phút.


Thời gian mẫu

N = 16, 4 chủ đề:

$ 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 chủ đề:

$ 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 chủ đề:

$ 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 chủ đề:

$ 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. Chào mừng bạn đến với phi hành đoàn 22 :)

Tôi không chắc tại sao nhưng phiên bản mới của bạn không nhanh hơn đối với tôi. Vẫn còn khoảng 9:30 khi tôi làm thời gian ./primo-c 22 8.

@Lembik sẽ có ý nghĩa nếu phân chia trung bình nhanh bằng 3 ca phải (3 = Sum {(n + 1) / (2 ^ n)}, n = 1..infty). Đối với kiến ​​trúc certian, tôi cho rằng đó có thể là trường hợp, nhưng trên phân chia của tôi chậm hơn đáng kể. Cảm ơn bạn đã dành thời gian để kiểm tra nó :)
primo

3

Python 3,3, N = 20, 3,5 phút

Tuyên bố miễn trừ trách nhiệm: ý định của tôi là KHÔNG đăng bài này dưới dạng câu trả lời của riêng tôi, vì thuật toán tôi đang sử dụng chỉ là một cổng không biết xấu hổ từ giải pháp RPython của primo . Mục đích của tôi ở đây chỉ là để hiển thị những gì bạn có thể làm trong Python nếu bạn kết hợp phép thuật của NumpyNumba các mô-đun .

Numba giải thích ngắn gọn:

Numba là một trình biên dịch chuyên biệt chỉ trong thời gian, biên dịch mã chú thích Python và NumPy thành LLVM (thông qua các trình trang trí). http://numba.pydata.org/

Cập nhật 1 : Tôi nhận thấy sau khi ném các con số xung quanh rằng chúng ta có thể bỏ qua một số số hoàn toàn. Vì vậy bây giờ maxf trở thành (1 << n) // 2maxs trở thành maxf 2 **. Điều này sẽ tăng tốc quá trình khá một chút. n = 16 giờ chỉ mất ~ 48 giây (giảm từ 4,5 phút). Tôi cũng có một ý tưởng khác mà tôi sẽ thử và xem liệu tôi có thể làm cho nó đi nhanh hơn một chút không.

Cập nhật 2: Thuật toán thay đổi (giải pháp của primo). Mặc dù cổng của tôi chưa hỗ trợ đa luồng, nhưng nó khá tầm thường. Thậm chí có thể phát hành CPython GIL bằng Numba và ctypes. Tuy nhiên, giải pháp này cũng chạy rất nhanh trên lõi đơn!

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)

Và cuối cùng:

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))

Điều này chạy trên máy của tôi trong 212688ms hoặc ~ 3,5 phút.


Cảm ơn. Bây giờ làm thế nào về n = 18? :)

Đã gần 20 phút kể từ khi tôi bắt đầu chương trình bằng cách sử dụng n = 18. Tôi nghĩ rằng có thể nói rằng Python không thể giải quyết vấn đề này ngay cả khi Numba đúng giờ sử dụng thuật toán cụ thể này.
Anna Jokela

Tôi lạc quan rằng một thuật toán tốt hơn tồn tại.

Tôi đã thử cài đặt pip numba nhưng nó nói rằng nó không thể tìm thấy llvmpy. Tôi đã thử cài đặt sudo pip llvmpy nhưng nó nói rằng nó không thể tìm thấy phiên bản. Tôi đã thử sudo pip cài đặt phiên bản nhưng nó nói tôi đã có nó.

Mặc dù tôi chưa có numba để làm việc (tôi nghĩ rằng cuối cùng tôi sẽ phải cài đặt anaconda) Tôi rất ấn tượng về điều này. Câu hỏi là, bạn có thể lấy nó để giải quyết N = 22 bằng cách sử dụng một phương pháp tương tự như phương pháp nimrod không?

2

C ++ N = 16

Tôi đang thử nghiệm trên EEEPC với một nguyên tử .. thời gian của tôi không có nhiều ý nghĩa. : D
Nguyên tử giải n = 14 trong 34 giây. Và n = 16 trong 20 phút. Tôi muốn kiểm tra n = 16 trên OP pc. Tôi lạc quan.

Ý tưởng là mỗi khi chúng tôi tìm ra giải pháp cho một F nhất định, chúng tôi đã tìm thấy giải pháp 2 ^ i vì chúng tôi có thể thay đổi phần dưới của S dẫn đến cùng một kết quả.

#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;
}

để biên dịch:

gcc 26459.cpp -std = c ++ 11 -O3 -march = bản địa -fstrict-aliasing -ftree-vectorize -Wall -pedantic -o 26459


1
Điều đó thật tuyệt. Tôi có một số ý tưởng nửa thực tế về cách giải quyết nó cho n lớn hơn. Bạn có muốn nghe họ hoặc điều đó có thể làm hỏng cuộc thi?

2

JAVASCRIPT n: 12

Trong máy tính của tôi mất 231.242 giây. Trong bản Demo tôi đang sử dụng webworkers để ngăn chặn trình duyệt đóng băng. Điều này chắc chắn có thể được cải thiện hơn nữa với các công nhân song song. Tôi biết JS không có cơ hội trong thử thách này nhưng tôi đã làm nó cho vui!

Nhấn vào đây để chạy Demo trực tuyến

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;

Còn một trong những công cụ javascript nhanh (ish) mới thì sao? Những người có thể được sử dụng?

Bạn có ý nghĩa gì đó như phi tiêu ?
rafaelcastrocouto

1
Thật ra tôi sai. Bạn cũng có thể thử cả firefox và crom. Trừ khi bạn muốn viết nó trong asm.js tất nhiên :)

1
thách thức được chấp nhận ... sẽ làm điều đó!
rafaelcastrocouto

1
Đã thử điều này và mất máy tính của tôi 5,4 giây để làm 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

Pháo đài: n = 12

Tôi mới thực hiện một phiên bản nhanh chóng trong Fortran, không có tối ưu hóa nào ngoại trừ OpenMP. Nó sẽ siết chặt chỉ dưới 10 phút cho n = 12 trên máy OP, phải mất 10:39 trên máy của tôi, tốc độ chậm hơn rất nhiều.

Số nguyên 64 bit thực sự có tác động tiêu cực; đoán tôi sẽ phải suy nghĩ lại toàn bộ thuật toán để việc này nhanh hơn nhiều. Không biết tôi có làm phiền không, tôi nghĩ rằng tôi sẽ dành thời gian để nghĩ ra một thử thách tốt hơn theo sở thích của tôi. Nếu bất cứ ai khác muốn lấy cái này và chạy với nó, hãy tiếp tục :)

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

Lua: n = 16

Tuyên bố miễn trừ trách nhiệm: ý định của tôi là KHÔNG đăng bài này dưới dạng câu trả lời của riêng tôi, vì thuật toán tôi đang sử dụng bị đánh cắp một cách đáng xấu hổ từ câu trả lời thông minh của Anna Jokela . đã bị đánh cắp một cách đáng xấu hổ từ câu trả lời thông minh của ilmale .

Ngoài ra, nó thậm chí không hợp lệ - nó có sự không chính xác gây ra bởi số dấu phẩy động (sẽ tốt hơn nếu Lua hỗ trợ số nguyên 64 bit). Tuy nhiên, tôi vẫn đang tải nó lên, chỉ để cho thấy giải pháp này nhanh như thế nào. Đó là ngôn ngữ lập trình động, nhưng tôi có thể tính n = 16 trong thời gian hợp lý (1 phút trên CPU 800 MHz).

Chạy với LuaJIT, trình thông dịch chuẩn quá chậm.

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

Cảm ơn bạn. Tôi nghĩ các phiên bản lua gần đây sử dụng int dài dài nên là 64 bit trên hệ thống 64 bit. Xem "lua_integer" tại lua.org/work/doc/manual.html .

@Lembik: Thú vị. Dù bằng cách nào, đó là Lua tiêu chuẩn (đã hỗ trợ long longthay vì doublecài đặt biên dịch), không phải LuaJIT.
Konrad Borowski

Tôi nghĩ rằng tôi đã sai trong mọi trường hợp wrt luajit. Người ta sẽ cần 5.3 mà không tồn tại. Lời khuyên tốt nhất mà mọi người có thể đưa ra là "thử 5.3-workx".
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.