Calcola OEIS A005434


10

Il compito è calcolare OEIS A005434 il più rapidamente possibile.

Considera una stringa binaria Sdi lunghezza n. Indicizzando da 1, possiamo determinare se S[1..i+1]corrisponde S[n-i..n]esattamente a tutti iin ordine da 0a n-1. Per esempio,

S = 01010

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

Questo perché le 0partite 0, 01non corrispondono 10, le 010partite 010, 0101non corrispondono 1010 e infine si 01010abbinano da sole.

Definire f(n)il numero di matrici distinte di Ys che Nsi ottengono quando si scorre su tutte le 2^ndiverse stringhe Sdi bit possibili di lunghezza n.

L'osservatore noterà che questa domanda è una variante più semplice di un'altra mia recente domanda . Tuttavia, mi aspetto che i trucchi intelligenti possano renderlo molto più veloce e più facile.

Compito

Per aumentare a npartire da 1, il tuo codice dovrebbe essere prodotto n, f(n).

Risposte di esempio

Per n = 1..24, le risposte corrette sono:

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

punteggio

Il tuo codice dovrebbe ripetere dal n = 1dare la risposta per ciascuno na turno. Farò cronometrare l'intera corsa, uccidendola dopo due minuti.

Il tuo punteggio è il più alto che nriesci a raggiungere in quel momento.

In caso di pareggio, vince la prima risposta.

Dove verrà testato il mio codice?

Eseguirò il tuo codice sotto Virtualbox in una VM guest Lubuntu (sul mio host Windows 7).

Il mio laptop ha 8 GB di RAM e una CPU Intel i7 5600U@2,6 GHz (Broadwell) con 2 core e 4 thread. Il set di istruzioni include SSE4.2, AVX, AVX2, FMA3 e TSX.

Voci principali per lingua

  • n = 599 in Rust bu Anders Kaseorg.
  • n = 30 in C di Grimy. La versione parallela arriva a 32 se eseguita nativamente in cygwin.

math.uni-bielefeld.de/~sillke/SEQUENCES/autocorrelation-range.c (collegato dalla pagina OEIS) eseguito con -O3 può calcolare fino a 100 in <.02 secondi sulla mia macchina
vroomfondel

@rogaos Oh caro. Dovrei eliminare la domanda ma ha già una risposta.

Penso che sia ancora un bel problema, ma forse fino a 1000 invece? Oppure chiedi risposte al golf con un programma sufficientemente veloce
vroomfondel,

1
@rogaos Ho appena rimosso completamente il limite massimo.

Risposte:


4

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

Provalo online!

Come funziona

Questa è un'implementazione memorizzata del predicato ricorsivo Ξ dato in Leo Guibas, " Periods in string" (1981). La funzione f(memo, n, p, s)trova il numero di correlazioni di lunghezza ncon il periodo più piccolo pe anche ciascuno dei periodi nell'insieme s.


Ti fa chiedere se esiste una soluzione più rapida per l'altro problema correlato. Molto impressionante!

È interessante notare che questo è interamente limitato dalla memoria. Accelera fino a ~ 500 e poi improvvisamente rallenta mentre si esaurisce la RAM.

2

Solo una semplice ricerca di forza bruta, per iniziare la sfida:

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

Compila con clang -fopenmp -Weverything -O3 -march=native. Sulla mia macchina raggiunge n = 34 in 2 minuti.

EDIT: cosparso alcune direttive OMP per un facile parallelismo.


@Lembik L'esistenza di una buona risposta è al di fuori dei motivi SE per la cancellazione? Non dovresti aspettare che qualcuno (possibilmente il commentatore) invii questo algoritmo come risposta e accetti quella risposta?
Grimmy,

Fai un ottimo punto

Purtroppo non riesco davvero a testare il tuo codice parallelo in virtualbox in quanto ho un totale di due core sulla mia CPU.

L'ho eseguito su Cygwin ed è arrivato a 32.
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.