Calcul OEIS A005434

10

La tâche consiste à calculer OEIS A005434 le plus rapidement possible.

Considérons une chaîne binaire Sde longueur n. L'indexation de 1, nous pouvons déterminer si S[1..i+1]correspond S[n-i..n]exactement à tous idans l'ordre de 0à n-1. Par exemple,

S = 01010

donne

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

En effet, 0correspond 0, 01ne correspond pas 10, 010correspond 010, 0101ne correspond pas 1010 et correspond finalement à 01010lui-même.

Définissez f(n)comme le nombre de tableaux distincts de Ys et Ns que l'on obtient lors de l'itération sur toutes les 2^ndifférentes chaînes Sde bits possibles de longueur n.

L'observateur remarquera que cette question est une variante plus simple d' une autre question récente . Cependant, je m'attends à ce que des astuces intelligentes puissent rendre cela beaucoup plus rapide et plus facile.

Tâche

Pour augmenter à npartir de 1, votre code devrait sortir n, f(n).

Exemples de réponses

Pour n = 1..24, les bonnes réponses sont:

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

Notation

Votre code doit répéter n = 1la réponse de chacun nà son tour. Je chronométrerai la course entière, en la tuant après deux minutes.

Votre score est le plus élevé que nvous ayez atteint pendant cette période.

En cas d'égalité, la première réponse l'emporte.

Où mon code sera-t-il testé?

Je vais exécuter votre code sous Virtualbox dans une machine virtuelle invitée Lubuntu (sur mon hôte Windows 7).

Mon ordinateur portable a 8 Go de RAM et un processeur Intel i7 5600U@2,6 GHz (Broadwell) avec 2 cœurs et 4 threads. Le jeu d'instructions comprend SSE4.2, AVX, AVX2, FMA3 et TSX.

Entrées principales par langue

  • n = 599 à Rust bu Anders Kaseorg.
  • n = 30 en C par Grimy. La version parallèle passe à 32 lorsqu'elle est exécutée en natif dans cygwin.

la source
math.uni-bielefeld.de/~sillke/SEQUENCES/autocorrelation-range.c (lié depuis la page OEIS) exécuté avec -O3 peut calculer jusqu'à 100 en <0,02 seconde sur ma machine
vroomfondel
@rogaos Oh mon cher. Je devrais supprimer la question mais elle a déjà une réponse.
Je pense que c'est toujours un problème cool - mais peut-être jusqu'à 1000 à la place? Ou demandez des réponses au golf un programme suffisamment rapide
vroomfondel
1
@rogaos Je viens de supprimer complètement la limite stricte.

Réponses:

4

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

Essayez-le en ligne!

Comment ça fonctionne

Il s'agit d'une implémentation mémorisée du prédicat récursif Ξ donné dans Leo Guibas, «Periods in strings» (1981). La fonction f(memo, n, p, s)trouve le nombre de corrélations de longueur navec la plus petite période pet également chacune des périodes de l'ensemble s.

Anders Kaseorg
la source
Vous fait vous demander s'il existe une solution plus rapide à l'autre problème connexe. Très impressionnant!
Fait intéressant, cela est entièrement limité en mémoire. Il accélère jusqu'à ~ 500, puis ralentit soudainement car il manque de RAM.
2

Juste une simple recherche par force brute, pour lancer le défi:

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

Compilez avec clang -fopenmp -Weverything -O3 -march=native. Sur ma machine, il atteint n = 34 en 2 minutes.

EDIT: saupoudré quelques directives OMP pour un parallélisme facile.

Grimmy
la source
@Lembik Existe-t-il une bonne réponse en dehors des motifs de suppression du SE? Ne devriez-vous pas attendre que quelqu'un (peut-être le commentateur) soumette cet algorithme comme réponse et accepte cette réponse?
Grimmy
Vous faites un très bon point
Malheureusement, je ne peux pas vraiment tester votre code parallèle dans virtualbox car j'ai un total de deux cœurs sur mon processeur.
Je l'ai exécuté dans cygwin et il est arrivé à 32.