おおよその相関を見つける


14

S長さのバイナリ文字列を考えnます。インデックス作成1、我々は計算することができハミング距離の間S[1..i+1]S[n-i..n]すべてのためにiから順に0しますn-1。等しい長さの2つのストリング間のハミング距離は、対応するシンボルが異なる位置の数です。例えば、

S = 01010

与える

[0, 2, 0, 4, 0].

これは0マッチ001ハミング距離が2に10010マッチ0100101 4にハミング距離があり1010 、最終的に01010自分自身にマッチします。

ただし、ハミング距離が最大1である出力のみに関心があります。したがって、このタスクではY、ハミング距離が最大で1であるかどうかを報告し、N場合そうでないます。したがって、上記の例では次のようになります

[Y, N, Y, N, Y]

すべてを反復するときに取得されるsとsのf(n)個別の配列の数になるように定義するYN2^nS長さの異なる可能性のあるビット文字列をn

仕事

n開始時の増加1場合、コードを出力する必要がありますf(n)

回答例

n = 1..24正解は次のとおりです。

1, 1, 2, 4, 6, 8, 14, 18, 27, 36, 52, 65, 93, 113, 150, 188, 241, 279, 377, 427, 540, 632, 768, 870

得点

あなたのコードはn = 1、それぞれに答えを与えることを繰り返す必要がありますnに順番にがあります。実行全体の時間を計り、2分後に殺します。

あなたのスコアは最高です nその時間に到達するです。

同点の場合、最初の答えが勝ちです。

コードはどこでテストされますか?

私の(少し古い)Windows 7ラップトップでcygwinの下でコードを実行します。そのため、これを簡単にするためにできる限りの支援をお願いします。

私のラップトップには、8GBのRAMと2つのコアと4つのスレッドを備えたIntel i7 5600U@2.6 GHz(Broadwell)CPUが搭載されています。命令セットには、SSE4.2、AVX、AVX2、FMA3、TSXが含まれます。

言語ごとの主要なエントリ

  • Anders KaseorgによるCryptoMiniSatを使用したRustのn = 40。(VboxのLubuntuゲストVMで。)
  • Christian SeviersによるBuDDyライブラリを使用したC ++ではn = 35。(VboxのLubuntuゲストVMで。)
  • N = 34ClingoはAnders Kaseorgによる。(VboxのLubuntuゲストVMで。)
  • N = 31はAnders Kaseorgによる。
  • N = 29Clojureの NikoNyrhによる。
  • n = 29、バータベルによるC
  • N = 27ハスケル bartavelleによって
  • alephalphaによるPari / gpn = 24
  • Python 2 + pypyn = 22
  • alephalphaによるMathematicaではn = 21。(自己報告)

将来の報奨金

これで、2分間でマシン上で最大n = 80になる回答に対して200ポイントの報奨金を与えます。


誰かが素朴なブルートフォースよりも速いアルゴリズムを見つけることを可能にするトリックを知っていますか?そうでない場合、この課題は「x86でこれを実装してください」(または、GPUがわかっている場合)
ジョナサンアラン

@JonathanAllan非常に素朴なアプローチをスピードアップすることは確かに可能です。正確にどれだけ早く取得できるかはわかりません。興味深いことに、ハミング距離が最大で0であればYを、そうでなければNを取得するように質問を変更した場合、既知の閉形式の公式があります。

@Lembik CPU時間またはリアルタイムを測定しますか?
-flawr

@flawrリアルタイムで測定していますが、それを数回実行し、最小値を取得して奇妙なものを排除しています。

回答:


9

Rust + CryptoMiniSatn≈41

src/main.rs

extern crate cryptominisat;
extern crate itertools;

use std::iter::once;
use cryptominisat::{Lbool, Lit, Solver};
use itertools::Itertools;

fn make_solver(n: usize) -> (Solver, Vec<Lit>) {
    let mut solver = Solver::new();
    let s: Vec<Lit> = (1..n).map(|_| solver.new_var()).collect();
    let d: Vec<Vec<Lit>> = (1..n - 1)
        .map(|k| {
                 (0..n - k)
                     .map(|i| (if i == 0 { s[k - 1] } else { solver.new_var() }))
                     .collect()
             })
        .collect();
    let a: Vec<Lit> = (1..n - 1).map(|_| solver.new_var()).collect();
    for k in 1..n - 1 {
        for i in 1..n - k {
            solver.add_xor_literal_clause(&[s[i - 1], s[k + i - 1], d[k - 1][i]], true);
        }
        for t in (0..n - k).combinations(2) {
            solver.add_clause(&t.iter()
                                   .map(|&i| d[k - 1][i])
                                   .chain(once(!a[k - 1]))
                                   .collect::<Vec<_>>()
                                   [..]);
        }
        for t in (0..n - k).combinations(n - k - 1) {
            solver.add_clause(&t.iter()
                                   .map(|&i| !d[k - 1][i])
                                   .chain(once(a[k - 1]))
                                   .collect::<Vec<_>>()
                                   [..]);
        }
    }
    (solver, a)
}

fn search(n: usize,
          solver: &mut Solver,
          a: &Vec<Lit>,
          assumptions: &mut Vec<Lit>,
          k: usize)
          -> usize {
    match solver.solve_with_assumptions(assumptions) {
        Lbool::True => search_sat(n, solver, a, assumptions, k),
        Lbool::False => 0,
        Lbool::Undef => panic!(),
    }
}

fn search_sat(n: usize,
              solver: &mut Solver,
              a: &Vec<Lit>,
              assumptions: &mut Vec<Lit>,
              k: usize)
              -> usize {
    if k >= n - 1 {
        1
    } else {
        let s = solver.is_true(a[k - 1]);
        assumptions.push(if s { a[k - 1] } else { !a[k - 1] });
        let c = search_sat(n, solver, a, assumptions, k + 1);
        assumptions.pop();
        assumptions.push(if s { !a[k - 1] } else { a[k - 1] });
        let c1 = search(n, solver, a, assumptions, k + 1);
        assumptions.pop();
        c + c1
    }
}

fn f(n: usize) -> usize {
    let (mut solver, proj) = make_solver(n);
    search(n, &mut solver, &proj, &mut vec![], 1)
}

fn main() {
    for n in 1.. {
        println!("{}: {}", n, f(n));
    }
}

Cargo.toml

[package]
name = "correlations-cms"
version = "0.1.0"
authors = ["Anders Kaseorg <andersk@mit.edu>"]

[dependencies]
cryptominisat = "5.0.1"
itertools = "0.6.0"

使い方

これは、SATソルバーを使用して、Y / N配列のプレフィックスへのすべての部分割り当てのツリーを再帰的に検索し、各ステップで現在の部分割り当てが一貫しているかどうかを確認します。CryptoMiniSatは、XOR句の特別な最適化により、このジョブに適したSATソルバーです。

制約の3つのファミリーは次のとおりです。

S IS K + ID KI、1≤ K N - 2、0≤I≤ N - K
D KI 1 D KI 2 ∨¬ A 、K、1≤ K N - 2、0≤ I 1 < I 2 N - K
¬ D KI 1 ∨⋯∨¬ D KI N - K - 1K 1、≤ KN - 2、0≤ I 1 <⋯< I N - K - 1N - K

ただし、最適化として、S 0はfalseに強制されるため、D k 0は単にS kと等しくなります。


2
やったー!:)

私はまだこれをWindowsでコンパイルしようとしています(cygwin + gccを使用)。cryptominisatのクローンを作成してコンパイルしました。しかし、錆びたコードをコンパイルする方法はまだわかりません。私はときcargo build私が得る--- stderr CMake Error: Could not create named generator Visual Studio 14 2015 Win64

2
@ rahnema1ありがとう。ただし、問題は、Rust自体ではなく、cryptominisatクレートに組み込まれたC ++ライブラリのCMakeビルドシステムにあるようです。
アンダースカセオルグ

1
@Lembikそのペーストから404を取得しています。
Mego

1
@ChristianSievers良い質問です。それは動作しますが、少し遅いようです(2倍程度)。なぜそれほど良くないのかわからないので、CryptoMiniSatはそのような増分ワークロードに対して最適化されていないのかもしれません。
アンダースカセオルグ

9

さび、n≈30 または 31または32

私のラップトップ(2つのコア、i5-6200U)では、これは約2.5 GiBのメモリを使用して53秒でn = 1、...、31を通過し、約5 GiBを使用して105秒でn = 1、...、32を通過しますメモリの。でコンパイルしcargo build --releaseて実行しtarget/release/correlationsます。

src/main.rs

extern crate rayon;

type S = u32;
const S_BITS: u32 = 32;

fn cat(mut a: Vec<S>, mut b: Vec<S>) -> Vec<S> {
    if a.capacity() >= b.capacity() {
        a.append(&mut b);
        a
    } else {
        b.append(&mut a);
        b
    }
}

fn search(n: u32, i: u32, ss: Vec<S>) -> u32 {
    if ss.is_empty() {
        0
    } else if 2 * i + 1 > n {
        search_end(n, i, ss)
    } else if 2 * i + 1 == n {
        search2(n, i, ss.into_iter().flat_map(|s| vec![s, s | 1 << i]))
    } else {
        search2(n,
                i,
                ss.into_iter()
                    .flat_map(|s| {
                                  vec![s,
                                       s | 1 << i,
                                       s | 1 << n - i - 1,
                                       s | 1 << i | 1 << n - i - 1]
                              }))
    }
}

fn search2<SS: Iterator<Item = S>>(n: u32, i: u32, ss: SS) -> u32 {
    let (shift, mask) = (n - i - 1, !(!(0 as S) << i + 1));
    let close = |s: S| {
        let x = (s ^ s >> shift) & mask;
        x & x.wrapping_sub(1) == 0
    };
    let (ssy, ssn) = ss.partition(|&s| close(s));
    let (cy, cn) = rayon::join(|| search(n, i + 1, ssy), || search(n, i + 1, ssn));
    cy + cn
}

fn search_end(n: u32, i: u32, ss: Vec<S>) -> u32 {
    if i >= n - 1 { 1 } else { search_end2(n, i, ss) }
}

fn search_end2(n: u32, i: u32, mut ss: Vec<S>) -> u32 {
    let (shift, mask) = (n - i - 1, !(!(0 as S) << i + 1));
    let close = |s: S| {
        let x = (s ^ s >> shift) & mask;
        x & x.wrapping_sub(1) == 0
    };
    match ss.iter().position(|&s| close(s)) {
        Some(0) => {
            match ss.iter().position(|&s| !close(s)) {
                Some(p) => {
                    let (ssy, ssn) = ss.drain(p..).partition(|&s| close(s));
                    let (cy, cn) = rayon::join(|| search_end(n, i + 1, cat(ss, ssy)),
                                               || search_end(n, i + 1, ssn));
                    cy + cn
                }
                None => search_end(n, i + 1, ss),
            }
        }
        Some(p) => {
            let (ssy, ssn) = ss.drain(p..).partition(|&s| close(s));
            let (cy, cn) = rayon::join(|| search_end(n, i + 1, ssy),
                                       || search_end(n, i + 1, cat(ss, ssn)));
            cy + cn
        }
        None => search_end(n, i + 1, ss),
    }
}

fn main() {
    for n in 1..S_BITS + 1 {
        println!("{}: {}", n, search(n, 1, vec![0, 1]));
    }
}

Cargo.toml

[package]
name = "correlations"
version = "0.1.0"
authors = ["Anders Kaseorg <andersk@mit.edu>"]

[dependencies]
rayon = "0.7.0"

オンラインでお試しください!

また、非常に少ないメモリを使用するわずかに遅いバリアントもあります。


どのような最適化を使用しましたか?

1
@Lembik最大の最適化は、コンパイルされた言語でビットごとの算術を使用してすべてを行うことに加えて、Y / N配列のプレフィックスを特定するために必要なだけの非決定性のみを使用することです。Y / N配列の可能性のあるプレフィックスを再帰的に検索し、そのプレフィックスを実現する可能性のある文字列のベクトルを使用します。とは言っても、これはまだ指数検索であり、これらの最適化は多項式因子によってのみ高速化します。
アンデルスカセオルグ

いい答えです。ありがとうございました。誰かがコンビナトリクスを掘り下げて大幅なスピードアップを望んでいます。

@Lembikメモリを浪費するバグを修正し、さらに最適化を行い、並列処理を追加しました。チャンスがありましたら、もう一度テストしてください。スコアを1または2増やしたいと思っています。何も思いつきません。
アンデルスカセオルグ

1
@Lembik OEISエントリには式がありません。(そこにあるMathematicaコードもブルートフォースを使用しているようです。)知っている場合は、それについて教えてください。
クリスチャンシーバーズ

6

BuDDyライブラリを使用したC ++

別のアプローチ:入力としてのビットを取り、特定の選択された位置または特定の位置でいくつかの固定値を与える場合に真であるバイナリ式を(バイナリ決定図として)持っています。その式が定数falseではない場合、自由な位置を選択して再帰し、との両方を試します。空き位置がない場合、可能な出力値が見つかりました。式が定数falseの場合、バックトラックします。SYNYN

可能な値が非常に少ないため、これは比較的合理的に機能し、多くの場合、早期にバックトラックできます。SATソルバーで同様のアイデアを試しましたが、あまり成功しませんでした。

#include<vector>
#include<iostream>
#include<bdd.h>

// does vars[0..i-1] differ from vars[n-i..n-1] in at least two positions?
bdd cond(int i, int n, const std::vector<bdd>& vars){
  bdd x1 { bddfalse };
  bdd xs { bddfalse };
  for(int k=0; k<i; ++k){
    bdd d { vars[k] ^ vars[n-i+k] };
    xs |= d & x1;
    x1 |= d;
  }
  return xs;
}

void expand(int i, int n, int &c, const std::vector<bdd>& conds, bdd x){
  if (x==bddfalse)
    return;
  if (i==n-2){
    ++c;
    return;
  }

  expand(i+1,n,c,conds, x & conds[2*i]);
  x &= conds[2*i+1];
  expand(i+1,n,c,conds, x);
}

int count(int n){
  if (n==1)   // handle trivial case
    return 1;
  bdd_setvarnum(n-1);
  std::vector<bdd> vars {};
  vars.push_back(bddtrue); // assume first bit is 1
  for(int i=0; i<n-1; ++i)
    if (i%2==0)            // vars in mixed order
      vars.push_back(bdd_ithvar(i/2));
    else
      vars.push_back(bdd_ithvar(n-2-i/2));
  std::vector<bdd> conds {};
  for(int i=n-1; i>1; --i){ // handle long blocks first
    bdd cnd { cond(i,n,vars) };
    conds.push_back( cnd );
    conds.push_back( !cnd );
  }
  int c=0;
  expand(0,n,c,conds,bddtrue);
  return c;
}

int main(void){
  bdd_init(20000000,1000000);
  bdd_gbc_hook(nullptr); // comment out to see GC messages
  for(int n=1; ; ++n){
    std::cout << n << " " << count(n) << "\n" ;
  }
}

debian 8(jessie)でコンパイルするには、インストールlibbdd-devしてくださいg++ -std=c++11 -O3 -o hb hb.cpp -lbdd。最初の引数をbdd_initさらに増やすと便利な場合があります。


これは面白そうです。これで何になりますか?

@Lembik私は私が速く答えることはできません非常に古いハードウェア上の100Sで31を取得する
クリスチャンSieversの

Windows上でこれをコンパイルする方法(たとえば、cygwinを使用)について、あなたが提供できるヘルプはありがたいことに受けました。

@Lembik Windwsについては知りませんが、github.com / fd00 / yacp / tree / master / buddyは役に立つと思われますcygwin
Christian Sievers

1
わかりました、このライブラリをツールキットに追加する必要があると確信しました。よくやった!
アンダースカセオルグ

4

クリンゴ、n≈30 または31 34

5行のClingoコードがブルートフォースのRustソリューションを追い越し、ChristianのBuDDyソリューションに非常に近づいているのを見て、少し驚いていました。

corr.lp

{s(2..n)}.
d(K,I) :- K=1..n-2, I=1..n-K, s(I), not s(K+I).
d(K,I) :- K=1..n-2, I=1..n-K, not s(I), s(K+I).
a(K) :- K=1..n-2, {d(K,1..n-K)} 1.
#show a/1.

corr.sh

#!/bin/bash
for ((n=1;;n++)); do
    echo "$n $(clingo corr.lp --project -q -n 0 -c n=$n | sed -n 's/Models *: //p')"
done

plot


これは素晴らしい!グラフから、BuDDyソリューションが突然悪化しているように見えます。理由は何ですか?

@Lembik確かにBuDDyを十分に調査していませんが、その時点でキャッシュが不足しているかもしれません。
アンダースカセオルグ

うわー!最初の値をbdd_init大きくすると役立つ場合があります。またはbdd_setmaxincrease、デフォルトの50000をはるかに超える値で呼び出してノードテーブルを増やすことができます。-プログラムの変更バージョンを使用していますか?
クリスチャンシーバーズ

2
あなたのグラフが大好きです。

1
あなたはオプションを使用して衝撃的なパフォーマンスの向上を取得する--configuration=craftyjumpytrendy同様の結果が得られ)。
クリスチャンジーバーズ

2

パリ/ GP、23

デフォルトでは、Pari / GPのスタックサイズは8 MBに制限されています。コードの最初の行で、default(parisize, "4g")この制限を4 GBに設定します。それでもスタックオーバーフローが発生する場合は、8 GBに設定できます。

default(parisize, "4g")
f(n) = #vecsort([[2 > hammingweight(bitxor(s >> (n-i) , s % 2^i)) | i <- [2..n-1]] | s <- [0..2^(n-1)]], , 8)
for(n = 1, 100, print(n " -> " f(n)))

22に達し、スタックオーバーフローが発生します。

24になりました。

2

Clojure、29で75 38秒、30で80、31で165

からのランタイム インテルi7の6700Kは、メモリ使用量が200 MB未満です。

project.clj(マルチスレッドにcom.climate / claypooleを使用):

(defproject tests "0.0.1-SNAPSHOT"
  :description "FIXME: write description"
  :dependencies [[org.clojure/clojure "1.8.0"]
                 [com.climate/claypoole "1.1.4"]]
  :javac-options ["-target" "1.6" "-source" "1.6" "-Xlint:-options"]
  :aot [tests.core]
  :main tests.core)

ソースコード:

(ns tests.core
  (:require [com.climate.claypoole :as cp]
            [clojure.set])
  (:gen-class))

(defn f [N]
  (let [n-threads   (.. Runtime getRuntime availableProcessors)
        mask-offset (- 31 N)
        half-N      (quot N 2)
        mid-idx     (bit-shift-left 1 half-N)
        end-idx     (bit-shift-left 1 (dec N))
        lower-half  (bit-shift-right 0x7FFFFFFF mask-offset)
        step        (bit-shift-left 1 12)
        bitcount
          (fn [n]
            (loop [i 0 result 0]
              (if (= i N)
                result
                (recur
                  (inc i)
                  (-> n
                      (bit-xor (bit-shift-right n i))
                      (bit-and (bit-shift-right 0x7FFFFFFF (+ mask-offset i)))
                      Integer/bitCount
                      (< 2)
                      (if (+ result (bit-shift-left 1 i))
                          result))))))]
    (->>
      (cp/upfor n-threads [start (range 0 end-idx step)]
        (->> (for [i      (range start (min (+ start step) end-idx))
                   :when  (<= (Integer/bitCount (bit-shift-right i mid-idx))
                              (Integer/bitCount (bit-and         i lower-half)))]
               (bitcount i))
             (into #{})))
      (reduce clojure.set/union)
      count)))

(defn -main [n]
  (let [n-iters 5]
    (println "Calculating f(n) from 1 to" n "(inclusive)" n-iters "times")
    (doseq [i (range n-iters)]
      (->> n read-string inc (range 1) (map f) doall println time)))
  (shutdown-agents)
  (System/exit 0))

ブルートフォースソリューションでは、各スレッドは範囲のサブセット(2 ^ 12項目)を超えて、検出されたパターンを示す整数値のセットを構築します。これらは一緒に「結合」され、したがって、個別のカウントが計算されます。スレッド化マクロを頻繁に使用しているにもかかわらず、コードがあまりにもトリッキーではないことを願っています。俺のmain JVMがウォームアップを取得するために、テストを数回実行します。

更新:整数の半分だけを反復処理すると、対称性のために同じ結果が得られます。また、重複を生成するため、数値の下位半分でビット数の多い数値をスキップします。

ビルド済みのuberjar(v1)(3.7 MB):

$ wget https://s3-eu-west-1.amazonaws.com/nikonyrh-public/misc/so-124424-v2.jar
$ java -jar so-124424-v2.jar 29
Calculating f(n) from 1 to 29 (inclusive) 5 times
(1 1 2 4 6 8 14 18 27 36 52 65 93 113 150 188 241 279 377 427 540 632 768 870 1082 1210 1455 1656 1974)
"Elapsed time: 41341.863703 msecs"
(1 1 2 4 6 8 14 18 27 36 52 65 93 113 150 188 241 279 377 427 540 632 768 870 1082 1210 1455 1656 1974)
"Elapsed time: 37752.118265 msecs"
(1 1 2 4 6 8 14 18 27 36 52 65 93 113 150 188 241 279 377 427 540 632 768 870 1082 1210 1455 1656 1974)
"Elapsed time: 38568.406528 msecs"
[ctrl+c]

異なるハードウェアでの結果、予想されるランタイムはO(n * 2^n)

i7-6700K  desktop: 1 to 29 in  38 seconds
i7-6820HQ laptop:  1 to 29 in  43 seconds
i5-3570K  desktop: 1 to 29 in 114 seconds

次の標準を使用することにより、このシングルスレッドを簡単に作成し、サードパーティの依存関係を回避できます。

(for [start (range 0 end-idx step)]
  ... )

組み込みのpmapも存在しますが、claypooleにはより多くの機能と調整可能性があります。


ええ、配布するのは簡単です。私のソリューションを再評価する時間はありますか、私はあなたが今30までそれを得ると確信しています。これ以上の最適化はありません。
ニコニール

悲しいことに、それは30経過時間の無いです:217150.87386ミリ秒

Ahaa、試してくれてありがとう:Dこれに曲線を当てはめ、120秒間で10進数の値を消費することを補間した方が良いかもしれませんが、それでもそれは素晴らしい挑戦です。
ニコニール

1

Mathematica、n = 19

alt +を押します。中止すると結果が出力されます

k = 0;
For[n = 1, n < 1000, n++,
Z = Table[HammingDistance[#[[;; i]], #[[-i ;;]]], {i, Length@#}] & /@
Tuples[{0, 1}, n];
Table[If[Z[[i, j]] < 2, Z[[i, j]] = 0, Z[[i, j]] = 1], {i, 
Length@Z}, {j, n}];
k = Length@Union@Z]
Print["f(", n, ")=", k]

私はこれを実行できませんので、指数関数的な時間を避ける方法を説明できますか?2 ^ 241は非常に大きな数字です!

コードの出力を表示できますか?

1
私はf(n)を意味していました...修正されました
-J42161217

1

Mathematica、21

f [n_]:=長さ@
     DeleteDuplicates @
      転置@
       テーブル[2> Tr @ IntegerDigits [#、2]&/ @ 
         BitXor [BitShiftRight [#、n-i]、Mod [#、2 ^ i]]、{i、1、 
         n-1}]&@ Range [0、2 ^(n-1)];
Do [Print [n-> f @ n]、{n、Infinity}]

比較のために、 Jenny_mathyの答えn = 19私のコンピューターにあります。

最も遅い部分は Tr@IntegerDigits[#, 2] &です。Mathematicaにハミングの重みが組み込まれていないのは残念です。


私のコードをテストしたい場合は、Mathematicaの無料トライアルをダウンロードできます


1

ACバージョン、組み込みポップカウントを使用

でより良くclang -O3動作しますが、もしあなたが持っている場合でも動作しますgcc

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

unsigned long pairs(unsigned int n, unsigned long s) { 
  unsigned long result = 0;

  for(int d=1;d<=n;d++) { 
    unsigned long mx = 1 << d;
    unsigned long mask = mx - 1;

    unsigned long diff = (s >> (n - d)) ^ (s & mask);
    if (__builtin_popcountl(diff) <= 1)
      result |= mx;
  } 
  return result;

}

unsigned long f(unsigned long  n) { 
  unsigned long max = 1 << (n - 1);
#define BLEN (max / 2)
  unsigned char * buf = malloc(BLEN);
  memset(buf, 0, BLEN);
  unsigned long long * bufll = (void *) buf;

  for(unsigned long i=0;i<=max;i++) { 
    unsigned int r = pairs(n, i);
    buf[r / 8] |= 1 << (r % 8);
  } 

  unsigned long result = 0;

  for(unsigned long i=0;i<= max / 2 / sizeof(unsigned long long); i++) { 
    result += __builtin_popcountll(bufll[i]);
  } 

  free(buf);

  return result;
}

int main(int argc, char ** argv) { 
  unsigned int n = 1;

  while(1) { 
    printf("%d %ld\n", n, f(n));
    n++;
  } 
  return 0;
}

すぐに24に達し、その後終了します。制限を増やす必要があります。

ああ、ベンチマークコードを削除するのを忘れた!問題のある2行を削除します:/
bartavelle

@Lembikは今修正する必要があります
-bartavelle

1

Haskell、(非公式n = 20)

これは単純なアプローチです-これまでのところ、最適化は行われていません。私はそれが他の言語に対してどれだけうまくいくのだろうと思った。

使用方法(haskellプラットフォームがあると仮定)インストールされ):

  • コードを1つのファイルに貼り付けます approx_corr.hs(または他の名前、それに応じて以下の手順を変更します)
  • ファイルに移動して実行します ghc approx_corr.hs
  • 走る approx_corr.exe
  • 最大値を入力してください n
  • 各計算の結果と、その時点までの累積リアルタイム(ミリ秒単位)が表示されます。

コード:

import Data.List
import Data.Time
import Data.Time.Clock.POSIX

num2bin :: Int -> Int -> [Int]
num2bin 0 _ = []
num2bin n k| k >= 2^(n-1) = 1 : num2bin (n-1)( k-2^(n-1))
           | otherwise  = 0: num2bin (n-1) k

genBinNum :: Int -> [[Int]]
genBinNum n = map (num2bin n) [0..2^n-1]

pairs :: [a] -> [([a],[a])]
pairs xs = zip (prefixes xs) (suffixes xs)
   where prefixes = tail . init . inits 
         suffixes = map reverse . prefixes . reverse 

hammingDist :: (Num b, Eq a) => ([a],[a]) -> b     
hammingDist (a,b) = sum $ zipWith (\u v -> if u /= v then 1 else 0) a b

f :: Int -> Int
f n = length $ nub $ map (map ((<=1).hammingDist) . pairs) $ genBinNum n
--f n = sum [1..n]

--time in milliseconds
getTime = getCurrentTime >>= pure . (1000*) . utcTimeToPOSIXSeconds >>= pure . round


main :: IO()
main = do 
    maxns <- getLine 
    let maxn = (read maxns)::Int
    t0 <- getTime 
    loop 1 maxn t0
     where loop n maxn t0|n==maxn = return ()
           loop n maxn t0
             = do 
                 putStrLn $ "fun eval: " ++ (show n) ++ ", " ++ (show $ (f n)) 
                 t <- getTime
                 putStrLn $ "time: " ++ show (t-t0); 
                 loop (n+1) maxn t0

コードは、実行時に出力を提供しないようです。これにより、テストが少し難しくなります。

奇妙なことに、エラーなしでコンパイルされますか?プログラムをコンパイルしようとするとどうなりますmain = putStrLn "Hello World!"か?
flawr

Data.Bitsモジュールには便利かもしれません。メインループには、main = do maxn <- getmax; t0 <- gettime; loop 1where loop n|n==maxn = return ()やのようなものを使用できますloop n = do printresult n (f n); t <- gettime; printtime (t-t0); loop (n+1)getmaxたとえばgetArgs、プログラムの引数を使用するために使用できます。
クリスチャンシーバーズ

ありがとうございます!私はstackoverflowでこの質問をしましたが、あなたもそれをそこに追加できたら素晴らしいと思います!
-flawr

私はそこで答える方法がわかりません。既に同じようなループがありますが、時間を取得することについては何も言いませんでした。
クリスチャンシーバーズ

1

popCountと手動で管理された並列処理を使用したHaskellソリューション

コンパイル: ghc -rtsopts -threaded -O2 -fllvm -Wall foo.hs

(落とす -llvm機能しない場合は)

実行: ./foo +RTS -N

module Main (main) where

import Data.Bits
import Data.Word
import Data.List
import qualified Data.IntSet as S 
import System.IO
import Control.Monad
import Control.Concurrent
import Control.Exception.Base (evaluate)

pairs' :: Int -> Word64 -> Int
pairs' n s = fromIntegral $ foldl' (.|.) 0 $ map mk [1..n]
  where mk d = let mask = 1 `shiftL` d - 1 
                   pc = popCount $! xor (s `shiftR` (n - d)) (s .&. mask)
               in  if pc <= 1 
                     then mask + 1 
                     else 0 

mkSet :: Int -> Word64 -> Word64 -> S.IntSet
mkSet n a b = S.fromList $ map (pairs' n) [a .. b]

f :: Int -> IO Int
f n 
   | n < 4 = return $ S.size $ mkSet n 0 mxbound
   | otherwise = do
        mvs <- replicateM 4 newEmptyMVar
        forM_ (zip mvs cpairs) $ \(mv,(mi,ma)) -> forkIO $ do
          evaluate (mkSet n mi ma) >>= putMVar mv
        set <- foldl' S.union S.empty <$> mapM readMVar mvs
        return $! S.size set
   where
     mxbound = 1 `shiftL` (n - 1)
     bounds = [0,1 `shiftL` (n - 3) .. mxbound]
     cpairs = zip bounds (drop 1 bounds)

main :: IO()
main = do
    hSetBuffering stdout LineBuffering
    mapM_ (f >=> print) [1..]

バッファリングの問題があります。cygwimコマンドラインから実行すると出力がまったく表示されないようです。

ソリューションを更新しましたが、それが大いに役立つかどうかわかりません。
バルタヴェル

@Lembikわからないことは明らかであるが、それはしてコンパイルする必要がある場合-O3、かつ迅速であるかもしれない-O3 -fllvm...
bartavelle

(そして、ソースコードの変更が発生していなければ、再コンパイルする前にすべてのビルドファイルを削除する必要があります)
-bartavelle

@Lembik:並列処理を導入しました。少し速くなるはずです。
-bartavelle

0

Python 2 + pypy、n = 22

一種のベースラインベンチマークとしての非常にシンプルなPythonソリューションを次に示します。

import itertools
def hamming(A, B):
    n = len(A)
    assert(len(B) == n)
    return n-sum([A[i] == B[i] for i in xrange(n)])

def prefsufflist(P):
    n = len(P)
    return [hamming(P[:i], P[n-i:n]) for i in xrange(1,n+1)]

bound = 1
for n in xrange(1,25):
    booleans = set()
    for P in itertools.product([0,1], repeat = n):
        booleans.add(tuple(int(HD <= bound) for HD in prefsufflist(P)))
    print "n = ", n, len(booleans)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.