あなたはどれくらい高く行くことができますか?(コーディング+アルゴリズムの課題)


34

Pythonが実際にどれくらい遅いかについて、誰もが(しばしば驚くべき)低レベルコーディングの専門知識を開発しました(またはあなたの言語はどれくらい速いですか?)およびPythonは本当に遅い(パートII)?アルゴリズムを改善する能力を伸ばす挑戦の時です。

次のコードは、長さ9のリストを計算します。iリスト内の位置は、とのi間の内積を計算するときに少なくとも連続したゼロが見つかった回数をカウントします。まさにこれを行うために、それはすべての可能なリストの上に反復した長さのとリストの長さ。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コアプロセッサへの標準のUbuntuインストールです。これは、コードを実行できる必要があることも意味します。結果として、簡単に入手できる無料のソフトウェアのみを使用し、コードをコンパイルして実行する方法の完全な指示を含めてください。

ステータス

  • C。@Forsによる49秒でn = 12
  • Javaの。n = 16 @ 3:PeterTaylorによる3:07
  • C ++。n = 16 by 2:ilby @ilmale
  • Rpython。@primoによる3:11のn = 22
  • Java。n = 22 @ 6:56 @PeterTaylorによって
  • ニムロッド。@ReimerBehrendsによる9:28秒でn = 24

勝者はNimrodにエントリーしたReimer Behrendsでした!

チェックとして、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
「数分以上かかるコードをテストしない権利を留保します。」>マシン上で正確な時間を指定する必要があります。そうしないと、この質問には客観的な勝利基準がありません。
pastebin.comスラッシュ0mr8spkT 14

14
私は大好きですこれらの「私のスピードを上げる」の挑戦を。これらを使用して商用製品を構築している場合、高速製品の1つの地獄を持つことになり、あなたも邪悪な天才です。
レインボルト14

1
おそらく、より有益なタイトルがこれに注意を引くでしょうか?
TheDoctor 14

8
この種の挑戦を続けているなら、少なくとも別の問題を解決して面白くすることを試みるべきだと思います(追加の仕様による同じ問題のバリエーションではありません)。
grovesNL

2
@ClaudiuのCPUには8つの物理コアがありますが、フェッチ/デコードとFPUユニットはコア間で共有されます。そのため、ボトルネックがこれらの領域の1つにある場合、クアッドコアのように動作します。整数ロジックを乱用し、大きなコードサイズを避けると、8コアのようになります。
ステファン

回答:


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分かかります)。

このアルゴリズムは再帰的検索を使用し、ゼロ以外の内積が検出されるたびに検索ツリーを枝刈りします。また、ベクトルの任意のペアについて、一方(F, -F)だけを考慮する必要があることを観察することにより、探索空間を半分にカットします。Sも)。

実装では、Nimrodのメタプログラミング機能を使用して、再帰検索の最初のいくつかのレベルを展開/インライン化します。これにより、gim 4.8および4.9をNimrodのバックエンドとして使用するときの時間を節約し、clangをかなり節約できます。

探索空間は、Fの選択と最初のN個の偶数個の位置で異なるSの値のみを考慮する必要があることを観察することにより、さらに刈り取ることができます。これらの場合、ループ本体が完全にスキップされるため、

内積がゼロである場所を集計することは、ループでビットカウント機能を使用するよりも高速に見えます。どうやらテーブルにアクセスすると、かなり良い局所性があります。

再帰的な検索がどのように機能するかを考えると、問題は動的プログラミングに適しているように見えますが、合理的な量のメモリでそれを行う明らかな方法はありません。

出力例:

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。それらは正確な場所で異なる必要があるためです。したがって、ビットが設定さNれているサイズのビットベクトルのリストを事前計算し、N/2これらを使用してSfromの初期部分を導出しFます。

第二に、F[N](MSBはビット表現でゼロであるため)の値を常に知っていることを観察することにより、再帰検索を改善できます。これにより、内積から再帰するブランチを正確に予測できます。これにより実際に検索全体を再帰ループに変えることができますが、実際には分岐予測がかなり台無しになるため、トップレベルを元の形式に保ちます。主に実行する分岐の量を減らすことにより、時間を節約できます。

いくつかのクリーンアップのために、コードは符号なし整数を使用し、64ビットで修正します(誰かが32ビットアーキテクチャでこれを実行したい場合に備えて)。

全体的なスピードアップは、3倍から4倍です。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秒に短縮されました(編集:誤って1つのスレッドで実行していました)。

この変更では、一方を回転させて他方に変換できる場合、ベクトルのペアが同じ先行ゼロを生成するという事実を利用します。残念ながら、かなり重要な低レベルの最適化では、ビット表現のFの最上位ビットが常に同じである必要がありますが、この等価性を使用すると、検索スペースが大幅に削減され、異なる状態スペースを使用するよりも約4分の1だけ実行時間が短縮されますFの削減、低レベルの最適化を相殺する以上の排除によるオーバーヘッド。ただし、この問題は、互いに逆のFも等価であるという事実も考慮することで解消できることがわかります。これにより、等価クラスの計算が少し複雑になりましたが、前述の低レベルの最適化を維持することもでき、約3倍の高速化につながりました。

累積データの128ビット整数をサポートするためのもう1つの更新。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ビットに収まります。
ステファン

2
あなたは間違いなく非常に印象的にバーを上げました。

1
Nimrodを使用している人を見るのは良いことです。:)
cjfaure

@Stefan N = 22の場合、コーディングウィザードはこのメソッドを10分未満で取得できますか?

N = 22を試しましたが、数時間後に終了しました。ただし、[-6036653088025083904、2087229562810269696、351473149499408384、59178309967151104、9975110458933248、1682628717576192、284866824372224、48558946385920、8416739196928、1518499004416、301448822784、112620 14015680]オーバーフローエラーのようです。私はnimrodを知りませんが、これを解決するために符号なしintを使用することは可能ですか?

11

Java(n=22?)

答えの大部分は、これにn=16似たアプローチを使用するよりも優れていると思いますが、活用する対称性とスレッド間でタスクを分割する方法が異なります。

質問で定義されたベクトルはビット文字列で置き換えられ、内積はオーバーラップウィンドウのXOR演算と正確にn/2ビットが設定されている(したがってn/2ビットがクリアされている)ことを確認できます。あるn! / ((n/2)!)の(中央の二項係数)ストリングnを有するビットn/2(私は呼び出し設定されたビットバランスストリング)は、そう任意の所与のためFの多くの窓があるSゼロ内積を得ているが。さらに、S1つに沿ってスライドし、内積がゼロになる入力ビットを見つけることができるかどうかを確認するアクションは、ノードがウィンドウで、エッジuがノードvを最初のビットのノードにリンクするグラフのエッジを探すことに対応しますの。n-1ビットのます最後ですn-1u

たとえば、とn=6そしてF=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 Core 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

C

基本的には、問題のアルゴリズムの実装をわずかに最適化したものです。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は通常、警告に関してはより厳密なので、警告を受け取ったことに少し驚いています。
Fors 14

Fors.c:13:17について文句を言います:警告: '&'のオペランドに '-'を囲む括弧を提案[-Wparentheses](2回)また、警告:形式 '%llu'は型 'long long unsigned int 'ただし、引数2のタイプは' uint64_t '[-Wformat =]です。実際、clangは私にとってもprintfステートメントについて文句を言います。

最新の変更では、GCCは警告メッセージをスローしません。
Fors 14

Fors.c:13:49についてはまだ文句を言います:警告: '^'のオペランドの算術を括弧で囲む[-Wparentheses]しかし、さらに悪いことに...私のマシンでは10分以上かかります。

5

Java、 n=16

の任意の値Fには、\binom{n}{n/2}内積がゼロのベクトルがあります。そのため、頂点が一致するベクトルであり、エッジがのシフトに対応するグラフを作成できます。その後、グラフ内のS最大長のパスをカウントするだけですn

私は条件をビット単位の操作に置き換えることでこれを微最適化しようとしませんでしたnが、実行時間が2倍に増えるごとに約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 Core 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

今は自分のソリューションを実装したくないので、便乗します。各頂点には最大で1つの後継があるため、実際には配列は必要ありません。f頂点の組み合わせと開始点を効率的に反復するにはf_xor_g、正確にn/2設定されたビットですべてを反復します。これらのそれぞれについて、すべてfを繰り返し処理しますg = f ^ f_xor_g
デビッドアイゼンスタット

@David、私は知っていますし、私のバージョン7は私のAtomネットブックで1分でn = 18を実行しますが、休日から戻るまで投稿できません。
ピーターテイラー

4

RPython、N = 22〜3:23

スタックレスの再帰降下を使用したマルチスレッド。プログラムは2つのコマンドライン引数を受け入れます。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

コンパイルします

mercurial、git、その他の好みのものを使用して、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-c22 8。

@Lembikは、除算が平均で3つの右シフト(3 = Sum {(n + 1)/(2 ^ n)}、n = 1..infty)と同じくらい速い場合に意味があります。certianアーキテクチャの場合、それが当てはまると思いますが、鉱山部門では顕著に遅いです。テストに時間を割いてくれてありがとう:)
primo

3

Python 3.3、N = 20、3.5分

免責事項:私が使用しているアルゴリズムはprimoのRPythonソリューションからの恥知らずなポートにすぎないため、これを自分の回答として投稿するつもりはありません。ここでの私の目的は、NumpyNumbaの魔法を組み合わせた場合にPythonでできることを示すことだけですモジュールモジュールの。

ヌバは簡単に説明しました:

Numbaは、注釈付きのPythonおよびNumPyコードを(デコレーターを介して)LLVMにコンパイルするジャストインタイムの特殊化コンパイラーです。http://numba.pydata.org/

更新1:数字を投げた後、数字の一部を完全にスキップできることに気付きました。したがって、maxf(1 << n)// 2になり、maxsmaxf 2 **になります。これにより、プロセスがかなりスピードアップします。n = 16にかかる時間は最大48秒です(4,5分から短縮)。また、別のアイデアもありますので、少し速くすることができるかどうか試してみます。

更新2:変更されたアルゴリズム(primoのソリューション)。私のポートはまだマルチスレッドをサポートしていませんが、追加するのは非常に簡単です。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を時間通りにPythonで解決することはできないと言っても安全だと思います。
アンナジョケラ

より良いアルゴリズムが存在すると楽観的です。

pip install numbaを試しましたが、llvmpyが見つかりませんと表示されます。sudo pip install llvmpyを試しましたが、versioneerが見つからないと言います。私はsudo pip install versioneerを試しましたが、すでに持っていると言います。

numbaがまだ動作していませんが(最終的にはanacondaをインストールする必要があると思います)、私はこれに感銘を受けました。問題は、nimrodの方法と同様の方法を使用してN = 22を解くことができるかどうかです。

2

C ++ N = 16

アトムを使用してEEEPCでテストしています。私の時間はあまり意味がありません。:D
原子は34秒でn = 14を解きます。そして、20分でn = 16です。OP pcでn = 16をテストします。私は楽観的です。

Sの下部を変更して同じ結果が得られるため、特定のFの解を見つけるたびに2 ^ iの解が見つかるという考え方です。

#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 = native -fstrict-aliasing -ftree-vectorize -Wall -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;

それらの新しい(っぽい)高速javascriptエンジンの1つはどうですか?それらを使用できますか?

ダーツみたいなの?
rafaelcastrocouto

1
実際、私は間違っています。同様に、FirefoxとChromeの両方を試すこともできます。もちろんasm.jsでそれを書きたくない限り:)

1
チャレンジが受け入れられました...やります!
rafaelcastrocouto

1
これを試して、コンピューターに5.4秒かかって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

FortranでQuick'n'Dirtyバージョンを作成しましたが、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の賢明な回答から恥知らずに盗まれているため、これを自分の回答として投稿するつもりはありません。これはイルマーレの賢い答えから恥知らずに盗まれました

その上、それは有効でもありません-浮動小数点数に起因する不正確さがあります(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

ありがとうございました。最近のluaバージョンは、64ビットシステムでは64ビットであるlong long intを使用していると思います。で"lua_integer"を参照してくださいlua.org/work/doc/manual.html

@Lembik:興味深い。いずれにしても、LuaJIT long longではなく、標準のLua(doubleコンパイル設定ではなく、既にサポートされています)です。
コンラッドボロウスキ

どんな場合でも、ルアジットについては間違っていたと思います。存在しない5.3が必要です。luaが提供できる最高のアドバイスは「5.3-workxを試す」ことでした。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.