Tính toán OEIS A005434


10

Nhiệm vụ là tính toán OEIS A005434 càng nhanh càng tốt.

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 tôi có thể xác định xem có S[1..i+1]khớp S[n-i..n]chính xác cho tất cả itheo thứ tự từ 0đến n-1. Ví dụ,

S = 01010

cho

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

Điều này là do 0các trận đấu 0, 01không phù hợp 10, 010các trận đấu 010, 0101không phù hợp 1010 và cuối cùng là 01010phù hợp với bản thân.

Xác định f(n)là số lượng các 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 nhau n.

Người quan sát sẽ nhận thấy câu hỏi này là một biến thể đơn giản hơn của một câu hỏi gần đây của tôi . Tuy nhiên, tôi hy vọng rằng các thủ thuật thông minh có thể làm cho việc này nhanh hơn và dễ dàng hơn nhiều.

Bài tập

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

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

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

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

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 từng nlượ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 nbạ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 trong Virtualbox trong máy ảo khách LubFi (trên máy chủ Windows 7 của tôi).

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 = 599 trong Rust bu Anders Kaseorg.
  • n = 30 in C bởi Grimy. Phiên bản song song đạt 32 khi chạy tự nhiên trong cygwin.

math.uni-bielefeld.de/~sillke/SEQUENCES/autocorrelation-range.c (được liên kết từ trang OEIS) chạy với -O3 có thể tính tới 100 trong <.02 giây trên máy của tôi
vroomfondel

@rogaos ơi. Tôi nên xóa câu hỏi nhưng nó đã có câu trả lời.

Tôi nghĩ đó vẫn là một vấn đề tuyệt vời - nhưng có thể lên tới 1000 thay thế? Hoặc hỏi câu trả lời để chơi golf một chương trình đủ nhanh
vroomfondel

1
@rogaos Mình mới gỡ bỏ giới hạn cứng hoàn toàn.

Câu trả lời:


4

Rỉ sét , 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>());
    }
}

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

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

Đây là một triển khai được ghi nhớ của vị từ đệ quy Ξ được đưa ra trong Leo Guibas, Thời kỳ trong chuỗi Hồi (1981). Hàm f(memo, n, p, s)tìm thấy số lượng tương quan của độ dài nvới khoảng thời gian nhỏ nhất pvà cả từng khoảng thời gian trong tập hợp s.


Làm cho bạn tự hỏi nếu có một giải pháp nhanh hơn cho các vấn đề liên quan khác. Rất ấn tượng!

Điều thú vị là đây hoàn toàn là bộ nhớ hạn chế. Nó tăng tốc lên tới ~ 500 và sau đó đột nhiên chậm lại khi hết RAM.

2

Chỉ cần một tìm kiếm đơn giản, để bắt đầu thử thách:

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

Biên dịch với clang -fopenmp -Weverything -O3 -march=native. Trên máy của tôi, nó đạt n = 34 trong 2 phút.

EDIT: rắc một số chỉ thị OMP để dễ dàng song song.


@Lembik Có tồn tại một câu trả lời tốt bên ngoài căn cứ SE để xóa không? Bạn có nên đợi ai đó (có thể là người bình luận) gửi thuật toán này làm câu trả lời và chấp nhận câu trả lời đó không?
Grimmy

Bạn thực hiện một điểm rất tốt

Đáng buồn là tôi thực sự không thể kiểm tra mã song song của bạn trong hộp ảo vì tôi có tổng cộng hai lõi trên CPU của mình.

Tôi đã chạy nó trong cygwin và nó đã lên tới 32.
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.