Computar OEIS A005434

10

A tarefa é calcular o OEIS A005434 o mais rápido possível.

Considere uma cadeia Sde comprimento binária n. Indexando de 1, podemos determinar se S[1..i+1]corresponde S[n-i..n]exatamente a todos ina ordem de 0até n-1. Por exemplo,

S = 01010

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

Isso ocorre porque 0combina 0, 01não combina 10, 010combina 010, 0101não combina 1010 e finalmente 01010combina com si próprio.

Define f(n)-se o número de matrizes distintas de Ys e Ns que se tem quando iteração sobre todas as 2^nsequências de bits diferentes possíveis Sde comprimento n.

O observador notará que esta questão é uma variante mais simples de outra questão recente minha . No entanto, espero que truques inteligentes possam tornar isso muito mais rápido e fácil.

Tarefa

Para aumentar a npartir de 1, seu código deve ser exibido n, f(n).

Respostas de exemplo

Pois n = 1..24, as respostas corretas são:

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

Pontuação

Seu código deve repetir n = 1a resposta de cada um n. Eu cronometrarei a corrida inteira, matando-a depois de dois minutos.

Sua pontuação é a mais alta que nvocê obtém nesse período.

Em caso de empate, a primeira resposta vence.

Onde meu código será testado?

Executarei seu código no Virtualbox em uma VM convidada do Lubuntu (no meu host do Windows 7).

Meu laptop possui 8 GB de RAM e uma CPU Intel i7 [email protected] GHz (Broadwell) com 2 núcleos e 4 threads. O conjunto de instruções inclui SSE4.2, AVX, AVX2, FMA3 e TSX.

Entradas principais por idioma

  • n = 599 em Rust bu Anders Kaseorg.
  • n = 30 em C por Grimy. A versão paralela chega a 32 quando é executada nativamente no cygwin.

fonte
math.uni-bielefeld.de/~sillke/SEQUENCES/autocorrelation-range.c (a partir da página OEIS) executados com O3 pode calcular-se a 100 em <.02 segundos em minha máquina
vroomfondel
@rogaos Oh querida. Eu devo excluir a pergunta, mas ela já tem uma resposta.
Eu acho que ainda é um problema legal - mas talvez até 1000 em vez disso? Ou pedir respostas ao golfe um programa suficientemente rápido
vroomfondel
11
@rogaos Acabei de remover completamente o limite rígido.

Respostas:

4

Ferrugem , 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>());
    }
}

Experimente online!

Como funciona

Esta é uma implementação memorizada do predicado recursivo Ξ dado em Leo Guibas, “Períodos em cadeias” (1981). A função f(memo, n, p, s)encontra o número de correlações de comprimento ncom o menor período pe também cada um dos períodos do conjunto s.

Anders Kaseorg
fonte
Faz você se perguntar se existe uma solução mais rápida para outro problema relacionado. Muito impressionante!
Curiosamente, isso é inteiramente limitado pela memória. Acelera até ~ 500 e, de repente, diminui à medida que fica sem memória RAM.
2

Apenas uma pesquisa simples de força bruta, para iniciar o desafio:

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

Compile com clang -fopenmp -Weverything -O3 -march=native. Na minha máquina, atinge n = 34 em 2 minutos.

EDIT: espalhou algumas diretivas do OMP para facilitar o paralelismo.

Grimmy
fonte
@Lembik A existência de uma boa resposta fora dos fundamentos da SE para exclusão? Você não deveria esperar que alguém (possivelmente o comentarista) envie esse algoritmo como resposta e aceite essa resposta?
Grimmy
Você faz um ponto muito bom
Infelizmente, não posso realmente testar seu código paralelo no virtualbox, pois tenho um total de dois núcleos na minha CPU.
Corri-lo em cygwin e chegou a 32.