Yaklaşık korelasyonları bulma


14

İkili bir Suzunluk dizesi düşünün n. Dan endekslenmesi 1, biz hesaplayabilir Hamming mesafeleri arasındaki S[1..i+1]ve S[n-i..n]herkes için isırayla 0için n-1. Eşit uzunlukta iki tel arasındaki Hamming mesafesi, karşılık gelen sembollerin farklı olduğu konumların sayısıdır. Örneğin,

S = 01010

verir

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

Bunun nedeni ise 0maçları 0, 01için Hamming mesafesi iki sahiptir 10, 010kibrit 010, 0101için Hamming mesafesi dört sahiptir 1010 ve son olarak 01010kendini eşleşir.

Bununla birlikte, sadece Hamming mesafesinin en fazla 1 olduğu çıkışlarla ilgileniyoruz. Bu nedenle, bu görevde YHamming mesafesinin en fazla bir olup olmadığını bildireceğiz N. Yani yukarıdaki örneğimizde

[Y, N, Y, N, Y]

Olası tüm farklı uzunluktaki bit dizelerini yinelediğinde f(n), Ys ve Ns'nin farklı dizilerinin sayısı olarak tanımlayın .2^nSn

Görev

nBaşlangıç ​​noktasını artırmak 1için kodunuzun çıktısı alınmalıdır f(n).

Örnek cevaplar

Çünkü n = 1..24, doğru cevaplar:

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

puanlama

Kodunuzun n = 1her biri için cevap vermesini yinelemesi gerekir n. Tüm koşuyu zamanlayacağım, iki dakika sonra öldüreceğim.

Puanınız no zaman aldığınız en yüksek puan .

Beraberlik durumunda, ilk cevap kazanır.

Kodum nerede test edilecek?

Kodunuzu cygwin altındaki (biraz eski) Windows 7 dizüstü bilgisayarımda çalıştıracağım. Sonuç olarak, lütfen bunu kolaylaştırmak için yapabileceğiniz her türlü yardımı verin.

Dizüstü bilgisayarımda 8GB RAM ve 2 çekirdekli ve 4 iş parçacıklı Intel i7 5600U@2.6 GHz (Broadwell) CPU var. Komut kümesi SSE4.2, AVX, AVX2, FMA3 ve TSX'i içerir.

Her dil için önde gelen girişler

  • N = 40 içinde Rust Anders Kaseorg tarafından CryptoMiniSat kullanılarak. (Lubuntu'da konuk VM, Vbox altında.)
  • n = 35 olarak C ++ Christian Seviers tarafından BUDDY kitaplığı kullanılarak. (Lubuntu'da konuk VM, Vbox altında.)
  • n = 34 içinde Clingo Anders Kaseorg ile. (Lubuntu'da Vbox altındaki konuk VM.)
  • n = 31 içinde Rust Anders Kaseorg ile.
  • n = 29 içinde Clojure NikoNyrh ile.
  • bartavelle ile C'de n = 29 .
  • n = 27 içinde Haskell'e Bartavelle göre
  • n = 24 , alefalfal tarafından Pari / gp'de.
  • n = 22 yılında Python 2 + PYPY bana göre.
  • n = 21 içinde Mathematica'nın alephalpha ile. (Kendini bildirdi)

Gelecek ödülleri

Şimdi iki dakika içinde makinemde n = 80'e ulaşan herhangi bir cevap için 200 puanlık bir ödül vereceğim .


Birinin saf bir kaba kuvvetten daha hızlı bir algoritma bulmasına izin verecek bir numara biliyor musunuz? Bu sorun "lütfen x86'da uygulayın" değilse (veya GPU'nuzu biliyorsak ...).
Jonathan Allan

@JonathanAllan Çok naif bir yaklaşımı hızlandırmak kesinlikle mümkündür. Tam olarak ne kadar hızlı alabileceğinizden emin değilim. İlginç bir şekilde, eğer Hamming mesafesi en fazla 0 ise bir Y ve aksi takdirde N olursa Y'yi elde etmek için soruyu değiştirirsek, bilinen bir kapalı form formülü vardır.

@Lembik CPU zamanını mı yoksa gerçek zamanı mı ölçeriz?
flawr

@flawr Gerçek zamanlı ölçüm yapıyorum ama birkaç kez çalıştırıyorum ve tuhaflıkları ortadan kaldırmak için minimum değeri alıyorum.

Yanıtlar:


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"

Nasıl çalışır

Bu, mevcut kısmi atamanın tutarlı olup olmadığını kontrol etmek için bir SAT çözücü kullanarak Y / N dizisinin öneklerine tüm kısmi atamaların ağacında özyinelemeli bir arama yapar ve değilse aramayı budama. CryptoMiniSat, XOR cümleleri için özel optimizasyonları nedeniyle bu iş için doğru SAT çözücüsüdür.

Üç kısıtlama ailesi

S iS k + iD ki , 1 ≤ kn - 2 için, 0 ≤ i ≤ n - k ;
D ki 1D ki 2 ∨ ¬ A k , 1 ≤ kn - 2 için, 0 ≤ i 1 < i 2n - k ;
¬ D Ki 1 ∨ ⋯ ∨ ¬ D Ki N - k - 1A k , 1 ≤ kn - 2 için, 0 ≤ i 1 <⋯ < i n - k - 1n - k ;

bir iyileştirme, yani hariç S 0 böylece yanlış zorlanır D k 0 sadece da eşit S k .


2
Woohoooooo! :)

Hala Windows (cygwin + gcc kullanarak) derlemeye çalışıyorum. Kriptominisi klonladım ve derledim. Ama hala pas kodunu nasıl derleyeceğimi bilmiyorum. Ben ne zaman cargo buildalıyorum--- stderr CMake Error: Could not create named generator Visual Studio 14 2015 Win64

2
@ rahnema1 Teşekkürler, ama sorun Rust ile değil, kriptominisat sandık içindeki gömülü C ++ kütüphanesinin CMake derleme sistemi ile ilgili gibi görünüyor.
Anders Kaseorg

1
@Lembik Şu macundan 404 tane alıyorum.
Mego

1
@ChristianSievers İyi soru. Bu işe yarıyor ama biraz daha yavaş görünüyor (2 × ya da öylesine). Neden bu kadar iyi olmaması gerektiğinden emin değilim, bu yüzden belki de CryptoMiniSat bu tür artımlı iş yükü için iyi optimize edilmemiştir.
Anders Kaseorg

9

Pas, n ≈ 30 veya 31 veya 32

Dizüstü bilgisayarımda (iki çekirdek, i5-6200U), bu , yaklaşık 2.5 GiB bellek kullanarak 53 saniyede n = 1,…, 31 veya yaklaşık 5 GiB kullanarak n = 1,…, 32 bellek. Derleyin cargo build --releaseve çalıştırın 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"

Çevrimiçi deneyin!

Ayrıca çok daha az bellek kullanan biraz daha yavaş bir değişkenim var .


Hangi optimizasyonları kullandınız?

1
@Lembik En büyük optimizasyon, derlenmiş bir dilde bitsel aritmetik ile her şeyi yapmanın yanı sıra, Y / N dizisinin önekini çivilemek için sadece gerektiği kadar belirsizliği kullanmaktır. Y / N dizisinin olası öneklerinde özyinelemeli bir arama yapıyorum, bu önek elde eden olası dizelerin bir vektörü, ancak sadece incelenmemiş ortaları sıfırlarla dolu dizeleri alıyorum. Bununla birlikte, bu hala üstel bir aramadır ve bu optimizasyonlar sadece polinom faktörleri tarafından hızlandırılmaktadır.
Anders Kaseorg

Güzel bir cevap. Teşekkür ederim. Birisinin önemli bir hız kazanmak için kombinatoriklere gireceğini umuyorum.

@Lembik Bellek israfını giderdim, daha fazla mikro optimizasyon yaptım ve paralellik ekledim. Lütfen bir şansınız olduğunda tekrar test edin — Puanımı 1 veya 2 oranında artırmayı umuyorum. Daha büyük hızlamalar için akılda birleştirici fikirleriniz var mı? Ben bir şey bulamadım.
Anders Kaseorg

1
@Lembik OEIS girişinde formül bulunmuyor. (Oradaki Mathematica kodu da kaba kuvvet kullanıyor gibi görünüyor.) Eğer birini biliyorsanız, bundan bahsetmek isteyebilirsiniz.
Christian Sievers

6

BuDDy kitaplığını kullanarak C ++

Farklı bir yaklaşım: bitleri girdi olarak alan ve seçilen belirli konumlarda veya bazı sabit değerlerde sabit değerler veren doğru bir ikili formüle ( ikili karar diyagramı olarak ) sahip olmak . Bu formül sürekli yanlış değilse, hem ve hem de çalışarak serbest bir konum ve recurse seçin . Serbest konum yoksa, olası bir çıkış değeri bulduk. Formül sürekli yanlışsa, geri gitme.SYNYN

Bu nispeten makul çalışır, çünkü erken geri dönebilmemiz için çok az olası değer vardır. Benzer bir fikri SAT çözücü ile denedim, ancak bu daha az başarılı oldu.

#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) ile derlemek için yükleyin libbdd-devve yapın g++ -std=c++11 -O3 -o hb hb.cpp -lbdd. İlk argümanı daha bdd_initda arttırmak faydalı olabilir .


Bu ilginç görünüyor. Bununla ne elde edersiniz?

@Lembik 100'lü yaşlarda 31'i çok daha hızlı yanıt vermeme izin vermeyen çok eski bir donanıma sahibim
Christian Sievers

Bunu Windows'ta nasıl derleyebileceğiniz (örneğin cygwin kullanarak) minnetle alınan herhangi bir yardım.

@Lembik Windws hakkında bilmiyorum ama github.com/fd00/yacp/tree/master/buddy yararlı görünüyor wrt cygwin
Christian Sievers

1
Vay be, tamam, beni bu kütüphaneyi araç setime eklemem gerektiğine ikna ettin. Aferin!
Anders Kaseorg

4

Clingo, n ≈ 30 veya 31 34

Beş satır Clingo kodunun kaba kuvvet Rust çözümümü geçtiğini ve Christian'ın BuDDy çözümüne gerçekten yaklaştığını görünce biraz şaşırdım - bunu daha yüksek bir zaman sınırı ile yenecek gibi görünüyor.

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

arsa


Bu harika! BuDDy çözümünün aniden kötüleştiği grafiğinizden görünüyor. Neden olduğu hakkında bir fikrin var mı?

@Lembik BuDDy'den emin olmak için yeterince araştırma yapmadım, ama belki de bu noktada önbellek kalmadı?
Anders Kaseorg

Vaov! Ben daha yüksek bir ilk değer bdd_inityardımcı olabilir ya da bdd_setmaxincrease50000 varsayılan çok üzerinde bir değerle çağırarak düğüm tablo daha fazla artırmak için izin düşünüyorum . - Benim programın değiştirilmiş sürümünü kullanıyor musunuz?
Christian Sievers

2
Grafiğini seviyorum.

1
Seçeneği kullanarak şok edici bir performans artışı elde edersiniz --configuration=crafty( jumpyve trendybenzer sonuçlar verirsiniz).
Christian Sievers

2

Pari / GP , 23

Varsayılan olarak, Pari / GP yığın boyutunu 8 MB ile sınırlar. Kodun ilk satırı, default(parisize, "4g")bu sınırı 4 GB olarak ayarlar. Hala yığın akışı veriyorsa, 8 GB olarak ayarlayabilirsiniz.

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'ye ulaşır ve ardından bir yığın akışı verir.

Şimdi 24 olacak.

2

Clojure, 75 38 saniyede 29, 80'de 30 ve 165'te 31

Intel i7 6700K'dan Runtimes , bellek kullanımı 200 MB'den az.

project.clj ( çoklu kullanım için com.climate / claypoole kullanır ):

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

Kaynak kodu:

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

Bir kaba kuvvet çözümü, her iş parçacığı aralığın bir alt kümesinin (2 ^ 12 öğe) üzerinden gider ve algılanan kalıpları gösteren bir tamsayı değeri kümesi oluşturur. Bunlar daha sonra bir araya getirilir ve böylece farklı sayım hesaplanır. Umarım çok fazla iş parçacığı makro kullanır rağmen kod takip etmek çok zor değil . Benim mainJVM ısındı almak için testi birkaç kez çalışır.

Güncelleme: Tamsayıların sadece yarısından fazlasını yinelemek, simetri nedeniyle aynı sonucu alır. Ayrıca, daha yüksek bitli sayıları atlamak, kopyaları ürettikçe sayının alt yarısında sayılır.

Önceden oluşturulmuş 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]

Farklı donanım sonuçları, beklenen çalışma zamanı 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

Bu tek iş parçacığını kolayca yapabilir ve aşağıdakiler için standardı kullanarak 3. taraf bağımlılığını önleyebilirsiniz:

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

Eh yerleşik pmap da var ama Claypoole fazla özellik ve Ayarlanabilirliğin sahiptir.


Evet, dağıtmayı önemsiz kılıyor. Çözümümü yeniden değerlendirmek için vaktiniz varsa, şimdi 30'a kadar alacağınızdan eminim. Görünürde başka optimizasyon yok.
NikoNyrh

Ne yazık ki 30 için bir hayır. Geçen süre: 217150.87386 msn

Ahaa, denediğiniz için teşekkürler: D Buna bir eğri sığdırmak ve 120 saniyelik ondalık değerin harcandığı enterpolasyon yapmak daha iyi olabilirdi, ancak bu bile güzel bir meydan okumadır.
NikoNyrh

1

Mathematica, n = 19

alt + tuşlarına basın. iptal etmek ve sonuç yazdırılır

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]

Bunu yapamam, bu yüzden üstel zaman almayı nasıl önlediğini açıklayabilir misiniz? 2 ^ 241 çok büyük bir sayı!

Kodun çıktısını gösterebilir misiniz?

1
Demek istediğim f (n) ... sabit
J42161217

1

Mathematica, 21

f [n_]: = Uzunluk @
     DeleteDuplicates @
      devrik @
       Tablo [2> Tr @ IntegerDigits [#, 2] & / @ 
         BitXor [BitShiftRight [#, n - i], Mod [#, 2 ^ i]], {i, 1, 
         n - 1}] & @ Aralık [0, 2 ^ (n - 1)];
[Yazdır [n -> f @ n], {n, Infinity}]

Karşılaştırma için, Jenny_mathy'nin cevabın = 19 bilgisayarımda veriyor .

En yavaş kısım Tr@IntegerDigits[#, 2] &. Mathematica'nın Hamming ağırlığı için yerleşik olmaması bir utanç.


Kodumu test etmek isterseniz , Mathematica'nın ücretsiz deneme sürümünü indirebilirsiniz .


1

AC sürümü, yerleşik popcount kullanarak

İle daha iyi clang -O3çalışır, ancak yalnızca varsa çalışır 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;
}

Çok hızlı bir şekilde 24 olur ve sonra biter. Sınırı artırmanız gerekiyor.

Tanrım, referans kodunu kaldırmayı unuttum! İki rahatsız edici çizgiyi kaldıracağım: /
bartavelle

@Lembik şimdi sabit olmalıdır
Bartavelle

1

Haskell, (gayri resmi n = 20)

Bu sadece saf bir yaklaşım - şimdiye kadar herhangi bir optimizasyon olmadan. Diğer dillere karşı ne kadar iyi olacağını merak ettim.

Nasıl kullanılır ( haskell platformunun yüklü olduğu varsayılarak ):

  • Kodu bir dosyaya approx_corr.hs(veya başka bir ada yapıştırın, aşağıdaki adımları uygun şekilde değiştirin)
  • Dosyaya gidin ve yürütün ghc approx_corr.hs
  • Çalıştırmak approx_corr.exe
  • Maksimum değeri girin n
  • Her bir hesaplamanın sonucu ve o ana kadar olan toplam gerçek zamanlı (ms cinsinden) görüntülenir.

Kod:

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

Kod çalışırken çıktı vermiyor gibi görünüyor. Bu, test edilmesini biraz zorlaştırır.

Tuhaf, hatasız derleniyor mu? Programı derlemeye çalışırsanız ne olur main = putStrLn "Hello World!"?
flawr

Data.BitsModül yararlı olabilir. Ana döngünüz için main = do maxn <- getmax; t0 <- gettime; loop 1nerede loop n|n==maxn = return ()ve gibi bir şey kullanabilirsiniz loop n = do printresult n (f n); t <- gettime; printtime (t-t0); loop (n+1). getmaxörneğin getArgsprogram argümanlarını kullanmak için kullanılabilir.
Christian Sievers

@ChristianSievers Çok teşekkürler !!! Stackoverflow bu soruyu sordum , ben de orada ekleyebilirsiniz eğer harika olurdu düşünüyorum!
flawr

Orada nasıl cevap vereceğimi bilmiyorum. Orada zaten benzer bir döngü var ve ben zaman almak hakkında hiçbir şey söylemedim: zaten burada olduğunu.
Christian Sievers

1

PopCount ve manuel olarak yönetilen paralellik kullanan bir Haskell çözümü

Derleme: ghc -rtsopts -threaded -O2 -fllvm -Wall foo.hs

( -llvmişe yaramazsa bırakın )

Çalıştırmak : ./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..]

Bir tamponlama sorunu var cygwim komut satırından çalıştırırsanız hiç çıktı almıyorum gibi görünüyor.

Çözümümü yeni güncelledim, ancak çok yardımcı olup olmayacağını bilmiyorum.
bartavelle

@Lembik Bunun açık olup olmadığından emin değilsiniz, ancak bu derlenmeli -O3ve daha hızlı olabilir -O3 -fllvm...
bartavelle

(Ve kaynak kod değişikliği olmazsa, yeniden derlemeden önce tüm derleme dosyaları kaldırılmalıdır)
bartavelle

@Lembik: Paralellik getirdim. Biraz daha hızlı olmalı.
bartavelle

0

Python 2 + pypy, n = 22

İşte bir tür temel ölçüt olarak gerçekten basit bir Python çözümü.

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)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.