Tìm mối tương quan gần đúng


14

Hãy xem xét một chuỗi nhị phân Scó độ dài n. Lập chỉ mục từ 1, chúng ta có thể tính khoảng cách Hamming giữa S[1..i+1]S[n-i..n]cho tất cả itheo thứ tự từ 0đến n-1. Khoảng cách Hamming giữa hai chuỗi có độ dài bằng nhau là số vị trí mà tại đó các ký hiệu tương ứng khác nhau. Ví dụ,

S = 01010

cho

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

Điều này là do các 0trận đấu 0, 01có Hamming khoảng cách hai đến 10, các 010trận đấu 010, 0101có Hamming khoảng cách bốn đến 1010 và cuối cùng là 01010chính nó.

Tuy nhiên, chúng tôi chỉ quan tâm đến kết quả đầu ra trong đó khoảng cách Hamming nhiều nhất là 1. Vì vậy, trong nhiệm vụ này, chúng tôi sẽ báo cáo Ynếu khoảng cách Hamming nhiều nhất là một và Nkhác. Vì vậy, trong ví dụ của chúng tôi ở trên, chúng tôi sẽ nhận được

[Y, N, Y, N, Y]

Xác định f(n)là số mảng riêng biệt của Ys và Ns nhận được khi lặp trên tất cả 2^ncác chuỗi bit có thể có Sđộ dài khác nhaun .

Bài tập

Để tăng nbắt đầu từ 1, mã của bạn sẽ xuất raf(n) .

Ví dụ câu trả lời

Đối với n = 1..24, câu trả lời đúng là:

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

Chấm điểm

Mã của bạn sẽ lặp đi lặp lại từ n = 1việc đưa ra câu trả lời cho mỗin lượt. Tôi sẽ thời gian chạy toàn bộ, giết nó sau hai phút.

Điểm của bạn là cao nhất n bạn đạt được trong thời gian đó.

Trong trường hợp hòa, câu trả lời đầu tiên sẽ thắng.

Mã của tôi sẽ được kiểm tra ở đâu?

Tôi sẽ chạy mã của bạn trên máy tính xách tay Windows 7 (hơi cũ) của tôi dưới cygwin. Do đó, vui lòng cung cấp bất kỳ trợ giúp nào bạn có thể để giúp thực hiện điều này dễ dàng.

Máy tính xách tay của tôi có RAM 8GB và CPU Intel i7 5600U@2.6 GHz (Broadwell) với 2 lõi và 4 luồng. Bộ hướng dẫn bao gồm SSE4.2, AVX, AVX2, FMA3 và TSX.

Các mục hàng đầu cho mỗi ngôn ngữ

  • n = 40Rust sử dụng CryptoMiniSat, bởi Anders Kaseorg. (Trong máy khách của Lubfox trong Vbox.)
  • n = 35 trong C ++ bằng thư viện BuDDy, bởi Christian Seviers. (Trong máy khách của Lubfox trong Vbox.)
  • n = 34 trong Clingo của tác giả Anders Kaseorg. (Trong máy khách của Lubfox trong Vbox.)
  • n = 31 trong Rust của tác giả Anders Kaseorg.
  • n = 29 trong Clojure của NikoNyrh.
  • n = 29 in C bằng bartavelle.
  • n = 27 trong Haskell bởi bartavelle
  • n = 24 tính bằng Pari / gp bởi alephalpha.
  • n = 22 trong Python 2 + pypy của tôi.
  • n = 21 trong Mathematica bởi alephalpha. (Tự báo cáo)

Tiền thưởng trong tương lai

Bây giờ tôi sẽ đưa ra một khoản tiền thưởng 200 điểm cho bất kỳ câu trả lời nào đạt tới n = 80 trên máy của tôi trong hai phút.


Bạn có biết một số mẹo sẽ cho phép ai đó tìm ra thuật toán nhanh hơn lực lượng vũ phu ngây thơ không? Nếu không, thách thức này là "vui lòng thực hiện điều này trong x86" (hoặc có thể nếu chúng tôi biết GPU của bạn ...).
Jonathan Allan

@Jonathan ALLan Chắc chắn có thể tăng tốc một cách tiếp cận rất ngây thơ. Chính xác thì bạn có thể nhanh đến mức nào. Thật thú vị, nếu chúng tôi thay đổi câu hỏi để bạn nhận được Y nếu khoảng cách Hamming nhiều nhất là 0 và N khác, thì có một công thức dạng đóng đã biết.

@Lembik Chúng ta đo thời gian CPU hay thời gian thực?
flawr

@flawr Tôi đang đo thời gian thực nhưng chạy nó vài lần và lấy mức tối thiểu để loại bỏ sự kỳ quặc.

Câu trả lời:


9

Rust + 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"

Làm thế nào nó hoạt động

Điều này thực hiện tìm kiếm đệ quy thông qua cây của tất cả các phép gán một phần cho các tiền tố của mảng Y / N, sử dụng bộ giải SAT để kiểm tra tại mỗi bước xem liệu phép gán một phần hiện tại có phù hợp hay không và tìm kiếm nếu không. CryptoMiniSat là trình giải SAT phù hợp cho công việc này do tối ưu hóa đặc biệt cho các mệnh đề XOR.

Ba họ ràng buộc là

S iS k + iD ki , với 1 kn - 2, 0 ≤ i ≤ n - k ;
D ki 1D ki 2A k , với 1 ≤ kn - 2, 0 ≤ i 1 < i 2n - k ;
¬ D ki 1 ∨ ⋯ ∨ ¬ D ki n - k - 1Một k , 1 ≤ kn - 2, 0 ≤ i 1 <⋯ < i n - k - 1n - k ;

ngoại trừ việc tối ưu hóa, S 0 bị buộc thành false, do đó D k 0 đơn giản bằng S k .


2
Woohoooooo! :)

Tôi vẫn đang cố gắng biên dịch cái này trong Windows (sử dụng cygwin + gcc). Tôi đã nhân bản cryptominisat và biên dịch nó. Nhưng tôi vẫn không biết cách biên dịch mã gỉ. Khi tôi làm cargo buildtôi nhận được--- stderr CMake Error: Could not create named generator Visual Studio 14 2015 Win64

2
@ rahnema1 Cảm ơn, nhưng có vẻ như vấn đề xảy ra với hệ thống xây dựng CMake của thư viện C ++ được nhúng trong thùng cryptominisat, không phải với chính Rust.
Anders Kaseorg

1
@Lembik Tôi nhận được 404 từ dán đó.
Mego

1
@ChristianSievers Câu hỏi hay. Điều đó hoạt động nhưng dường như chậm hơn một chút (2 × hoặc hơn). Tôi không chắc tại sao nó không tốt như vậy, vì vậy có lẽ CryptoMiniSat chưa được tối ưu hóa tốt cho loại khối lượng công việc gia tăng đó.
Anders Kaseorg

9

Rỉ sét, n ≈ 30 hoặc 31 hoặc 32

Trên máy tính xách tay của tôi (hai lõi, i5-6200U), điều này có được thông qua n = 1 , Lít , 31 trong 53 giây, sử dụng khoảng 2,5 GiB bộ nhớ hoặc qua n = 1, Lỗi , 32 trong 105 giây, sử dụng khoảng 5 GiB của bộ nhớ. Biên dịch với cargo build --releasevà chạy 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"

Hãy thử trực tuyến!

Tôi cũng có một biến thể chậm hơn một chút bằng cách sử dụng bộ nhớ rất ít.


Những gì tối ưu hóa bạn đã sử dụng?

1
@Lembik Tối ưu hóa lớn nhất, bên cạnh việc thực hiện mọi thứ với số học bitwise trong một ngôn ngữ được biên dịch, là chỉ sử dụng càng nhiều điều không cần thiết khi cần thiết để tìm ra tiền tố của mảng Y / N. Tôi thực hiện tìm kiếm đệ quy trên các tiền tố có thể có của mảng Y / N, mang theo một vectơ các chuỗi có thể đạt được tiền tố đó, nhưng chỉ các chuỗi có phần giữa không được giải thích được chứa đầy các số không. Điều đó nói rằng, đây vẫn là một tìm kiếm theo cấp số nhân và những tối ưu hóa này chỉ tăng tốc nó bởi các yếu tố đa thức.
Anders Kaseorg

Đó là một câu trả lời tốt đẹp. Cảm ơn bạn. Tôi hy vọng ai đó sẽ đào sâu vào tổ hợp để tăng tốc đáng kể.

@Lembik Tôi đã sửa một lỗi lãng phí bộ nhớ, thực hiện tối ưu hóa vi mô nhiều hơn và thêm tính song song. Vui lòng kiểm tra lại khi bạn có cơ hội. Tôi hy vọng sẽ tăng điểm của mình lên 1 hoặc 2. Bạn có ý tưởng kết hợp nào cho việc tăng tốc lớn hơn không? Tôi đã không nghĩ ra bất cứ điều gì.
Anders Kaseorg

1
@Lembik Không có công thức nào được đưa ra tại mục OEIS. (Mã Mathicala dường như cũng sử dụng brute-force.) Nếu bạn biết về một mã, bạn có thể muốn nói với họ về nó.
Christian Sievers

6

C ++ sử dụng thư viện BuDDy

Một cách tiếp cận khác: có một công thức nhị phân (như sơ đồ quyết định nhị phân ) lấy các bit Slàm đầu vào và là iff thực sự cung cấp một số giá trị cố định của Yhoặc Ntại các vị trí được chọn nhất định. Nếu công thức đó không phải là hằng sai, hãy chọn một vị trí miễn phí và lặp lại, thử cả haiYN. Nếu không có vị trí miễn phí, chúng tôi đã tìm thấy một giá trị đầu ra có thể. Nếu công thức là hằng sai, quay lui.

Điều này hoạt động tương đối hợp lý vì có rất ít giá trị có thể để chúng ta thường có thể quay lại sớm. Tôi đã thử một ý tưởng tương tự với người giải SAT, nhưng điều đó ít thành công hơn.

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

Để biên dịch với debian 8 (jessie), hãy cài đặt libbdd-dev và làm g++ -std=c++11 -O3 -o hb hb.cpp -lbdd. Nó có thể hữu ích để tăng đối số đầu tiên bdd_inithơn nữa.


Điều này có vẻ thú vị. Bạn làm gì với điều này?

@Lembik Tôi nhận được 31 trong 100 giây trên phần cứng rất cũ sẽ không cho phép tôi trả lời nhanh hơn
Christian Sievers

Bất kỳ trợ giúp nào bạn có thể cung cấp về cách biên dịch này trên Windows (ví dụ như sử dụng cygwin) đều nhận được một cách biết ơn.

@Lembik Tôi không biết về Windws nhưng github.com/fd00/yacp/tree/master/buddy có vẻ hữu ích wrt cygwin
Christian Sievers

1
Ồ, được rồi, bạn đã thuyết phục tôi rằng tôi cần thêm thư viện này vào bộ công cụ của mình. Làm tốt!
Anders Kaseorg

4

Clingo, n ≈ 30 hoặc 31 34

Tôi hơi ngạc nhiên khi thấy năm dòng mã Clingo đã vượt qua giải pháp Rust vũ phu của tôi và đến rất gần với giải pháp BuDDy của Christian. Có vẻ như nó cũng sẽ đánh bại nó với giới hạn thời gian cao hơn.

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

âm mưu


Điều đó thật tuyệt! Nhìn từ biểu đồ của bạn, giải pháp BuDDy đột nhiên trở nên tồi tệ hơn. Bất cứ ý tưởng tại sao?

@Lembik Tôi chưa điều tra BuDDy đủ để chắc chắn, nhưng có lẽ nó đã hết bộ nhớ cache vào thời điểm đó?
Anders Kaseorg

Ồ Tôi nghĩ rằng giá trị đầu tiên cao hơn bdd_initcó thể giúp hoặc cho phép tăng bảng nút nhiều hơn bằng cách gọi bdd_setmaxincreasevới giá trị cao hơn mặc định 50000. - Bạn có đang sử dụng phiên bản thay đổi của chương trình của tôi không?
Christian Sievers

2
Tôi thích đồ thị của bạn.

1
Bạn nhận được một sự gia tăng hiệu suất gây sốc bằng cách sử dụng tùy chọn --configuration=crafty( jumpytrendycho kết quả tương tự).
Christian Sievers

2

Pari / GP , 23

Theo mặc định, Pari / GP giới hạn kích thước ngăn xếp của nó là 8 MB. Dòng đầu tiên của mã default(parisize, "4g"), đặt giới hạn này là 4 GB. Nếu nó vẫn cung cấp luồng stackover, bạn có thể đặt thành 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)))

Đạt 22 và sau đó cung cấp một stackoverflow.

Đến 24 giờ.

2

Clojure, 29 trong 75 38 giây, 30 trong 80 và 31 trong 165

Thời gian chạy từ Intel i7 6700K , sử dụng bộ nhớ dưới 200 MB.

project.clj (sử dụng com.climate / claypoole cho đa luồng):

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

Mã nguồn:

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

Một giải pháp brute-force, mỗi luồng đi qua một tập hợp con của phạm vi (2 ^ 12 mục) và xây dựng một tập hợp các giá trị nguyên cho biết các mẫu được phát hiện. Sau đó chúng được "kết hợp" lại với nhau và do đó, số lượng riêng biệt được tính toán. Tôi hy vọng mã không quá phức tạp để theo dõi mặc dù nó sử dụng macro phân luồng rất nhiều. Tôi mainchạy thử nghiệm một vài lần để làm nóng JVM.

Cập nhật: Lặp lại chỉ hơn một nửa số nguyên, nhận được kết quả tương tự do tính đối xứng. Đồng thời bỏ qua các số có số bit cao hơn ở nửa dưới của số vì chúng cũng tạo ra các bản sao.

Uberjar dựng sẵn ( 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]

Kết quả trên các phần cứng khác nhau, thời gian chạy dự kiến ​​là 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

Bạn có thể dễ dàng thực hiện chuỗi đơn này và tránh sự phụ thuộc của bên thứ 3 bằng cách sử dụng tiêu chuẩn cho:

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

Vâng, pmap tích hợp cũng tồn tại nhưng claypoole có nhiều tính năng và khả năng điều chỉnh hơn.


Vâng, nó làm cho nó tầm thường để phân phối. Bạn có thời gian để đánh giá lại giải pháp của tôi không, tôi khá chắc chắn rằng bạn sẽ nhận được nó - đến 30 giờ. Tôi không có tối ưu hóa hơn nữa trong tầm nhìn.
NikoNyrh

Đáng buồn thay, nó không có trong 30. Thời gian đã trôi qua: 217150.87386 msecs

Ahaa, cảm ơn vì đã thử: D Có thể tốt hơn để phù hợp với một đường cong trên điều này và nội suy rằng tại đó giá trị thập phân 120 giây được sử dụng nhưng ngay cả khi đây là một challgenge tốt đẹp.
NikoNyrh

1

Toán học, n = 19

nhấn alt +. hủy bỏ và kết quả sẽ được in

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]

Tôi không thể chạy cái này để bạn có thể giải thích làm thế nào nó tránh mất thời gian theo cấp số nhân? 2 ^ 241 là một con số rất lớn!

Bạn có thể hiển thị đầu ra của mã?

1
Ý tôi là f (n) ... đã sửa
J42161217

1

Toán học, 21

f [n_]: = Độ dài @
     XóaD trùng lặp @
      Chuyển đổi @
       Bảng [2> Tr @ IntegerDigits [#, 2] & / @ 
         BitXor [BitShiftRight [#, n - i], Mod [#, 2 ^ i]], {i, 1, 
         n - 1}] & @ Phạm vi [0, 2 ^ (n - 1)];
Làm [In [n -> f @ n], {n, Infinity}]

Để so sánh, câu trả lời của Jenny_mathy đưa ra n = 19trên máy tính của tôi.

Phần chậm nhất là Tr@IntegerDigits[#, 2] & . Thật đáng tiếc khi Mathicala không tích hợp sẵn cho trọng lượng Hamming.


Nếu bạn muốn kiểm tra mã của tôi, bạn có thể tải xuống bản dùng thử miễn phí của Mathematica .


1

Phiên bản AC, sử dụng số lượng tích hợp

Hoạt động tốt hơn với clang -O3, nhưng cũng hoạt động nếu bạn chỉ có 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;
}

Nó đến 24 rất nhanh và sau đó kết thúc. Bạn cần tăng giới hạn.

Trời ơi, tôi quên xóa mã điểm chuẩn! Tôi sẽ xóa hai dòng vi phạm: /
bartavelle

@Lembik nên được sửa ngay bây giờ
bartavelle

1

Haskell, (không chính thức n = 20)

Đây chỉ là cách tiếp cận ngây thơ - cho đến nay mà không có bất kỳ tối ưu hóa. Tôi tự hỏi nó sẽ tốt như thế nào so với các ngôn ngữ khác.

Cách sử dụng (giả sử bạn đã cài đặt nền tảng haskell ):

  • Dán mã vào một tệp approx_corr.hs (hoặc bất kỳ tên nào khác, sửa đổi các bước sau cho phù hợp)
  • Điều hướng đến tệp và thực thi ghc approx_corr.hs
  • Chạy approx_corr.exe
  • Nhập tối đa n
  • Kết quả của mỗi tính toán được hiển thị, cũng như thời gian thực tích lũy (tính bằng ms) cho đến thời điểm đó.

Mã số:

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

Mã xuất hiện không cho đầu ra khi nó chạy. Điều này làm cho nó một chút khó khăn để kiểm tra.

Lạ thật, nó có biên dịch không có lỗi không? Điều gì xảy ra nếu bạn cố gắng biên dịch chương trình main = putStrLn "Hello World!"?
flawr

Các Data.Bitsmô-đun có thể hữu ích. Đối với vòng lặp chính của bạn, bạn có thể sử dụng một cái gì đó như main = do maxn <- getmax; t0 <- gettime; loop 1ở đâu loop n|n==maxn = return ()loop n = do printresult n (f n); t <- gettime; printtime (t-t0); loop (n+1). getmaxví dụ có thể sử dụng getArgsđể sử dụng các đối số chương trình.
Christian Sievers

@ChristianSievers Cảm ơn rất nhiều !!! Tôi đã hỏi câu hỏi này tại stackoverflow, tôi nghĩ sẽ thật tuyệt nếu bạn có thể thêm nó vào đó!
flawr

Tôi không thấy làm thế nào để trả lời ở đó. Bạn đã có một vòng lặp tương tự ở đó và tôi không nói gì về việc có thời gian: rằng bạn đã có ở đây.
Christian Sievers

1

Một giải pháp Haskell, sử dụng popCount và quản lý song song

Biên dịch: ghc -rtsopts -threaded -O2 -fllvm -Wall foo.hs

(bỏ -llvmnếu nó không hoạt động)

Chạy : ./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..]

Có một vấn đề về bộ đệm, dường như tôi không nhận được bất kỳ đầu ra nào nếu tôi chạy nó từ dòng lệnh cygwim.

Tôi vừa cập nhật giải pháp của mình, nhưng tôi không biết liệu nó có giúp được gì nhiều không.
bartavelle

@Lembik Không chắc chắn nếu điều đó là hiển nhiên, nhưng điều đó nên được biên dịch -O3và có thể nhanh hơn với -O3 -fllvm...
bartavelle

(Và tất cả các tệp xây dựng nên được xóa trước khi biên dịch lại, nếu không xảy ra thay đổi mã nguồn)
bartavelle

@Lembik: Tôi giới thiệu song song. Nó sẽ nhanh hơn một chút.
bartavelle

0

Con trăn 2 +, n = 22

Đây là một giải pháp Python thực sự đơn giản như một loại điểm chuẩn cơ bản.

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)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.