Sequência de fumaça de fractal

33

Introdução

A229037 possui uma plotagem bastante intrigante (pelo menos nos primeiros termos):

Existe a conjectura de que possa realmente ter algum tipo de propriedade fractal.

Como essa sequência é construída?

Definir a(1) = 1, a(2) = 1, em seguida, para cada n>2encontrar um positivo mínimo número inteiro a(n)de tal modo que para cada sequência aritmética 3 prazo n,n+k,n+2kde índices, os valores correspondentes da sequência a(n),a(n+k),a(n+2k)é não uma sequência de aritmética.

Desafio

Dado um número inteiro positivo ncomo entrada, imprima os primeiros ntermos a(1), ... , a(n)desta sequência. (Com qualquer formatação razoável. Possíveis caracteres / sequências iniciais / de treinamento são irrelevantes.)

Existem trechos para gerar esta sequência disponível, mas acho que outras abordagens podem ser mais fáceis de jogar / mais adequadas para determinados idiomas.

Informe-nos como o seu programa funciona. Se você cruzar um algoritmo particularmente eficiente, convém mencionar isso também, pois isso permitirá plotar mais termos da sequência em menor tempo.

Primeiros casos de teste:

1, 1, 2, 1, 1, 2, 2, 4, 4, 1, 1, 2, 1, 1, 2, 2, 4, 4, 2, 4, 4, 5, 5, 8, 5, 5, 9, 1, 1, 2, 1, 1, 2, 2, 4, 4, 1, 1, 2, 1, 1, 2, 2, 4, 4, 2, 4, 4, 5, 5, 8, 5, 5, 9, 9, 4, 4, 5, 5, 10, 5, 5, 10, 2, 10, 13, 11, 10, 8, 11, 13, 10, 12, 10, 10, 12, 10, 11, 14, 20, 13

Mais casos de teste:

  a(100)  =   4
  a(500)  =   5
 a(1000)  =  55
 a(5000)  =  15
a(10000)  = 585

Todos os termos até n=100000estão disponíveis aqui: https://oeis.org/A229037/b229037.txt

Obrigado @ MartinBüttner pela ajuda e incentivo.

flawr
fonte
2
Ei, onde eu já vi esse gráfico antes? :-D
Luis Mendo
12
Mude a cabeça um pouco para a esquerda, amplie um pouco, e pronto! (:
flawr 12/01
4
Um vídeo numberphile acabou de aparecer: youtube.com/watch?v=o8c4uYnnNnc
flawr
2
Aposto que o código dele não é tão golfe!
Luis Mendo

Respostas:

12

Python 2, 95 bytes

l=[];n=input()
exec"a=min(set(range(n))-{2*b-c for b,c in zip(l,l[1::2])});print-~a;l=[a]+l;"*n

O principal truque é gerar os números que o novo valor deve evitar. Mantendo a sequência invertida até agora l, vejamos quais elementos podem formar uma progressão aritmética de três termos com o valor que estamos prestes a adicionar.

? 4 2 2 1 1 2 1 1   a b c
^ ^ ^               ? 4 2
^   ^   ^           ? 2 1
^     ^     ^       ? 2 2
^       ^       ^   ? 1 1

Os outros números são os membros emparelhados le cada segundo elemento de l, então zip(l,l[1::2]). Se esse par for, (b,c)então a progressão aritmética (a,b,c)acontece para a=2*b-c. Depois de gerar o conjunto de a's para evitar, o código pega o mínimo do complemento, imprime e anexa à lista. (Na verdade, o cálculo é feito com números diminuídos em 1 e impressos em 1 mais alto, para range(n)servir como um universo de candidatos.)

xnor
fonte
8

Mathematica, 95 bytes

For[n_~s~k_=0;n=0,n<#,For[i=n,--i>0,s[2n-i,2f@n-f@i]=1];For[++n;i=1,n~s~i>0,++i];Print[f@n=i]]&

Certamente não é a abordagem mais eficiente, mas isso é realmente bastante eficiente, comparado com os algoritmos que eu tentei na página OEIS.

Em vez de verificar todos os valores proibidos para cada s (n) quando chegamos lá, estou usando uma abordagem baseada em peneiras. Quando encontramos um novo valor s (n) , verificamos imediatamente quais valores isso proíbe m> n . Depois resolvemos os s (n + 1) procurando o primeiro valor que não era proibido.

Isso pode ser ainda mais eficiente alterando a condicional --i>0para 2n-#<=--i>0. Nesse caso, evitamos verificar valores proibidos para n maior que a entrada.

Para uma versão um pouco mais legível, iniciei com esse código, que armazena os resultados maxem uma função fe, em seguida, jogamos na função pura de uma linha acima:

 max = 1000;
 ClearAll[sieve, f];
 sieve[n_, k_] = False;
 For[n = 0, n < max,
  temp = f[n];
  For[i = n - 1, i > 0 && 2 n - i <= max, --i,
   sieve[2 n - i, 2 temp - f[i]] = True;
   ];
  ++n;
  i = 1;
  While[sieve[n, i], ++i];
  f@n = i;
  ]
Martin Ender
fonte
3

Haskell, 90 , 89 , 84 , 83 bytes

Provavelmente pode ser jogado mais, mas isso ainda é um começo tentativa :

a n|n<1=0|n<3=1|1<2=[x|x<-[1..],and[x/=2*a(n-k)-a(n-k-k)||a(n-k-k)<1|k<-[1..n]]]!!0

Ungolfed:

a n | n<1        = 0 
    | n<3        = 1
    | otherwise  = head (goods n)

goods n = [x | x <- [1..], isGood x n]

isGood x n = and [ x - a(n-k) /= a(n-k) - a(n-k-k) || a(n-k-k) == 0 | k <- [1..n] ]

Esta é uma implementação simples que retorna '0' fora dos limites. Então, para cada valor possível, verifica que, para todos os k <= n e dentro dos limites, [x, a (xk), a (x-2k)] não é uma sequência aritmética.

Limite superior da complexidade do tempo (usando o fato da página OEIS de que a (n) <= (n + 1) / 2:

t n <= sum[ sum[2*t(n-k) + 2*t(n-k-k) | k <- [1..n]] | x <- [1..(n+1)/2]]
    <= sum[ sum[4*t(n-1)              | k <- [1..n]] | x <- [1..(n+1)/2]]
    <= sum[     4*t(n-1)*n                         ] | x <- [1..(n+1)/2]]
    <=          4*t(n-1)*n*(n+1)/2
    ->
O(t(n)) == O(2^(n-2) * n! * (n+1)!)

Não tenho certeza de quão bom é esse limite, pois o cálculo dos primeiros valores de 1k de 't' e o uso de um modelo linear nos logs dos valores deram appx. O (22 ^ n), com valor de p <10 ^ (- 1291), caso isso importe.

Em um nível de implementação, compilando com '-O2', levou ~ 35 minutos para calcular os 20 primeiros valores.

Michael Klein
fonte
11
Qual é a complexidade de tempo do seu programa?
flawr
@flawr Adicionada alguma análise de complexidade de tempo ao post
Michael Klein
3

Braquilog , 33 31 bytes

;Ė{~b.hℕ₁≜∧.¬{ġh₃hᵐs₂ᶠ-ᵐ=}∧}ⁱ⁽↔

Experimente online!

Caso seja importante: o golfe de 2 bytes foi possível graças a um recurso que eu solicitei depois de trabalhar nesse desafio.

Explicação

Geramos iterativamente a sequência como uma lista em ordem inversa, por exemplo [2,2,1,1,2,1,1] , e a revertemos no final.

Existem alguns predicados aninhados aqui. Vamos vê-los de dentro para fora. O primeiro ġh₃hᵐs₂ᶠ-ᵐ=, pega uma subsequência candidata a(n),a(n-1),...,a(0)e determina se a(n),a(n-k),a(n-2k)é uma sequência aritmética para alguns k.

ġ            Group the list into equal-length sublists (with the possible exception of
             the last sublist, which might be shorter)
 h₃          Get the first 3 sublists from that list
   hᵐ        and get the head of each of those 3 sublists
             We now have a list containing a(n),a(n-k),a(n-2k) for some k
     s₂ᶠ     Find all 2-element sublists of that list: [a(n),a(n-k)] and [a(n-k),a(n-2k)]
        -ᵐ   Find the difference of each pair
          =  Assert that the two pairwise differences are equal

Por exemplo, com entrada de [1,2,1,1,2,1,1]:

ġ has possible outputs of
    [[1],[2],[1],[1],[2],[1],[1]]
    [[1,2],[1,1],[2,1],[1]]
    [[1,2,1],[1,2,1],[1]]
    [[1,2,1,1],[2,1,1]]
    [[1,2,1,1,2],[1,1]]
    [[1,2,1,1,2,1],[1]]
    [[1,2,1,1,2,1,1]]
h₃ has possible outputs of
    [[1],[2],[1]]
    [[1,2],[1,1],[2,1]]
    [[1,2,1],[1,2,1],[1]]
hᵐ has possible outputs of
    [1,2,1]
    [1,1,2]
    [1,1,1]
s₂ᶠ has possible outputs of
    [[1,2],[2,1]]
    [[1,1],[1,2]]
    [[1,1],[1,1]]
-ᵐ has possible outputs of
    [-1,1]
    [0,-1]
    [0,0]
= is satisfied by the last of these, so the predicate succeeds.

O próximo predicado para fora,, ~b.hℕ₁≜∧.¬{...}∧insere uma subsequência a(n-1),a(n-2),...,a(0)e gera a próxima maior subsequência a(n),a(n-1),a(n-2),...,a(0).

~b.hℕ₁≜∧.¬{...}∧
~b.                 The input is the result of beheading the output; i.e., the output is
                    the input with some value prepended
  .h                The head of the output
    ℕ₁              is a natural number >= 1
      ≜             Force a choice as to which number (I'm not sure why this is necessary,
                    but the code doesn't work without it)
        ∧           Also,
         .          the output
          ¬{...}    does not satisfy the nested predicate (see above)
                    I.e. there is no k such that a(n),a(n-k),a(n-2k) is an arithmetic sequence
                ∧   Break unification with the output

Finalmente, o predicado principal ;Ė{...}ⁱ⁽↔pega um número de entrada e gera muitos termos da sequência.

;Ė{...}ⁱ⁽↔
;           Pair the input number with
 Ė          the empty list
  {...}ⁱ⁽   Using the first element of the pair as the iteration count and the second
            element as the initial value, iterate the nested predicate (see above)
         ↔  Reverse, putting the sequence in the proper order
DLosc
fonte
3

Ruby , 71 bytes

->n,*a{a.fill(0,n){|s|([*1..n]-(1..s/2).map{|o|2*a[s-o]-a[s-2*o]})[0]}}

Experimente online!

Gera todos os valores proibidos, depois pega o complemento dessa matriz em (1..n) e pega o primeiro valor do resultado. Isso significa que estou assumindo que, a[n] <= npara todos os n, que é facilmente comprovado usando indução (se os primeiros termos n / 2 são todos inferiores a n / 2, não pode haver uma progressão aritmética levando a n).

O truque sintático aqui também é levemente interessante: *aé usado para inicializar uma matriz de argumentos adicionais (que seriam ignorados se passássemos por alguma) e, em seguida, a.fillmodifica a matriz de argumentos e a devolve implicitamente.

histocrata
fonte
11
-1 byte: em vez de a[s-o]e a[s-2*o], você pode usar a[s-=1]ea[s-o]
GB
3

APL (Dyalog Extended) , SBCS de 37 bytes

Muito obrigado a Adám por sua ajuda ao escrever e jogar esta resposta no The APL Orchard , um ótimo lugar para aprender a língua APL. Experimente online!

Edit: -6 bytes graças a Adám

⌽{⍵,⍨⊃(⍳1+≢⍵)~-¯2⊥⍵[2×⍀⍥⍳⌊2÷⍨≢⍵]}⍣⎕,⍬

Explicação

{⍵,⍨⊃(⍳1+≢⍵)~-¯2⊥⍵[2×⍀⍥⍳⌊2÷⍨≢⍵]}   is our right argument, the sequence S

                        2÷⍨≢⍵    We start by calculating X = len(S2
                                 Get a range [1, X]
                   2×⍀⍥           With that we can get S[:X] and S[:X×2:2]
                                  or S up to halfway and every 2nd element of S
             2⊥⍵[           ]   And with that we can get 2*S[:X] - S[:X×2:2]
                                  Which is C=2*B-A of a progression A B C
     (⍳1+≢⍵)~                     We remove these Cs from our possible a(n)s
                                  I use range [1, len(S)+1]
                                 Get the first result, which is the minimum
 ⍵,⍨                              And then prepend that to S


⌽{...}⍣⎕,⍬

 {...}⍣⎕    We iterate an "input"  times
        ,⍬  with an empty list  as the initial S
           and reversing S at the end as we have built it backwards

APL (Dyalog Unicode) , 43 39 38 bytes SBCS

Aqui está uma solução mais rápida, mas menos eficiente, que pode ser calculada ⍺(10000)em alguns segundos.

⌽{⍵,⍨⊃(⍳1+≢⍵)~-⌿⍵[1 2 1∘.×⍳⌊2÷⍨≢⍵]}⍣⎕,⍬

Experimente online!

Sherlock9
fonte
2

MATLAB, 156 147 bytes

Eu finalmente comecei a jogar isso um pouco:

N=input('');s=[0;0];for n=1:N,x=s(n,~~s(n,:));try,a(n)=find(~ismember(1:max(x)+1,x),1);catch,a(n)=1;end,s(n+1:2*n-1,end+1)=2*a(n)-a(n-1:-1:1);end,a

Ungolfed:

N=input('');                                   % read N from stdin

s=[0;0];
for n=1:N,
    x=s(n,~~s(n,:));                           % x=nonzeros(s(n,:));
    try,
        a(n)=find(~ismember(1:max(x)+1,x),1);  % smallest OK number
    catch,
        a(n)=1;                                % case of blank page for n
    end,

    s(n+1:2*n-1,end+1)=2*a(n)-a(n-1:-1:1);     % determined new forbidden values
end,
a                                              % print ans=...

A entrada é lida no STDIN e a impressão é feita automaticamente com o ans=material anexado. Espero que isso se qualifique como saída "razoável".

Esta é também uma solução à base de peneiro: a variável s(i,:)mantém o controle dos valores de sequência que são proibidos para a(i). O try-catchbloco é necessário para tratar o caso de uma smatriz vazia (ou seja, zero completo) : nesse caso, o menor valor de 1já é permitido.

No entanto, a necessidade de memória (ou tempo de execução?) Fica bastante bagunçada acima N=2000. Então, aqui está uma solução mais eficiente e não competitiva:

%pre-alloc
s = zeros([N,fix(N*0.07+20)]); %strict upper bound, needs adjusting later
i = zeros(1,N);

a = 1;
for n = 2:N,
    x = s(n,1:i(n));
    if isempty(x),
        a(n) = 1;
    else
        a(n) = find(~ismember(1:max(x)+1,x),1);
    end,

    j = n+1:min(2*n-1,N);
    i(j) = i(j)+1;
    s(N,max(i(j))) = 0;   %adjust matrix size if necessary
    b = a(n-1:-1:1);
    s(sub2ind([N,size(s,2)+1],j,i(j))) = 2*a(n)-b(1:length(j));
end

Nesta implementação, a smatriz novamente contém valores proibidos, mas de uma maneira bem ordenada, sem nenhum bloco zero (que está presente na versão concorrente). O vetor de índice iacompanha o número de vetores proibidos em s. À primeira vista, as células seriam ótimas para rastrear as informações armazenadas s, mas elas seriam lentas e não podíamos indexar várias delas ao mesmo tempo.

Um recurso desagradável do MATLAB é que, embora você possa dizer M(1,end+1)=3;e expandir automaticamente uma matriz, você não pode fazer o mesmo com a indexação linear. Isso meio que faz sentido (como o MATLAB deve saber o tamanho da matriz resultante, na estrutura da qual deve interpretar os índices lineares?), Mas ainda me surpreendeu. Esta é a razão da linha supérflua s(N,max(i(j))) = 0;: isso expandirá a smatriz para nós sempre que necessário. A estimativa do tamanho inicial N*0.07+20vem de um ajuste linear para os primeiros elementos, a propósito.

Para testar o tempo de execução, também verifiquei uma versão ligeiramente modificada do código, onde inicializei a smatriz para ter tamanho N/2. Para os primeiros 1e5elementos, isso parece ser um palpite muito generoso, por isso removi a etapa de expansão smencionada no parágrafo anterior. Juntos, isso implica que o código será mais rápido, mas também menos robusto em altíssimos níveis.N (já que não sei como é a série).

Então, aqui estão alguns tempos de execução, comparando

  • v1: a versão competitiva do golfe,
  • v2: a versão de baixo tamanho inicial e à prova de falhas
  • v3: a versão de tamanho inicial generoso, pode falhar por N grande.

Eu as medi no R2012b, utilizando o máximo de 5 execuções dentro de uma definição de função nomeada tic/toc.

  1. N=100:
    • v1: 0.011342 s
    • v2: 0.015218 s
    • v3: 0.015076 s
  2. N=500:
    • v1: 0.101647 s
    • v2: 0.085277 s
    • v3: 0.081606 s
  3. N=1000:
    • v1: 0.641910 s
    • v2: 0.187911 s
    • v3: 0.183565 s
  4. N=2000:
    • v1: 5.010327 s
    • v2: 0.452892 s
    • v3: 0.430547 s
  5. N=5000:
    • v1: N / A (não esperou)
    • v2: 2.021213 s
    • v3: 1.572958 s
  6. N=10000:
    • v1: N / A
    • v2: 6.248483 s
    • v3: 5.812838 s

Parece que a v3versão é significativamente, mas não surpreendentemente mais rápida. Não sei se um elemento de incerteza (para muito grande N) vale o pequeno aumento no tempo de execução.

Andras Deak
fonte
M=1;M(end+1)=2;funciona perfeitamente bem para mim?
flawr
@flawr que funcionará para escalares e vetores. Tente em M=rand(2); M(end+1)=2vez disso :)
Andras Deak 13/01
Ah, agora eu vejo =)
flawr
2

Jelly , 24 19 bytes

Esta é a minha primeira resposta Jelly em um bom tempo. Feliz por estar de volta.

Esta é uma porta da minha resposta APL que por si só é uma adaptação de muitos dos algoritmos usados ​​aqui. A principal diferença é que esse índice é 0.

Editar: -5 bytes graças a Jonathan Allan.

Experimente online!

Ḋm2ɓṁḤ_
ŻJḟÇṂ;
1Ç¡U

Explicação

Ḋm2ɓṁḤ_  First link. Takes our current sequence S as our left argument.

         We are trying to calculate, of an arithmetic progression A B C, 
           the C using the formula, C = 2*B - A
Ḋ        Remove the first element of S.
 m2      Get every element at indices 0, 2, 4, ...
           This is equivalent to getting every second element of S, a list of As.
   ɓ     Starts a dyad with reversed arguments.
           The arguments here are S and As.
    ṁ    This molds S in the shape of As, giving us a list of Bs.
     Ḥ   We double the Bs.
      _  And subtract As from 2 * Bs.

ŻJḟÇṂ;  Second link. Takes S as our left argument.

Ż       Append a 0 to S.
 J      Range [1, len(z)]. This gets range [1, len(S) + 1].
  ḟÇ    Filter out the results of the previous link, our Cs.
    Ṃ   Take the minimum of Cs.
     ;  And concatenate it with the rest of the sequence so far.

1Ç¡U  Third link. Where we feed our input, n.

1     A list with the element 1.
 Ç¡   Run the previous link n times.
   U  Reverse everything at the end.
Sherlock9
fonte
fará tão bem quanto œ-salvar um byte
Jonathan Allan
Certamente você pode zerar o índice (conforme a sequência ) e, portanto, substituir “”com a 1saída de uma representação Jelly de uma lista de um programa completo, economizando mais um.
Jonathan Allan
Œœị@2pode ser jogado para Ḋm2salvar dois.
Jonathan Allan
L‘Rpode ser jogado para ŻJsalvar um.
Jonathan Allan
@JonathanAllan Cinco bytes inteiros! Obrigado!
Sherlock9
1

ES6, 114 bytes

n=>[...r=Array(n)].map((x,i,s)=>{for(y=1;x&&x[y];y++);r[i]=y;for(j=i;++j<n;s[j][y+y-r[i+i-j]]=1)s[j]=s[j]||[]}&&r

Retorna uma matriz dos primeiros n elementos da sequência, para que os índices sejam 1 fora da versão não destruída abaixo. Eu usei a abordagem da peneira. Esta versão fica mais lenta após cerca de n = 2000; uma versão anterior evitava ler o início da matriz, o que significava que não diminuía a velocidade até cerca de n = 2500. Uma versão mais antiga usava a matriz de peneiras como uma lista de valores proibidos, em vez de uma matriz booleana cujos valores eram proibidos; isso pode chegar a n = 5000 sem suar a camisa. Minha versão original tentou usar máscaras de bits, mas isso não ajudou em nada (e também era muito tempo, com 198 bytes).

A versão não tão lenta como esta:

function smoke(n) {
    result = [];
    sieve = [];
    for (i = 1; i <= n; i++) {
        value = 1;
        if (sieve[i]) {
            while (sieve[i][value]) {
                value++;
            }
        }
        result[i] = value;
        for (j = 1; j < i && i + j <= n; j++) {
            if (!sieve[i + j]) sieve[i + j] = [];
            sieve[i + j][value + value - result[i - j]] = true;
        }
    }
    return result;
}
Neil
fonte