Cálculo de primos Collatz


21

Defina a função f (n) para um número inteiro positivo n da seguinte maneira:

  • n / 2 , se n for par
  • 3 * n + 1 , se n for ímpar

Se você aplicar repetidamente esta função a qualquer n maior que 0, o resultado sempre parecerá convergir para 1 (embora ninguém tenha conseguido provar isso ainda). Essa propriedade é conhecida como Conjectura Collatz .

Defina o tempo de parada de um número inteiro como o número de vezes que você deve passar pela função Collatz f antes de atingir 1. Aqui estão os tempos de parada dos 15 primeiros números inteiros:

1  0
2  1
3  7
4  2
5  5
6  8
7  16
8  3
9  19
10 6
11 14
12 9
13 9
14 17
15 17

Vamos ligar para qualquer conjunto de números com o mesmo tempo de parada primos Collatz . Por exemplo, 5 e 32 são primos de Collatz, com um tempo de parada de 5.

Sua tarefa: escreva um programa ou função que use um número inteiro não negativo e gere o conjunto de primos Collatz cujo tempo de parada é igual a esse número inteiro.

Entrada

Um número inteiro não negativo S, fornecido via STDIN, ARGV ou argumento de função.

Saída

Uma lista de todos os números cujo tempo de parada é S, classificados em ordem crescente . A lista pode ser impressa pelo seu programa ou retornada ou impressa pela sua função. O formato de saída é flexível: espaço separado, nova linha ou qualquer formato de lista padrão do seu idioma é adequado, desde que os números sejam facilmente distinguíveis um do outro.

Exigências

Seu envio deve fornecer resultados corretos para qualquer S ≤ 30. Ele deve terminar em segundos ou minutos, não horas ou dias.

Exemplos

0  -> 1
1  -> 2
5  -> 5, 32
9  -> 12, 13, 80, 84, 85, 512
15 -> 22, 23, 136, 138, 140, 141, 150, 151, 768, 832, 848, 852, 853, 904, 906, 908, 909, 5120, 5376, 5440, 5456, 5460, 5461, 32768

Aqui está uma síntese da saída para S = 30 .

Este é o : o programa mais curto em bytes vence. Boa sorte!


E quanto aos ciclos? Não vi menção a evitar o ciclo. Porque para S = 5, existem 3 valores [4, 5, 32] porque você pode
— digitar

11
@JPMC Evitar o ciclo é implícito na definição de tempo de parada. O tempo de parada de 4 é 2, não 5, porque 2 é "o número de vezes que você precisa passar pela função Collatz antes de atingir 1".
— DLosc

Ah, me perdoe. Eu estava pensando que um número poderia ter vários tempos de parada, pois vários caminhos podem levar a ele. Mas isso foi em relação à criação do 1, não ao trabalho do N. Desculpe por isso.
— JPMC

11
@DLosc Pyth, é claro.
— Isaacg

Respostas:


7

Pitão, 26 24 21 bytes

Su+yMG-/R3fq4%T6G1Q]1

Este código é executado instantaneamente para S=30. Experimente você mesmo: Demonstração

Obrigado a @isaacg por salvar 5 bytes.

Explicação

Meu código começa com 1e desfaz a função Collatz. Ele mapeia todos os números dda S-1etapa para 2*de (d-1)/3. O último nem sempre é válido.

                        implicit: Q = input number
                   ]1   start with G = [1]
 u                Q     apply the following function Q-times to G:
                          update G by
   yMG                      each number in G doubled
  +                       +
          fq4%T6G           filter G for numbers T, which satisfy 4==T%6
       /R3                  and divide them by 3
      -          1          and remove 1, if it is in the list
                            (to avoid jumping from 4 to 1)
S                       sort the result and print

Esse é um belo uso de -F.
— Isaacg 28/05

11
Se você colocar um valor - ... 1ao redor da soma dentro da redução, não precisará que a redução seja a .u, nem a parte -Fexterna. Salva 2 caracteres.
— Isaacg 28/05

@isaacg Obrigado. Na verdade, eu tinha isso em uma versão anterior, mas o removi durante a depuração de um erro.
— Jakube 28/05

3
Eu emprestei @ isaacg's para minha própria resposta. Passei horas tentando encontrar o código mais curto para remover duplicatas, mas essa é de longe a solução mais elegante. Além disso, eu realmente gosto do uso de uma tupla para descartar quocientes inválidos. Infelizmente, CJam não tem tuplas, mas eu consegui mapear quocientes inválidos para 1.
— Dennis

@Jakube q4%d6é equivalente a !%hhd6, mas 1 caractere menor.
— Isaacg 29/05

8

Mathematica, 98 92 89 bytes

Esta solução resolve S = 30imediatamente:

(p={0};l={1};Do[l=Complement[##&@@{2#,Mod[a=#-1,2]#~Mod~3~Mod~2a/3}&/@l,p=p⋃l],{#}];l)&

Esta é uma função sem nome, tendo Scomo único parâmetro e retornando uma lista dos primos Collatz.

O algoritmo é uma pesquisa simples e abrangente. Os primos Collatz para um dado Ssão todos os números inteiros que podem ser alcançados a partir dos primos Collatz para S-1via 2*nou números ímpares que podem ser alcançados via (n-1)/3. Também precisamos garantir que produzimos apenas os números inteiros alcançados pela primeira vez após as Setapas, para acompanhar todos os primos anteriores pe removê-los do resultado. Como estamos fazendo isso de qualquer maneira, podemos salvar alguns bytes calculando as etapas de todos os primos anteriores (não apenas os de S-1) para salvar alguns bytes (o que o torna um pouco mais lento, mas não notavelmente para o necessário S).

Aqui está uma versão um pouco mais legível:

(
  p = {0};
  l = {1};
  Do[
    l = Complement[
      ## & @@ {2 #, Mod[a = # - 1, 2] #~Mod~3~Mod~2 a/3} & /@ l,
      p = p ⋃ l
    ]~Cases~_Integer,
    {#}
  ];
  l
) &

5

Python 2, 86 83 75 73 71 bytes

f=lambda n,k=1:sorted([k][n:]or(k>4==k%6and f(n-1,k/3)or[])+f(n-1,k*2))

Ligue como f(30). n = 30é praticamente instantâneo.

(Agradecemos a @DLosc pela idéia de recursão ksendo um número, e não uma lista de primos, e alguns bytes. Agradecemos a @isaacg por desistir ~-.)

Essa variante é muito mais curta, mas infelizmente leva muito tempo devido à ramificação exponencial:

f=lambda n,k=1:sorted([k][n:]or(k>4==k%6)*f(n-1,k/3)+f(n-1,k*2))

Interessante - a minha solução original é muito semelhante, mas (tendo algumas otimizações das suas) sai 2 bytes mais curto: f=lambda d,n=1:d and sorted(sum((c(d-1,x)for x in[n*2]+[~-n/3][:n>4==n%6]),[]))or[n]. É menos eficiente com as chamadas de função, mas ainda o faz n = 30em menos de um segundo.
— DLosc

11
@DLosc Gostei da sua idéia e fez :) uma melhor
— SP3000

Agradável! Aqui estão mais 2 bytes de desconto:f=lambda n,k=1:sorted([k][n:]or(k>4==k%6and f(n-1,~-k/3)or[])+f(n-1,k*2))
— DLosc

@DLosc Ahaha thanks. Eu ainda juro que tem que haver uma maneira melhor de curto-circuito ...
— Sp3000

Eu acho que ~-é desnecessário porque você está usando a divisão inteira.
— Isaacg

5

CJam, 29 26 bytes

Xari{{2*_Cmd8=*2*)}%1-}*$p

O crédito vai para @isaacg por sua idéia de remover 1s após cada iteração, o que me salvou dois bytes diretamente e outro indiretamente.

Experimente online no intérprete CJam (deve terminar em menos de um segundo).

Como funciona

Xa       e# Push A := [1].
ri{      e# Read an integer from STDIN and do the following that many times:
  {      e# For each N in A:
    2*   e#     Push I := (N * 2) twice.
    _Cmd e#     Push (I / 12) and (I % 12).
     8=  e#     Push K := (I % 12 == 8).

         e#     (K == 1) if and only if the division ((N - 1) / 3) is exact and
         e#     yields an odd integer. In this case we can compute the quotient 
         e#     as (I / 12) * 2 + 1.

    *2*) e#     Push J := (I / 12) * K * 2 + 1.

         e#     This yields ((N - 1) / 3) when appropriate and 1 otherwise.
  }%     e# Replace N with I and J.
  1-     e# Remove all 1's from A.

         e# This serves three purposes:

         e# 1. Ones have been added as dummy values for inappropriate quotients.

         e# 2. Not allowing 1's in A avoids integers that have already stopped
         e#    from beginning a new cycle. Since looping around has been prevented,
         e#    A now contains all integers of a fixed stopping time.

         e# 3. If A does not contain duplicates, since the maps N -> I and N -> J
         e#      are inyective (exluding image 1) and yield integers of different
         e#      parities, the updated A won't contain duplicates either.

}*       e#
$p       e# print(sort(C))

4

CJam, 35 bytes

1]ri{_"(Z/Y*"3/m*:s:~\L+:L-_&0-}*$p

Explicação em breve. Esta é uma versão muito mais rápida que a abordagem "bastante direta" (veja no histórico de edições).

Experimente on-line aqui, para N = 30execução em segundos na versão on-line e instantaneamente no Java Compiler


Quanto tempo isso levará para os insumos maiores? It should finish in seconds or minutes, not hours or days.
— DLosc

Ah entendo. O Python versão que eu escrevi olhou para levar cerca de 5 horas para N = 30.
— DLosc

A versão mais recente é executada quase instantaneamente.
— Optimizer

6
Há um erro no seu código. O caso de teste S=15não funciona.
— Jakube 28/05

3

Java 8, 123

x->java.util.stream.LongStream.range(1,(1<<x)+1).filter(i->{int n=0;for(;i>1;n++)i=i%2<1?i/2:3*i+1;return n==x;}).toArray()

Quando x30, o programa leva 15 minutos e 29 segundos.

Expandido

class Collatz {
    static IntFunction<long[]> f =
            x -> java.util.stream.LongStream.range(1, (1 << x) + 1).filter(i -> {
                int n = 0;
                for (; i > 1; n++)
                    i = i % 2 < 1 ? i / 2 : 3 * i + 1;
                return n == x;
            }).toArray();

    public static void main(String[] args) {
        System.out.println(Arrays.toString(f.apply(15)));
    }
}

Apenas curioso, quanto tempo isso leva para S = 30?
— Geobits 28/05

Isso funciona apenas no Java 8, correto? Usar javac 1.7.0_79no Ubuntu me deu muitos erros de sintaxe.
— DLosc 28/05

@DLosc Correct; Vou mencionar isso no post.
— Ypnypn

Restringir a condição do terminal do loop para i > 1 && ++n <= x(você também pode soltar n++) parece ser ainda mais rápido para apenas mais 5 caracteres ... cerca de 3 minutos para S = 30 para mim. Que fica raspada com segurança menos de um minuto se eu incluir .parallel()também, mas uma vez que este é o código-golf ...
— HJK

1

Python 2, 118 bytes

Bem, imaginei que não alcançaria a melhor pontuação em Python depois de ver a solução do @ Sp3000. Mas parecia um pequeno problema divertido, então eu queria tentar uma solução independente de qualquer maneira:

s={1}
for k in range(input()):
 p,s=s,set()
 for t in p:s.add(2*t);t>4and(t-1)%6==3and s.add((t-1)/3)
print sorted(s)

A mesma coisa antes de remover o espaço em branco:

s={1}
for k in range(input()):
    p,s=s,set()
    for t in p:
        s.add(2 * t)
        t > 4 and (t - 1) % 6 == 3 and s.add((t - 1) / 3)
print sorted(s)

Esta é uma implementação muito direta de uma primeira pesquisa abrangente. Em cada etapa, temos o conjunto com o tempo de parada ke derivamos o conjunto com o tempo de parada k + 1adicionando os possíveis predecessores de cada valort no conjunto da etapa k:

  • 2 * t é sempre um possível antecessor.
  • Se tpode ser escrito como 3 * u + 1, onde ué um número ímpar que não é 1, entãou é um predecessor.

Demora cerca de 0,02 segundos para executar N = 30no meu MacBook Pro.


Em geral, s.add(x)é desnecessário em um golfe, pois geralmente você pode fazê-lo s|={x}. Além disso, usar em ~-xvez de (x+1)salvar entre colchetes. Mas por outro lado, bom trabalho :)
— SP3000

@ Sp3000 Obrigado. Não posso substituir facilmente o segundo s.add()porque ele se torna uma tarefa e não pode mais fazer parte da expressão. Funciona para o primeiro. Os forloops baseados em contadores também são sempre bem detalhados. Eu pensei que poderia reduzi-lo usando um whileloop, mas acabou sendo exatamente do mesmo tamanho.
— Reto Koradi

Em vez de um forloop, desde que você não use a entrada de qualquer outra forma provavelmente você pode fazer exec"..."*input()em vez :)
— SP3000

1

PHP 5.4+, 178 bytes

A função

function c($s,$v=1,$p=[],&$r=[]){$p[]=$v;if(!$s--){return$r[$v][]=$p;}c($s,$v*2,$p,$r);is_int($b=($v-1)/3)&!in_array($b,$p)&$b%2?c($s,$b,$p,$r):0;ksort($r);return array_keys($r);}

Teste e Saída

echo "0 - ".implode(',',c(0)).PHP_EOL;
// 0 - 1
echo "1 - ".implode(',',c(1)).PHP_EOL;
// 1 - 2
echo "5 - ".implode(',',c(5)).PHP_EOL;
// 5 - 5,32
echo "9 - ".implode(',',c(9)).PHP_EOL;
// 9 - 12,13,80,84,85,512
echo "15 - ".implode(',',c(15)).PHP_EOL;
// 15 - 22,23,136,138,140,141,150,151,768,832,848,852,853,904,906,908,909,5120,5376,5440,5456,5460,5461,32768

S (30) é executado em 0,24 segundos * , retorna 732 elementos. Um casal é

86,87,89,520,522,524,525,528, [ ... ] ,178956928,178956960,178956968,178956970,1073741824

* Para economizar em bytes, eu tinha que adicionar ksorte array_keysa cada passo. A única outra opção que tive foi criar uma pequena função de invólucro que chama c()e depois chama array_keyse ksortno resultado uma vez. Mas devido ao tempo ainda estar decentemente decente, decidi levar o desempenho para uma baixa contagem de bytes. Sem a classificação e processamento adequados, o tempo é em média de 0,07 segundos para S (30).

Se alguém tiver alguma maneira inteligente de obter o processamento adequado apenas uma vez sem muitos bytes adicionais, informe-me! (Eu guardo meus números como chaves de matriz, daí o uso de array_keyse ksort)


0

Linguagem C

#include <stdio.h>
#include <limits.h>    
const int s = 30;

bool f(long i)
{
    int r = 0;
    for(;;)
        if (i < 0 || r > s) return false;
        else if (i == 1) break;
        else{r ++;i = i % 2 ? 3*i + 1 : i/2;}
    return (r==s);
}

void main(){
    for(long i = 1; i < LONG_MAX; i++) if (f(i)) printf("%ld ", i);
}

5
Olá e bem-vindo ao PPCG! Como se trata de uma competição de golfe por código , convém tornar seu código o mais curto possível. Além disso, inclua o nome do idioma na sua postagem.
— Alex A.

Você pode pressionar o {}botão para formatar seu código, o que eu fiz para você. Mas como Alex diz, adicione o nome do idioma (C?) E tente jogar com ele :) Mas seja bem-vindo!
— Sp3000 29/05

@ Sp3000 obrigado por ajudar a formatar o código
— windy

A função fnão está se comportando corretamente. Com s=5, recebo vários resultados incorretos. if (r == s)return true;deveria ser return (r==s), já fque não retornará nada significativo quando (r < s). Além disso, eu acho que você deve declarar iem fque long, uma vez que ele irá transbordar rapidamente para alguns valores.
— Dennis

@Dennis obrigado :) deveria ser #return (r==s);
— 2929 de vento
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.