Seberapa tinggi Anda bisa pergi? (Tantangan coding + algoritma)


34

Sekarang semua orang telah mengembangkan keahlian pengkodean tingkat rendah (sering luar biasa) untuk Seberapa lambat Python sebenarnya? (Atau seberapa cepat bahasa Anda?) Dan Seberapa Lambat Apakah Python Sungguh (Bagian II)? sekarang saatnya untuk tantangan yang juga akan meregangkan kemampuan Anda untuk meningkatkan suatu algoritma.

Kode berikut menghitung daftar panjang 9. Posisi idalam daftar menghitung berapa kali setidaknya inol berturut-turut ditemukan ketika menghitung produk dalam antara Fdan S. Untuk melakukan hal ini dengan tepat, ini mengulangi semua daftar Fpanjang yang mungkin ndan daftar Spanjang 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

Outputnya adalah

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

Jika Anda meningkatkan n menjadi 10,12,14,16,18,20 menggunakan kode ini sangat cepat menjadi terlalu lambat.

Aturan

  • Tantangannya adalah untuk memberikan hasil yang benar sebesar dan mungkin. Hanya nilai n yang relevan.
  • Jika ada seri, kemenangan menuju ke kode tercepat di mesin saya untuk n terbesar.
  • Saya berhak untuk tidak menguji kode yang membutuhkan waktu lebih dari 10 menit.
  • Anda dapat mengubah algoritme dengan cara apa pun yang Anda suka asalkan memberikan hasil yang benar. Bahkan Anda harus mengubah algoritma untuk membuat kemajuan yang layak menuju kemenangan.
  • Pemenang akan diberikan satu minggu sejak pertanyaan ditetapkan.
  • Hadiah akan diberikan ketika jatuh tempo yang sedikit setelah ketika pemenang akan diberikan.

Mesin Saya Pengaturan waktu akan dijalankan pada mesin saya. Ini adalah instalasi ubuntu standar pada Prosesor Delapan Core AMD FX-8350. Ini juga berarti saya harus dapat menjalankan kode Anda. Sebagai konsekuensinya, hanya gunakan perangkat lunak gratis yang mudah tersedia dan harap sertakan instruksi lengkap cara menyusun dan menjalankan kode Anda.

Status .

  • C . n = 12 dalam 49 detik oleh @Fors
  • Jawa . n = 16 dalam 3:07 oleh @PeterTaylor
  • C ++ . n = 16 dalam 2:21 oleh @ilmale
  • Rpython . n = 22 dalam 3:11 oleh @primo
  • Jawa . n = 22 dalam 6:56 oleh @PeterTaylor
  • Nimrod . n = 24 dalam 9:28 detik oleh @ReimerBehrends

Pemenangnya adalah Reimer Behrends dengan entri di Nimrod!

Sebagai tanda centang, output untuk n = 22 seharusnya [12410090985684467712, 2087229562810269696, 351473149499408384, 59178309967151104, 9975110458933248, 1682628717576192, 284866824372224, 48558946385920, 8416739196928, 1518499004416, 301448822784, 71620493312, 22100246528, 8676573184, 3897278464, 1860960256, 911646720, 451520512, 224785920, 112198656, 56062720, 28031360, 14015680].


Kompetisi sudah berakhir, tetapi ... Saya akan menawarkan 200 poin untuk setiap pengiriman yang meningkat n dengan 2 (dalam 10 menit di komputer saya) sampai saya kehabisan poin. Penawaran ini terbuka selamanya .


1
"Saya berhak untuk tidak menguji kode yang membutuhkan waktu lebih dari beberapa menit." > Anda harus menentukan waktu yang tepat pada mesin Anda, jika tidak pertanyaan ini tidak memiliki kriteria kemenangan yang objektif.
pastebin.com memangkas 0mr8spkT

14
Saya suka tantangan "tingkatkan kecepatan saya" ini. Jika Anda membangun produk komersial dengan ini, Anda akan memiliki satu neraka produk cepat, dan Anda juga jenius jahat .
Rainbolt

1
Mungkin judul yang lebih informatif akan menarik perhatian pada ini?
TheDoctor

8
Jika kita terus melakukan tantangan semacam ini, saya pikir kita setidaknya harus berusaha untuk memecahkan masalah yang berbeda agar tetap menarik (bukan variasi pada masalah yang sama dengan spesifikasi tambahan).
grovesNL

2
@Claudiu CPU-nya memiliki 8 core fisik, tetapi unit fetch / decode dan FPU dibagi di antara core. Jadi ketika bottleneck ada di salah satu area itu, ia berperilaku lebih seperti quadcore. Abuse integer logic dan hindari ukuran kode besar dan itu lebih seperti 8-core.
Stefan

Jawaban:


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)

Kompilasi dengan

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

(Nimrod dapat diunduh di sini .)

Ini berjalan dalam waktu yang ditentukan untuk n = 20 (dan untuk n = 18 ketika hanya menggunakan utas tunggal, membutuhkan waktu sekitar 2 menit dalam kasus terakhir).

Algoritme menggunakan pencarian rekursif, memangkas pohon pencarian setiap kali produk dalam yang tidak nol ditemukan. Kami juga memotong ruang pencarian menjadi dua dengan mengamati bahwa untuk setiap pasangan vektor, (F, -F)kita hanya perlu mempertimbangkan satu karena yang lainnya menghasilkan set produk dalam yang sama (dengan meniadakan Sjuga).

Implementasinya menggunakan fasilitas metaprogramming Nimrod untuk membuka gulungan / sebaris beberapa level pertama dari pencarian rekursif. Ini menghemat sedikit waktu ketika menggunakan gcc 4.8 dan 4.9 sebagai backend dari Nimrod dan jumlah yang wajar untuk dentang.

Ruang pencarian dapat dipangkas lebih lanjut dengan mengamati bahwa kita hanya perlu mempertimbangkan nilai-nilai S yang berbeda dalam jumlah genap dari posisi N pertama dari pilihan kita F. Namun, kompleksitas atau kebutuhan memori yang tidak skala untuk nilai-nilai besar dari N, mengingat bahwa tubuh loop benar-benar dilewati dalam kasus-kasus itu.

Tabulasi di mana produk dalam adalah nol tampaknya lebih cepat daripada menggunakan fungsi penghitungan bit dalam loop. Rupanya mengakses tabel memiliki lokasi yang cukup bagus.

Tampaknya seolah-olah masalahnya harus sesuai dengan pemrograman dinamis, mengingat cara kerja pencarian rekursif, tetapi tidak ada cara yang jelas untuk melakukan itu dengan jumlah memori yang masuk akal.

Output contoh:

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]

Untuk tujuan membandingkan algoritme dengan implementasi lainnya, N = 16 membutuhkan waktu sekitar 7,9 detik pada mesin saya ketika menggunakan utas tunggal dan 2,3 detik saat menggunakan empat inti.

N = 22 membutuhkan waktu sekitar 15 menit pada mesin 64-inti dengan gcc 4.4.6 sebagai backend Nimrod dan meluap bilangan bulat 64-bit leadingZeros[0](mungkin bukan yang tidak ditandatangani, belum melihatnya).


Pembaruan: Saya telah menemukan ruang untuk beberapa perbaikan lagi. Pertama, untuk nilai tertentu F, kita dapat menghitung 16 entri pertama dari Svektor terkait secara tepat, karena mereka harus berbeda di N/2tempat yang tepat . Jadi kita melakukan precompute daftar vektor bit dari ukuran Nyang memiliki N/2bit diatur dan menggunakannya untuk mendapatkan bagian awal Sdari F.

Kedua, kita dapat meningkatkan pencarian rekursif dengan mengamati bahwa kita selalu tahu nilai F[N](karena MSB adalah nol dalam representasi bit). Ini memungkinkan kami untuk memprediksi dengan tepat cabang mana yang kami rekur masuk dari produk dalam. Meskipun itu benar-benar memungkinkan kita mengubah seluruh pencarian menjadi loop rekursif, itu sebenarnya cukup mengacaukan prediksi cabang, jadi kami menjaga level teratas dalam bentuk aslinya. Kami masih menghemat waktu, terutama dengan mengurangi jumlah cabang yang kami lakukan.

Untuk beberapa pembersihan, kode sekarang menggunakan bilangan bulat yang tidak ditandatangani dan memperbaikinya pada 64-bit (kalau-kalau ada seseorang yang ingin menjalankan ini pada arsitektur 32-bit).

Speedup keseluruhan adalah antara faktor x3 dan x4. N = 22 masih membutuhkan lebih dari delapan core untuk berjalan dalam waktu kurang dari 10 menit, tetapi pada mesin 64-core sekarang turun menjadi sekitar empat menit (dengan numThreadsmenambahkan sesuai). Saya tidak berpikir ada banyak ruang untuk perbaikan tanpa algoritma yang berbeda.

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]

Diperbarui lagi, memanfaatkan kemungkinan pengurangan lebih lanjut di ruang pencarian. Berjalan sekitar 9:49 menit untuk N = 22 pada mesin quadcore saya.

Pembaruan akhir (saya pikir). Kelas kesetaraan yang lebih baik untuk pilihan F, memotong runtime untuk N = 22 hingga 3:19 menit 57 detik (sunting: Saya tidak sengaja menjalankannya dengan hanya satu utas) pada mesin saya.

Perubahan ini memanfaatkan fakta bahwa sepasang vektor menghasilkan nol terkemuka yang sama jika satu dapat diubah menjadi yang lain dengan memutarnya. Sayangnya, optimasi tingkat rendah yang cukup kritis mensyaratkan bahwa bit atas F dalam representasi bit selalu sama, dan saat menggunakan kesetaraan ini memangkas ruang pencarian sedikit dan mengurangi runtime sekitar seperempat lebih dari menggunakan ruang keadaan yang berbeda pengurangan pada F, overhead dari menghilangkan optimasi level rendah lebih dari mengimbanginya. Namun, ternyata masalah ini dapat dihilangkan dengan juga mempertimbangkan fakta bahwa F yang merupakan invers satu sama lain juga setara. Sementara ini menambah kompleksitas perhitungan kelas ekivalensi sedikit, itu juga memungkinkan saya untuk mempertahankan optimasi tingkat rendah yang disebutkan di atas, yang mengarah ke peningkatan sekitar x3.

Satu lagi pembaruan untuk mendukung bilangan bulat 128-bit untuk akumulasi data. Untuk mengkompilasi dengan integer 128 bit, Anda harus longint.nimdari sini dan mengkompilasinya -d:use128bit. N = 24 masih membutuhkan waktu lebih dari 10 menit, tetapi saya sudah memasukkan hasil di bawah ini untuk mereka yang tertarik.

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)

Hasil dengan N = 22 adalah 12410090985684467712 yang mengambil 63,42 bit dan dengan demikian cocok dengan 64-bit yang tidak ditandatangani.
Stefan

2
Anda telah menaikkan bilah dengan sangat mengesankan.

1
Senang melihat seseorang menggunakan Nimrod. :)
cjfaure

@Stefan Mungkin ahli kode Anda bisa mendapatkan metode ini di bawah 10 menit untuk N = 22?

Saya mencoba N = 22 yang tidak berakhir setelah beberapa jam. Namun itu memberi saya [-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] yang tampaknya merupakan kesalahan melimpah. Saya tidak tahu nimrod tetapi apakah mungkin menggunakan ints yang tidak ditandatangani untuk menyelesaikan ini?

11

Jawa ( n=22?)

Saya pikir sebagian besar jawaban yang lebih baik daripada n=16menggunakan pendekatan yang mirip dengan ini, meskipun mereka berbeda dalam simetri yang mereka eksploitasi dan cara mereka membagi tugas di antara utas.

Vektor yang didefinisikan dalam pertanyaan dapat diganti dengan string bit, dan produk dalam dengan XORing jendela yang tumpang tindih dan memeriksa bahwa ada n/2bit yang diatur secara tepat (dan karenanya n/2bit dibersihkan). Ada n! / ((n/2)!)string (koefisien binomial pusat) nbit dengan n/2bit yang ditetapkan (yang saya sebut string seimbang ), jadi untuk apa pun yang diberikan Fada banyak jendela Syang memberikan produk dalam nol. Selain itu, aksi geser Ssepanjang satu dan memeriksa apakah kita masih dapat menemukan bit masuk yang memberikan nol produk dalam sesuai dengan mencari tepi dalam grafik yang node adalah jendela dan yang ujungnya menghubungkan sebuah node uke node vyang n-1bit pertama adalah yang terakhirn-1bit u.

Misalnya, dengan n=6dan F=001001kami mendapatkan grafik ini:

Grafik untuk F = 001001

dan untuk F=001011kita dapatkan grafik ini:

Grafik untuk F = 001011

Maka kita perlu menghitung untuk setiap idari 0ke nberapa banyak jalan panjang iada, menjumlahkan atas grafik untuk setiap F. Saya pikir kebanyakan dari kita menggunakan pencarian mendalam-pertama.

Perhatikan bahwa grafiknya jarang: mudah untuk membuktikan bahwa setiap node memiliki paling banyak 1 dan paling banyak satu. Itu juga berarti bahwa satu-satunya struktur yang mungkin adalah rantai sederhana dan loop sederhana. Ini menyederhanakan DFS sedikit.

Saya mengeksploitasi beberapa simetri: string seimbang ditutup di bawah bit terbalik ( ~operasi dalam banyak bahasa dari keluarga ALGOL), dan di bawah rotasi bit, sehingga kita dapat mengelompokkan nilai-nilai Fyang terkait dengan operasi ini dan hanya melakukan DFS sekali.

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

Pada 2.5GHz Core 2 saya saya dapatkan

# 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

Karena komputer Lembik memiliki 8 core dan mengeksekusi program single-threaded saya sebelumnya dua kali lebih cepat dari saya, saya optimis bahwa itu akan dieksekusi n=22dalam waktu kurang dari 8 menit.


7:17! Sangat bagus. Maukah Anda menjelaskan metode Anda sedikit lagi?

6

C

Ini pada dasarnya hanya implementasi algoritma yang sedikit dioptimalkan dalam pertanyaan. Itu dapat mengelola n=12dalam batas waktu.

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

Test run for n=12, termasuk kompilasi:

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

Komentar: Saya baru saja mengaktifkan otak saya dan menggunakan beberapa kombinasi sederhana untuk menghitung bahwa nilai pertama akan selalu n! / ((n / 2)!)^2 * 2^(n + m - 1). Tampak bagi saya bahwa harus ada solusi aljabar sepenuhnya untuk masalah ini.


Saya mendapat banyak peringatan saat saya menyusun ini. Coba gcc -Wall -Wextra Fors.c -o Fors

Ada beberapa variabel yang tidak digunakan yang dilupakan dari iterasi sebelumnya, tetapi saya menghapusnya sehingga setidaknya beberapa peringatan seharusnya menghilang. Saya tidak memiliki GCC saat ini (hanya Dentang), dan Dentang tidak memberi saya peringatan saat ini (setelah menghapus variabel yang tidak digunakan). Dan karena Dentang biasanya lebih ketat dalam hal peringatan, saya harus mengatakan saya sedikit terkejut bahwa Anda mendapat peringatan sama sekali.
Fors

Ia mengeluh tentang Fors.c: 13: 17: peringatan: menyarankan tanda kurung di sekitar '-' dalam operan '&' [-Wparentheses] (dua kali) dan juga peringatan: format '% llu' mengharapkan argumen tipe 'lama tidak bertanda ', tetapi argumen 2 memiliki tipe' uint64_t '[-Wformat =]. Bahkan dentang mengeluh tentang pernyataan printf juga bagi saya,

Dengan perubahan terbaru, GCC tidak seharusnya melempar pesan peringatan apa pun.
Fors

Masih mengeluh tentang Fors.c: 13: 49: peringatan: menyarankan tanda kurung di sekitar aritmatika dalam operan '^' [-Wparentheses] Tapi dalam berita yang lebih buruk ... butuh waktu lebih dari 10 menit di mesin saya.

5

Jawa, n=16

Untuk setiap nilai yang diberikan Fada \binom{n}{n/2}vektor yang memiliki produk dalam nol dengannya. Jadi kita bisa membuat grafik yang simpulnya adalah vektor-vektor yang cocok dan ujung-ujungnya sesuai dengan pergeseran S, dan kemudian kita hanya perlu menghitung lintasan panjang hingga ndalam grafik.

Saya belum mencoba mengoptimalkan mikro ini dengan mengganti kondisional dengan operasi bitwise, tetapi setiap penambahan dua kali lipat nwaktu berjalan sekitar 16 kali lipat, jadi itu tidak akan membuat perbedaan yang cukup kecuali saya cukup dekat dengan ambang batas. Di mesin saya, saya tidak.

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

Pada 2.5GHz Core 2 saya saya dapatkan

$ 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

Membonceng karena saya tidak ingin mengimplementasikan solusi saya sendiri sekarang. Setiap simpul memiliki paling banyak satu penerus, sehingga Anda tidak benar-benar membutuhkan array. Untuk beralih secara efisien melalui kombinasi fdan memulai simpul, lakukan iterasi di atas semua f_xor_gdengan n/2bit yang diatur secara tepat . Untuk masing-masing hal ini, lakukan iterate pada semua fdan ambil g = f ^ f_xor_g.
David Eisenstat

@ David, saya tahu, dan versi 7 saya n = 18 dalam satu menit di netbook Atom saya, tapi saya tidak bisa mempostingnya sampai saya kembali dari liburan.
Peter Taylor

4

RPython, N = 22 ~ 3: 23

Multi-berulir, menggunakan keturunan rekursif tanpa tumpukan. Program menerima dua argumen baris perintah: N, dan jumlah utas pekerja.

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

Untuk Mengkompilasi

Buat klon lokal dari repositori PyPy menggunakan mercurial, git, atau apa pun yang Anda suka. Ketik mantra berikut (dengan asumsi skrip di atas diberi nama convolution-high.py):

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

di mana %PYPY_REPO%merupakan variabel lingkungan yang menunjuk ke repositori yang baru saja Anda kloning. Kompilasi membutuhkan waktu sekitar satu menit.


Contoh waktu

N = 16, 4 utas:

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

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

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

$ 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. Selamat datang di 22 kru :)

Saya tidak yakin mengapa tetapi versi baru Anda tidak lebih cepat untuk saya. Masih sekitar 9:30 ketika saya melakukan waktu ./primo-c 22 8.

@Lembik itu masuk akal jika pembagian rata-rata secepat 3 shift kanan (3 = Jumlah {(n + 1) / (2 ^ n)}, n = 1..infty). Untuk arsitektur certian, saya kira itu mungkin terjadi, tetapi pada divisi tambang terlihat lebih lambat. Terima kasih telah meluangkan waktu untuk mengujinya :)
primo

3

Python 3.3, N = 20, 3.5 menit

Disclaimer: maksud saya BUKAN untuk memposting ini sebagai jawaban saya sendiri, karena algoritma yang saya gunakan hanyalah port yang tidak tahu malu dari solusi RPython primo . Tujuan saya di sini adalah hanya untuk menunjukkan apa yang dapat Anda lakukan dengan Python jika Anda menggabungkan keajaiban Numpy dan Numba modul.

Numba menjelaskan secara singkat:

Numba adalah kompilator khusus tepat waktu yang mengkompilasi kode Python dan NumPy beranotasi ke LLVM (melalui dekorator). http://numba.pydata.org/

Pembaruan 1 : Saya perhatikan setelah melemparkan angka-angka di sekitar bahwa kita dapat melewati beberapa angka sepenuhnya. Jadi sekarang maxf menjadi (1 << n) // 2 dan maxs menjadi maxf 2 **. Ini akan mempercepat prosesnya sedikit. n = 16 sekarang hanya membutuhkan ~ 48s (turun dari 4,5 menit). Saya juga punya ide lain yang akan saya coba dan lihat apakah saya bisa membuatnya sedikit lebih cepat.

Pembaruan 2: Mengubah algoritma (solusi primo). Meskipun port saya belum mendukung multithreading, itu cukup sepele untuk ditambahkan. Bahkan dimungkinkan untuk melepaskan CPython GIL menggunakan Numba dan ctypes. Solusi ini, bagaimanapun, berjalan sangat cepat pada single core juga!

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)

Dan akhirnya:

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

Ini berjalan di mesin saya di 212688ms atau ~ 3,5 menit.


Terima kasih. Sekarang bagaimana dengan n = 18? :)

Sudah hampir 20 menit sejak saya memulai program menggunakan n = 18. Saya pikir aman untuk mengatakan bahwa Python tidak dapat menyelesaikan ini bahkan dengan Numba tepat waktu menggunakan algoritma khusus ini.
Anna Jokela

Saya optimis bahwa ada algoritma yang lebih baik.

Saya mencoba pip menginstal numba tetapi dikatakan tidak dapat menemukan llvmpy. Saya mencoba sudo pip install llvmpy tetapi katanya tidak menemukan versi. Saya mencoba sudo pip menginstal versioneer tetapi katanya saya sudah memilikinya.

Meskipun saya belum punya numba untuk bekerja (saya pikir saya harus menginstal anaconda pada akhirnya) saya terkesan dengan ini. Pertanyaannya adalah, dapatkah Anda menyelesaikannya dengan N = 22 menggunakan metode yang mirip dengan nimrod?

2

C ++ N = 16

Saya menguji pada EEEPC dengan atom .. waktu saya tidak masuk akal. : D
Atom memecahkan n = 14 dalam 34 detik. Dan n = 16 dalam 20 menit. Saya ingin menguji n = 16 pada OP pc. Saya optimis.

Idenya adalah bahwa setiap kali kita menemukan solusi untuk F yang diberikan, kita telah menemukan 2 ^ i solusi karena kita dapat mengubah bagian bawah S yang mengarah ke hasil yang sama.

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

untuk mengkompilasi:

gcc 26459.cpp -std = c ++ 11 -O3 -march = asli -fstrict-aliasing -ftree-vectorize -Wall -pedantic -o 26459


1
Ini bagus. Saya punya beberapa ide setengah matang sebenarnya untuk bagaimana menyelesaikannya untuk n lebih besar. Apakah Anda ingin mendengarnya atau bisakah itu merusak kompetisi?

2

JAVASCRIPT n: 12

Di komputer saya butuh 231,242 detik. Dalam Demo saya menggunakan pekerja web untuk mencegah pembekuan browser. Ini tentu dapat lebih ditingkatkan dengan pekerja paralel. Saya tahu JS tidak memiliki peluang dalam tantangan ini, tetapi saya melakukannya untuk bersenang-senang!

Klik untuk menjalankan Demo online

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;

Bagaimana dengan salah satu mesin javascript cepat (ish) baru itu? Bisakah itu digunakan?

Maksudmu sesuatu seperti panah ?
rafaelcastrocouto

1
Sebenarnya saya salah. Anda mungkin juga mencoba firefox dan chromium. Kecuali Anda ingin menulisnya di asm.js tentu saja :)

1
tantangan diterima ... akan melakukannya!
rafaelcastrocouto

1
Mencoba ini dan mengambil komputer saya 5,4 detik untuk melakukan 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

Fortran: n = 12

Saya baru saja membuat versi quick'n'dirty di Fortran, tidak ada optimasi kecuali OpenMP. Itu harus masuk tepat di bawah 10 menit untuk n = 12 pada mesin OPs, dibutuhkan 10:39 pada mesin saya yang lebih lambat.

Bilangan bulat 64-bit memang memiliki dampak kinerja negatif; kira saya harus memikirkan kembali seluruh algoritma untuk ini menjadi lebih cepat. Tidak tahu apakah saya akan repot, saya pikir saya lebih suka menghabiskan waktu memikirkan tantangan bagus yang lebih sesuai dengan selera saya. Jika ada orang lain yang ingin mengambil ini dan menjalankannya, silakan :)

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

Penafian: niat saya BUKAN untuk memposting ini sebagai jawaban saya sendiri, karena algoritma yang saya gunakan dicuri tanpa malu-malu dari jawaban pintar Anna Jokela . yang tanpa malu-malu dicuri dari jawaban pintar ilmale .

Selain itu, bahkan tidak valid - ia memiliki ketidakakuratan yang disebabkan oleh angka floating point (akan lebih baik jika Lua akan mendukung bilangan bulat 64-bit). Namun, saya masih mengunggahnya, hanya untuk menunjukkan seberapa cepat solusi ini. Ini adalah bahasa pemrograman yang dinamis, namun saya dapat menghitung n = 16 dalam waktu yang wajar (1 menit pada CPU 800MHz).

Jalankan dengan LuaJIT, penerjemah standar terlalu lambat.

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

Terima kasih. Saya pikir versi lua terbaru menggunakan int panjang yang seharusnya 64 bit pada sistem 64 bit. Lihat "lua_integer" di lua.org/work/doc/manual.html .

@Lembik: Menarik. Apa pun itu, itu adalah Lua standar (yang sudah mendukung long longalih-alih doubledengan pengaturan kompilasi), bukan LuaJIT.
Konrad Borowski

Saya pikir saya hanya salah dalam hal apa pun luajit. Seseorang akan membutuhkan 5.3 yang tidak ada. Nasihat terbaik yang bisa diberikan orang adalah "coba 5.3-workx".
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.