寻找近似相关


14

考虑一个S长度为string 的二进制字符串n。从索引中1,我们可以计算出海明距离之间S[1..i+1],并S[n-i..n]为所有i从订单0n-1。等长的两个字符串之间的汉明距离是相应符号不同的位置数。例如,

S = 01010

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

这是因为0matchs 001具有汉明距离2到10010matches 0100101具有汉明距离4到1010 最后01010匹配自己。

但是,我们仅对汉明距离最大为1的输出感兴趣。因此,在此任务中,我们将报告Y汉明距离是否最多为1,N否则为否。因此,在上面的示例中,我们将获得

[Y, N, Y, N, Y]

定义f(n)Ys和Ns在2^n不同S长度的所有可能位字符串上迭代时得到的不同数组的数目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。我将为整个运行计时,两分钟后将其终止。

您的分数是n您当时获得的最高分。

如果是平局,则第一个答案将获胜。

我的代码将在哪里进行测试?

我将在cygwin下的我的Windows 7笔记本电脑上运行您的代码。因此,请提供一切可能的帮助,以简化此过程。

我的笔记本电脑具有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 guest虚拟机中。)
  • Christian Seviers撰写的使用BuDDy库在C ++中使用n = 35。(在Vbox下的Lubuntu guest虚拟机中。)
  • n = 34,安德斯·卡塞格(Anders Kaseorg)在克林戈(Clingo)。(在Vbox下的Lubuntu guest虚拟机中。)
  • N = 31锈病由Anders Kaseorg。
  • NikoNyrh 在Clojure中n = 29
  • n = 29 in C由手枪演奏。
  • n = 27Haskell由bartavelle
  • n = 24(Pari / gp为单位)。
  • 在我的Python 2 + pypy中n = 22
  • Mathematica中,alephalpha的n = 21。(自我报告)

未来赏金

现在,对于两分钟之内在我的计算机上达到n = 80的任何答案,我将给予200点奖励。


您是否知道一些技巧,可以使某人找到比幼稚的蛮力更快的算法?如果不是这样,那么挑战就是“请在x86中实现”(或者也许如果我们知道您的GPU ...)。
乔纳森·艾伦,

@JonathanAllan当然可以加快非常幼稚的方法。我不确定您能得到多快。有趣的是,如果我们更改了问题,以使汉明距离最大为0时得到Y,否则为N,那么就有一个已知的封闭式公式。

@Lembik我们测量CPU时间还是实时时间?
瑕疵的

@flawr我正在测量实时,但是运行了几次,并花了最少的时间来消除怪异。

Answers:


9

锈+ 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数组前缀的树。由于对XOR子句进行了特殊的优化,因此CryptoMiniSat是适合此工作的SAT解算器。

约束的三个家族是

小号小号ķ + d ,1≤ ķñ - 2,0≤I≤ Ñ - ķ ;
d 1d 2 ∨¬ ķ,对于1≤ ķñ - 2,0≤ 1 < 2Ñ - ķ ;
¬ d 1 ∨⋯∨¬ d ñ - ķ - 1ķ,对于1≤ ķñ - 2,0≤ 1 <⋯< ñ - ķ - 1ñ - ķ ;

除此之外,作为优化,S 0被强制为false,因此D k 0仅等于S k


2
呜呜呜!:)

我仍在尝试在Windows(使用cygwin + gcc)中进行编译。我克隆了cryptominisat并进行了编译。但是我仍然不知道如何编译rust代码。我何时cargo build收到--- stderr CMake Error: Could not create named generator Visual Studio 14 2015 Win64

2
@ rahnema1谢谢,但听起来问题出在cryptominisat板条箱中嵌入式C ++库的CMake构建系统中,而不是Rust本身。
Anders Kaseorg

1
@Lembik我从粘贴中得到了404。
Mego

1
@ChristianSievers好问题。那行得通,但似乎要慢一些(大约2倍)。我不确定为什么它不应该那么好,所以也许CryptoMiniSat尚未针对此类增量工作负载进行优化。
Anders Kaseorg

9

锈,n≈30 31或32

在我的笔记本电脑(两个内核,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数组的可能前缀进行递归搜索,并采用实现该前缀的可能字符串的向量,但只对未检查中间的字符串填充零。也就是说,这仍然是指数搜索,而这些优化只能通过多项式因子来加快搜索速度。
Anders Kaseorg

这是一个很好的答案。谢谢。我希望有人能够深入研究组合技术,从而显着提高速度。

@Lembik我已经修复了内存浪费的错误,进行了更多的微优化,并增加了并行性。如果有机会,请重新测试-我希望将我的分数提高1或2。您是否考虑过组合想法以提高速度?我什么都没想。
Anders Kaseorg

1
@Lembik OEIS条目中没有给出公式。(那里的Mathematica代码似乎也使用了蛮力。)如果您知道其中之一,则可能要告诉他们。
Christian Sievers

6

使用BuDDy库的 C ++

一种不同的方法:具有一个二进制公式(作为二进制决策图),该二进制公式将的位S作为输入,并且在给出某些选定位置YN某些选定位置的固定值时为true 。如果该公式不是常数false,请选择一个自由位置并递归,同时尝试YN。如果没有自由位置,我们发现了可能的输出值。如果公式为常数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似乎对wrt cygwin很有帮助
Christian Sievers

1
哇,好吧,您已经使我确信我需要将此库添加到我的工具箱中。做得好!
Anders Kaseorg

4

Clingo,n≈30 或31 34

看到五行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

哇!我认为较高的第一个值bdd_init可能会有所帮助,或者通过以bdd_setmaxincrease远远超过默认值50000的值进行调用来允许更多地增加节点表。-您是否正在使用程序的已更改版本?
Christian Sievers

2
我确实喜欢你的图表。

1
通过使用该选项,您将获得惊人的性能提升--configuration=craftyjumpytrendy给出相似的结果)。
Christian Sievers

2

巴黎/ GP,23

默认情况下,Pari / GP将其堆栈大小限制为8 MB。代码的第一行default(parisize, "4g")将此限制设置为4 GB。如果仍然提供stackoverflow,则可以将其设置为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,然后给出stackoverflow。

现在到24。

2

Clojure,75秒内29 秒,80秒内30秒和165秒内31

Intel 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个问题。我没有进一步的优化。
NikoNyrh

不幸的是,这是30的否定。经过的时间:217150.87386毫秒

Ahaa,谢谢您的尝试:D最好在曲线上进行插值,然后插值,在该值上花费120秒的十进制值可能更好,但即使这样,这也是一个不错的挑战。
NikoNyrh

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)];
执行[打印[n-> f @ n],{n,无穷大}]

为了便于比较,Jenny_mathy的回答给了n = 19我的电脑上。

最慢的部分是 Tr@IntegerDigits[#, 2] &。可惜Mathematica没有内置的汉明重量。


如果要测试我的代码,可以下载Mathematica的免费试用版


1

AC版本,使用内置popcount

与之搭配使用效果更好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

Haskell,(非正式n = 20)

这只是幼稚的方法-到目前为止尚未进行任何优化。我想知道它与其他语言相比效果如何。

使用方法(假设您有haskell平台安装):

  • 将代码粘贴到一个文件中 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!"怎样?
瑕疵的

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用来使用程序参数。
Christian Sievers

@ChristianSievers非常感谢!!!我在stackoverflow上问了这个问题,如果您也可以在此添加它,那会很好!
瑕疵的

我在那里看不到答案。您已经有一个类似的循环,而我没有说出时间:您已经在这里。
Christian Sievers

1

Haskell解决方案,使用popCount和手动管理的并行性

编译: 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

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)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.