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!

DLosc
fonte
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
Jakube
fonte
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
) &
Martin Ender
fonte
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))
Sp3000
fonte
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))
Dennis
fonte
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

Optimizer
fonte
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)));
    }
}
Ypnypn
fonte
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.

Reto Koradi
fonte
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)

JPMC
fonte
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);
}
ventoso
fonte
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