คำนวณ OEIS A005434


10

ภารกิจคือการคำนวณOEIS A005434โดยเร็วที่สุด

พิจารณาสตริงไบนารีของความยาวS nจัดทำดัชนีจาก1เราสามารถตรวจสอบว่าS[1..i+1]ตรงกับS[n-i..n]ตรงทั้งหมดiในการสั่งซื้อจากไป0 n-1ตัวอย่างเช่น,

S = 01010

จะช่วยให้

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

เพราะนี่คือ0การแข่งขัน0, 01ไม่ตรง10, 010การแข่งขัน010, 0101ไม่ตรงกับ1010 และในที่สุดก็01010ตรงกับตัวเอง

กำหนดf(n)ให้เป็นหมายเลขของอาร์เรย์ที่แตกต่างกันของYและNs หนึ่งได้รับเมื่อ iterating เหนือทุก2^nบิตสตริงที่แตกต่างกันไปได้ของความยาวSn

สังเกตจะสังเกตเห็นคำถามนี้เป็นตัวแปรที่เรียบง่ายของคำถามที่ผ่านมาของฉันอีก อย่างไรก็ตามฉันคาดหวังว่าเทคนิคที่ฉลาดสามารถทำให้สิ่งนี้เร็วและง่ายขึ้น

งาน

สำหรับการเพิ่มการnเริ่มต้นที่รหัสของคุณควรเอาท์พุท1n, 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คุณจะได้รับในเวลานั้น

ในกรณีที่เสมอกันคำตอบแรกจะชนะ

รหัสของฉันจะถูกทดสอบที่ไหน

ฉันจะเรียกใช้รหัสของคุณภายใต้ Virtualbox ในVM Lub guest guest VM (บนโฮสต์ Windows 7 ของฉัน)

แล็ปท็อปของฉันมี RAM 8GB และ Intel i7 5600U@2.6 GHz (Broadwell) CPU ที่มี 2 คอร์และ 4 เธรด ชุดการเรียนการสอนประกอบด้วย SSE4.2, AVX, AVX2, FMA3 และ TSX

รายการนำหน้าต่อภาษา

  • n = 599ในRust bu Anders Kaseorg
  • n = 30ในCโดย Grimy รุ่นขนานได้รับถึง32เมื่อทำงานใน cygwin

math.uni-bielefeld.de/~sillke/SEQUENCES/autocorrelation-range.c (เชื่อมโยงจากหน้า OEIS) รันด้วย -O3 สามารถคำนวณได้สูงสุด 100 ใน <.02 วินาทีในเครื่องของฉัน
vroomfondel

@rogaos โอ้ที่รัก ฉันควรลบคำถาม แต่มีคำตอบแล้ว

ฉันคิดว่ามันยังคงเป็นปัญหาที่ยอดเยี่ยม - แต่อาจมากถึง 1,000 แทน? หรือถามคำตอบสำหรับการเล่นกอล์ฟโปรแกรมที่รวดเร็วเพียงพอ
vroomfondel

1
@rogaos ฉันเพิ่งลบขีด จำกัด อย่างสมบูรณ์

คำตอบ:


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, “ Periods in strings” (1981) ฟังก์ชั่นf(memo, n, p, s)พบว่าจำนวนของความสัมพันธ์ของความยาวnที่มีระยะเวลาที่มีขนาดเล็กที่สุดและยังแต่ละงวดในชุดps


ทำให้คุณสงสัยว่ามีวิธีแก้ไขปัญหาอื่น ๆ ที่เกี่ยวข้องได้เร็วขึ้นหรือไม่ ที่น่าประทับใจมาก!

ที่น่าสนใจคือหน่วยความจำมี จำกัด มันมีความเร็วสูงถึง ~ 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คอมไพล์ด้วย บนเครื่องของฉันมันถึง n = 34 ใน 2 นาที

แก้ไข: โรยคำสั่ง OMP บางอย่างเพื่อให้ขนานได้ง่าย


@Lembik การมีคำตอบที่ดีนอกเขต SE สำหรับการลบหรือไม่? คุณไม่ควรรอใครสักคน (อาจเป็นผู้วิจารณ์) เพื่อส่งอัลกอริทึมนี้เป็นคำตอบและยอมรับคำตอบนั้น?
Grimmy

คุณสร้างจุดดีมาก

น่าเศร้าที่ฉันไม่สามารถทดสอบโค้ดคู่ขนานของคุณใน virtualbox ได้เนื่องจากฉันมีสองคอร์ทั้งหมดใน CPU ของฉัน

ฉันวิ่งใน cygwin และไปถึง 32
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.