计算OEIS A005434


10

任务是尽快计算OEIS A005434

考虑一个S长度为string 的二进制字符串n。从索引1,我们可以确定是否完全S[1..i+1]匹配从到的顺序。例如,S[n-i..n]i0n-1

S = 01010

[Y, N, Y, N, Y].

这是因为0matchs 001不匹配10010matchs 0100101不匹配1010 并最终01010匹配自身。

定义f(n)Ys和Ns在2^n不同S长度的所有可能位字符串上迭代时得到的不同数组的数目n

观察者会注意到这个问题是我最近另一个问题的一个简单的变体。但是,我希望巧妙的技巧可以使这一过程变得更快,更容易。

任务

为了增加n开头1,应该输出代码n, f(n)

示例答案

对于n = 1..24,正确的答案是:

1, 2, 3, 4, 6, 8, 10, 13, 17, 21, 27, 30, 37, 47, 57, 62, 75, 87, 102, 116, 135, 155, 180, 194

计分

您的代码应从依次n = 1给出每个答案的角度进行迭代n。我将为整个运行计时,两分钟后将其终止。

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

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

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

我将在Lubuntu来宾VM(在Windows 7主机上)的Virtualbox下运行您的代码。

我的笔记本电脑有8GB的RAM和一个带有2核和4线程的Intel i7 5600U@2.6 GHz(Broadwell)CPU。指令集包括SSE4.2,AVX,AVX2,FMA3和TSX。

每种语言的主要条目

  • n = 599 in Rust bu Anders Kaseorg。
  • N = 30Ç由肮脏的。在cygwin中本地运行时,并行版本达到32

使用-O3运行的math.uni-bielefeld.de/~sillke/SEQUENCES/autocorrelation-range.c(从OEIS页面链接)可以在<.02秒
内在

@rogaos哦,亲爱的。我应该删除问题,但已经有了答案。

我认为这仍然是一个很酷的问题-但是可能最多有1000个?或问一个足够快的程序来打高尔夫
vroomfondel

1
@rogaos我刚刚完全删除了硬限制。

Answers:


4

,n≈660

use std::collections::HashMap;
use std::iter::once;
use std::rc::Rc;

type Memo = HashMap<(u32, u32, Rc<Vec<u32>>), u64>;

fn f(memo: &mut Memo, mut n: u32, p: u32, mut s: Rc<Vec<u32>>) -> u64 {
    debug_assert!(p != 0);
    let d = n / p;
    debug_assert!(d >= 1);
    let r = n - p * if d >= 2 { d - 1 } else { 1 };

    let k = s.binary_search(&(n - r + 1)).unwrap_or_else(|i| i);
    for &i in &s[..k] {
        if i % p != 0 {
            return 0;
        }
    }

    if d >= 3 {
        let o = n - (p + r);
        n = p + r;
        s = Rc::new(s[k..].iter().map(|i| i - o).collect());
    } else if n == p {
        return 1;
    } else if k != 0 {
        s = Rc::new(s[k..].to_vec());
    }

    let query = (n, p, s);
    if let Some(&c) = memo.get(&query) {
        return c;
    }
    let (n, p, s) = query;

    let t = Rc::new(s.iter().map(|i| i - p).collect::<Vec<_>>());
    let c = if d < 2 {
        (1..r + 1).map(|q| f(memo, r, q, t.clone())).sum()
    } else if r == p {
        (1..p + 1)
            .filter(|&q| p % q != 0 || q == p)
            .map(|q| f(memo, r, q, t.clone()))
            .sum()
    } else {
        let t = match t.binary_search(&p) {
            Ok(_) => t,
            Err(k) => {
                Rc::new(t[..k]
                            .iter()
                            .cloned()
                            .chain(once(p))
                            .chain(t[k..].iter().cloned())
                            .collect::<Vec<_>>())
            }
        };
        (1..t.first().unwrap() + 1)
            .filter(|&q| p % q != 0 || q == p)
            .map(|q| f(memo, r, q, t.clone()))
            .sum()
    };
    memo.insert((n, p, s), c);
    c
}

fn main() {
    let mut memo = HashMap::new();
    let s = Rc::new(Vec::new());
    for n in 1.. {
        println!("{} {}",
                 n,
                 (1..n + 1)
                     .map(|p| f(&mut memo, n, p, s.clone()))
                     .sum::<u64>());
    }
}

在线尝试!

怎么运行的

这是Leo Guibas在“字符串中的句点(1981)中给出的递归谓词memo的一个记忆性实现。该函数f(memo, n, p, s)查找n具有最小周期的长度的相关数,p以及集合中的每个周期s


让您想知道是否有一个更快的解决其他相关问题的方法。非常令人印象深刻!

有趣的是,这完全受内存限制。它加速到〜500,然后在RAM用完时突然变慢。

2

只需简单的蛮力搜索,即可开始挑战:

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

typedef uint16_t u16;
typedef uint64_t u64;

static u64 map[1<<16];

int main(void)
{
    for (u64 n = 1;; ++n) {
        u64 result = 1;
        u64 mask = (1ul << n) - 1;
        memset(map, 0, sizeof(map));

        #pragma omp parallel
        #pragma omp for
        for (u64 x = 1ul << (n - 1); x < 1ul << n; ++x) {

            u64 r = 0;
            for (u64 i = 1; i < n; ++i)
                r |= (u64) (x >> i == (x & (mask >> i))) << i;
            if (!r)
                continue;

            u16 h = (u16) (r ^ r >> 13 ^ r >> 27);
            while (map[h] && map[h] != r)
                ++h;

            if (!map[h]) {
                #pragma omp critical
                if (!map[h]) {
                    map[h] = r;
                    ++result;
                }
            }
        }

        printf("%ld\n", result);
    }
}

用编译clang -fopenmp -Weverything -O3 -march=native。在我的机器上,它在2分钟内达到n = 34。

编辑:洒一些OMP指令,以简化并行性。


@Lembik是否存在删除SE理由之外的好答案?您不应该等待某个人(可能是评论者)提交此算法作为答案并接受该答案吗?
Grimmy

您提出了一个非常好的观点

可悲的是,由于我的CPU上总共有两个内核,因此我无法在virtualbox中真正测试您的并行代码。

我跑它Cygwin和它得到32
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.