근사 상관 관계 찾기


14

Slength 의 이진 문자열 을 고려하십시오 n. 에서 색인 1, 우리는 계산할 수 해밍 거리 사이 S[1..i+1]S[n-i..n]모두를 i에서 순서 0n-1. 길이가 같은 두 줄 사이의 해밍 거리는 해당 기호가 다른 위치 수입니다. 예를 들어

S = 01010

준다

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

이 때문입니다 0일치 0, 01에 해밍 거리 두 가지고 10, 010일치 010, 0101에 해밍 거리 네 가지가 있습니다 1010 그리고 마지막으로 01010자신을 일치합니다.

그러나 해밍 거리가 최대 1 인 출력에만 관심이 있습니다. 따라서이 작업 Y에서 해밍 거리가 최대 한 개인 지 아닌지 를보고합니다 N. 위의 예에서 우리는

[Y, N, Y, N, Y]

길이가 다른 가능한 모든 비트 문자열 을 반복 할 때 얻는 s 및 s f(n)의 개별 배열 수로 정의하십시오 .YN2^nSn

직무

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그 시간에 당신 의 점수가 가장 높습니다 .

동점 인 경우 첫 번째 답이 이깁니다.

내 코드는 어디에서 테스트됩니까?

cygwin 아래의 (약간 오래된) Windows 7 랩톱에서 코드를 실행합니다. 결과적으로이 작업을 쉽게 수행 할 수 있도록 도움을주십시오.

내 노트북에는 8GB의 RAM과 2 개의 코어와 4 개의 스레드가있는 Intel i7 5600U@2.6GHz (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 앤더스 Kaseorg 의해. (Vbox 아래의 Lubuntu 게스트 VM에서)
  • Anders Kaseorg의 Rust 에서 n = 31 .
  • NikoNyrh의 Clojure 에서 n = 29
  • bartavelle에 의해 C 에서 n = 29 .
  • Bartavelle의 Haskell 에서 n = 27
  • alephalpha에 의한 Pari / gp 에서 n = 24 .
  • N = 22 에서 파이썬 2 + pypy 내게로.
  • N = 21티카 alephalpha 의해. (자체보고)

미래 바운티

이제 2 분 안에 내 컴퓨터에서 최대 n = 80 에 이르는 답변에 대해 200 포인트의 현상금을 줄 것입니다.


누군가가 순진한 무차별 대입보다 더 빠른 알고리즘을 찾을 수 있도록하는 몇 가지 요령을 알고 있습니까? 이 도전이 아니라면 "x86에서 이것을 구현하십시오"(또는 GPU를 알고 있다면 ...).
Jonathan Allan

@JonathanAllan 매우 순진한 접근 방식을 가속화하는 것이 가능합니다. 정확히 얼마나 빨리 얻을 수 있는지 모르겠습니다. 흥미롭게도, 해밍 거리가 최대 0이면 Y를, 그렇지 않으면 N을 얻도록 질문을 변경하면 알려진 닫힌 형식 공식이 있습니다.

@Lembik CPU 시간 또는 실시간을 측정합니까?
flawr

@flawr 실시간으로 측정하고 있지만 몇 번 실행하고 이상을 제거하기 위해 최소값을 사용합니다.

답변:


9

녹 + CryptoMiniSat , n ≈ 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"

작동 원리

이것은 모든 부분 할당의 트리를 통해 Y / N 배열의 접두사에 대한 재귀 검색을 수행하고 SAT 솔버를 사용하여 각 단계에서 현재 부분 할당이 일치하는지 여부를 확인하고 그렇지 않은 경우 검색을 제거합니다. CryptoMiniSat은 XOR 절에 대한 특수 최적화로 인해이 작업에 적합한 SAT 솔버입니다.

구속 조건의 세 가지 패밀리는

S IS K + ID , 1 ≤ KN - 2, 0 ≤ I ≤ N - K ;
D KI 1D KI 2 ∨ ¬ K , 1 ≤ KN - 2, 0 ≤ I 1 < I 2N - K ; ¬ D ki 1 ∨ ⋯ ∨ ¬ D ki nk − 1
∨ ;k , 1 ≤ k n − 2의 경우 0 ≤ i 1 <⋯ < i nk − 1 n 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 빌드 시스템에 문제가있는 것처럼 들립니다.
Anders Kaseorg 2016 년

1
@Lembik 나는 그 페이스트에서 404를 얻고 있습니다.
Mego

1
@ChristianSievers 좋은 질문입니다. 그것은 작동하지만 조금 느리게 보입니다 (2 배 정도). 왜 그렇게 좋지 않아야하는지 잘 모르겠습니다. 따라서 CryptoMiniSat은 이러한 종류의 증분 워크로드에 맞게 최적화되지 않았습니다.
Anders Kaseorg

9

녹, n ≈ 30 또는 31 또는 32

내 랩탑 (두 코어, i5-6200U)에서 약 2.5GiB 의 메모리를 사용하여 n = 1,…, 31 초를 통해 31 초, 또는 약 5GiB를 사용하여 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 배열의 가능한 접두사를 재귀 적으로 검색하여 그 접두사를 얻을 수있는 가능한 문자열의 벡터를 취하지 만 중간에 검사되지 않은 문자열은 0으로 채워집니다. 즉, 이것은 여전히 ​​지수 검색이며 이러한 최적화는 다항식 요인에 의해서만 속도를 높입니다.
Anders Kaseorg

좋은 대답입니다. 감사합니다. 나는 누군가가 조합 속도로 파고 들어 상당한 속도를 얻길 바라고 있습니다.

@Lembik 나는 메모리 낭비 버그를 수정하고 더 미세 최적화를 수행했으며 병렬 처리를 추가했습니다. 기회가 생기면 다시 테스트하십시오. 점수를 1 또는 2 씩 높이고 싶습니다. 속도 향상을 위해 조합 아이디어가 있습니까? 나는 아무것도 오지 않았다.
Anders Kaseorg

1
@Lembik OEIS 항목에는 공식이 없습니다. Mathematica 코드도 무차별 대입을 사용하는 것 같습니다. 하나를 아는 경우 해당 코드를 알려줄 수 있습니다.
Christian Sievers 2016 년

6

BuDDy 라이브러리를 사용하는 C ++

다른 접근법은 (와 같은 이진 수식이 이진 결정도 의 비트 소요) S어떤 고정 값으로 제공에만 true 입력으로하고있다 Y거나 N특정 위치에서 선택한다. 해당 수식이 상수가 아닌 경우 자유 위치를 선택하고 되풀이하여 Y와를 모두 시도하십시오 N. 자유 위치가 없으면 가능한 출력 값을 찾았습니다. 수식이 상수 false이면 역 추적합니다.

가능한 값이 너무 적기 때문에 상대적으로 합리적으로 작동하기 때문에 종종 초기에 역 추적 할 수 있습니다. 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 나는 더 빨리 대답 할 수없는 아주 오래된 하드웨어에서 100 대 중 31 대를 얻었습니다
Christian Sievers

Windows에서이를 컴파일하는 방법 (예 : cygwin 사용)에 대해 감사의 말씀을 전합니다.

@Lembik 나는 Windws에 대해 모르지만 github.com/fd00/yacp/tree/master/buddy은 Cygwin에서 도움 WRT 보인다
기독교 승인 Sievers

1
와, 알았어.이 라이브러리를 툴킷에 추가해야한다고 확신했다. 잘 했어!
Anders Kaseorg

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를 조사하지 않았지만 그 시점에서 캐시가 부족할 수 있습니까?
Anders Kaseorg

와! 첫 번째 값이 높을수록 기본값 50000보다 훨씬 높은 값 bdd_init으로 호출하여 노드 테이블을 더 늘릴 수 있다고 생각합니다 bdd_setmaxincrease.-변경된 버전의 프로그램을 사용하고 있습니까?
Christian Sievers

2
나는 당신의 그래프를 좋아합니다.

1
당신은 옵션을 사용하여 충격 성능 향상을 얻을 수 --configuration=crafty( jumpytrendy유사한 결과를 제공).
Christian Sievers

2

파리 / GP , 23

기본적으로 Pari / GP는 스택 크기를 8MB로 제한합니다. 코드의 첫 번째 줄은 default(parisize, "4g")이 제한을 4GB로 설정합니다. 여전히 스택 오버플로가 발생하면 8GB로 설정할 수 있습니다.

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에 도달 한 후 stackoverflow를 제공합니다.

이제 24에 도착합니다.

2

Clojure, 29에서 75 38 초, 30에서 80 및 31에서 165

Intel i7 6700K의 런타임 , 메모리 사용량은 200MB 미만입니다.

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 항목)을 넘어 감지 된 패턴을 나타내는 정수 값 집합을 작성합니다. 그런 다음 이들을 "결합"하여 고유 카운트를 계산합니다. 스레딩 매크로 를 많이 사용하지만 코드가 너무 까다 롭지 않기를 바랍니다 . 내는 mainJVM은 예열 얻기 위해 테스트를 몇 번 실행됩니다.

업데이트 : 정수의 절반 만 반복하면 대칭으로 인해 동일한 결과를 얻습니다. 또한 비트 수가 더 높은 숫자는 숫자의 절반이 중복되므로 숫자를 건너 뜁니다.

사전 빌드 된 Uberjar ( v1 ) ( 3.7MB ) :

$ 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 도 존재하지만 클레이 풀에는 더 많은 기능과 조정 기능이 있습니다.


예, 배포하기가 쉽지 않습니다. 내 솔루션을 다시 평가할 시간이 있습니까? 지금 최대 30 개까지 얻을 수있을 것입니다. 더 이상 최적화가 보이지 않습니다.
NikoNyrh

슬프게도 그것은 30에 대한 아니오입니다. 경과 시간 : 217150.87386 밀리 초

Ahaa, 그것을 시도해 주셔서 감사합니다 : D 이것에 곡선을 맞추고 십진수 120 초가 소비 된 것을 보간하는 것이 좋을 수도 있지만 이것이 좋은 challgenge입니다.
NikoNyrh

1

수학, 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)을 의미했다 ... fixed
J42161217

1

매스 매 티카, 21

f [n_] : = 길이 @
     DeleteDuplicates @
      바꾸어 놓다@
       표 [2> Tr @ IntegerDigits [#, 2] & / @ 
         BitXor [BitShiftRight [#, n-i], Mod [#, 2 ^ i]], {i, 1, 
         n-1}] & @ Range [0, 2 ^ (n-1)];
수행 [인쇄 [n-> f @ n], {n, Infinity}]

비교를 위해 Jenny_mathy의 대답n = 19내 컴퓨터에서 제공 합니다.

가장 느린 부분은 Tr@IntegerDigits[#, 2] &입니다. Mathematica에 해밍 무게에 대한 빌트인이 없다는 것은 수치스러운 일입니다.


내 코드를 테스트하려면 Mathematica 무료 평가판을 다운로드 할 수 있습니다 .


1

내장 popcount를 사용하는 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에 도달 한 후 종료됩니다. 한도를 늘려야합니다.

세상에, 나는 벤치 마크 코드를 제거하는 것을 잊었다! 나는이 개 문제를 일으키는 줄을 제거합니다 : /
bartavelle

@Lembik가 수정되어야한다
bartavelle

1

하스켈, (비공식 n = 20)

이것은 최적화되지 않은 순진한 접근법입니다. 나는 그것이 다른 언어들과 얼마나 잘 어울릴 지 궁금했다.

사용 방법 ( 하스켈 플랫폼이 설치되어 있다고 가정 ) :

  • 한 파일에 코드를 붙여 넣습니다 approx_corr.hs(또는 다른 이름으로 적절하게 다음 단계를 수정하십시오)
  • 파일로 이동하여 실행 ghc approx_corr.hs
  • 운영 approx_corr.exe
  • 최대 값을 입력하십시오 n
  • 각 계산 결과와 해당 시점까지 누적 실시간 (ms)이 표시됩니다.

암호:

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 1loop n|n==maxn = return ()loop n = do printresult n (f n); t <- gettime; printtime (t-t0); loop (n+1). getmax예를 들어 getArgs프로그램 인수를 사용하는 데 사용할 수 있습니다.
Christian Sievers 2016 년

@ChristianSievers 감사합니다 !!! 나는 stackoverflow 에서이 질문을 했는데, 거기에 추가 할 수 있다면 좋을 것이라고 생각합니다!
flawr

거기에 어떻게 대답해야할지 모르겠습니다. 당신은 이미 비슷한 고리를 가지고 있고, 나는 시간을 얻는 것에 대해 아무 말도하지 않았습니다.
Christian Sievers

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 명령 행에서 실행하면 출력이 전혀없는 버퍼링 문제가 있습니다.

방금 솔루션을 업데이트했지만 많은 도움이 될지 모르겠습니다.
bartavelle

@Lembik 확실하지 않은지 확실하지 않지만,로 컴파일해야 -O3하고 다음과 같이 더 빠를 수도 있습니다 -O3 -fllvm.
bartavelle

(그리고 소스 코드가 변경되지 않은 경우 모든 빌드 파일을 다시 컴파일하기 전에 제거해야합니다.)
bartavelle

@Lembik : 병렬 처리를 도입했습니다. 조금 더 빨라야합니다.
bartavelle

0

파이썬 2 + 파이, 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.