Calcolo dei cugini di Collatz


21

Definire la funzione f (n) per un numero intero positivo n come segue:

  • n / 2 , se n è pari
  • 3 * n + 1 , se n è dispari

Se si applica ripetutamente questa funzione a qualsiasi n maggiore di 0, il risultato sembra sempre convergere in 1 (anche se nessuno è ancora stato in grado di dimostrarlo). Questa proprietà è nota come congettura di Collatz .

Definisci il tempo di arresto di un numero intero come il numero di volte che devi passare attraverso la funzione Collatz f prima che raggiunga 1. Ecco i tempi di arresto dei primi 15 numeri interi:

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

Chiamiamo qualsiasi set di numeri con lo stesso tempo di arresto Cugini Collatz . Ad esempio, 5 e 32 sono cugini di Collatz, con un tempo di arresto di 5.

Il tuo compito: scrivi un programma o una funzione che accetta un numero intero non negativo e genera l'insieme dei cugini Collatz il cui tempo di arresto è uguale a quel numero intero.

Ingresso

Un numero intero non negativo S, fornito tramite STDIN, ARGV o argomento della funzione.

Produzione

Un elenco di tutti i numeri il cui tempo di arresto è S, ordinati in ordine crescente . L'elenco può essere emesso dal programma o restituito o emesso dalla funzione. Il formato di output è flessibile: va separato lo spazio, separato da una nuova riga o qualsiasi formato di elenco standard della tua lingua, purché i numeri siano facilmente distinguibili l'uno dall'altro.

Requisiti

L'invio deve fornire risultati corretti per qualsiasi S ≤ 30. Dovrebbe concludersi in secondi o minuti, non ore o giorni.

Esempi

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

Ecco una sintesi dell'output per S = 30 .

Questo è : vince il programma più breve in byte. In bocca al lupo!


Che dire dei cicli? Non ho visto una menzione di evitamento del ciclo. Perché per S = 5, ci sono 3 valori [4, 5, 32] perché puoi andare "1 - 2 - 4 - 1 - 2- 4"
JPMC

1
@JPMC La prevenzione del ciclo è implicita nella definizione del tempo di arresto. Il tempo di arresto di 4 è 2, non 5, perché 2 è "il numero di volte che devi passarlo attraverso la funzione Collatz prima che raggiunga 1."
DLosc,

Ah, perdonami. Pensavo che un numero potesse avere più tempi di arresto, poiché più percorsi possono condurlo. Ma questo era rispetto all'edificazione da 1, non funzionante da N. Mi dispiace per quello.
JPMC

1
@DLosc Pyth, ovviamente.
isaacg,

Risposte:


7

Pyth, 26 24 21 byte

Su+yMG-/R3fq4%T6G1Q]1

Questo codice viene eseguito immediatamente per S=30. Provalo tu stesso: dimostrazione

Grazie a @isaacg per aver salvato 5 byte.

Spiegazione

Il mio codice inizia con 1e annulla la funzione Collatz. Mappa tutti i numeri ddel S-1passaggio su 2*de (d-1)/3. L'ultimo non è sempre valido però.

                        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

È un bellissimo uso di -F.
isaacg,

1
Se aggiungi una - ... 1somma all'interno della riduzione, non è necessario che la riduzione sia una .u, né l' -Festerno. Salva 2 caratteri.
isaacg,

@isaacg Grazie. In realtà l'ho avuto in una versione precedente, ma l'ho rimosso durante il debug di un errore.
Jakube,

3
Ho preso in prestito @ isaacg per la mia risposta. Ho passato ore a cercare il codice più breve per rimuovere i duplicati, ma questa è di gran lunga la soluzione più elegante. Inoltre, mi piace molto l'uso di una tupla per scartare i quozienti non validi. Purtroppo, CJam non ha tuple, ma sono riuscito a mappare quozienti non validi a 1.
Dennis

@Jakube q4%d6è equivalente a !%hhd6, ma 1 carattere più corto.
isaacg,

8

Mathematica, 98 92 89 byte

Questa soluzione risolve S = 30immediatamente:

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

Questa è una funzione senza nome che prende Scome unico parametro e restituisce un elenco dei cugini Collatz.

L'algoritmo è una semplice ricerca in ampiezza. I cugini Collatz per un dato Ssono tutti gli interi che possono essere raggiunti dai cugini Collatz per S-1via 2*no numeri dispari che possono essere raggiunti via (n-1)/3. Dobbiamo anche assicurarci di produrre solo i numeri interi che sono stati raggiunti per la prima volta dopo i Spassaggi, in modo da tenere traccia di tutti i cugini precedenti pe rimuoverli dal risultato. Dato che lo stiamo facendo comunque, possiamo salvare alcuni byte calcolando i passaggi di tutti i cugini precedenti (non solo quelli di S-1) per salvare alcuni byte (che lo rende leggermente più lento, ma non evidentemente per il necessario S).

Ecco una versione leggermente più leggibile:

(
  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 byte

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

Chiama come f(30). n = 30è praticamente istantaneo.

(Grazie a @DLosc per l'idea di ricorrere kessendo un numero piuttosto che un elenco di cugini e pochi byte. Grazie a @isaacg per aver perso ~-.)

Questa variante è molto più breve, ma purtroppo richiede troppo tempo a causa della ramificazione esponenziale:

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

Interessante - la mia soluzione originale è molto simile, ma (prendendo un paio di ottimizzazioni dalla tua) esce 2 byte più breve: 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]. È meno efficiente con le chiamate di funzione ma lo fa ancora n = 30in meno di un secondo.
DLosc,

1
@DLosc Mi è piaciuta la tua idea e l'ho resa migliore :)
Sp3000,

Bello! Ecco altri 2 byte di sconto: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 grazie. Giuro ancora che ci deve essere un modo migliore di corto circuito però ...
Sp3000,

Penso che ~-non sia necessario perché stai usando la divisione intera.
isaacg,

5

CJam, 29 26 byte

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

Il merito va a @isaacg per la sua idea di rimuovere 1 dopo ogni iterazione, che mi ha salvato due byte direttamente e uno indirettamente.

Provalo online nell'interprete CJam (dovrebbe finire in meno di un secondo).

Come funziona

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 byte

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

Spiegazione in arrivo. Questa è una versione molto più veloce dell'approccio "piuttosto semplice" (vederlo nella cronologia delle modifiche).

Provalo online qui per il N = 30quale viene eseguito in pochi secondi sulla versione online e immediatamente nel compilatore Java


Quanto tempo ci vorrà per gli input più grandi? It should finish in seconds or minutes, not hours or days.
DLosc,

Ah, capisco. La versione di Python che ho scritto sembrava impiegare circa 5 ore per N = 30.
DLosc,

L'ultima versione funziona quasi istantaneamente.
Ottimizzatore

6
C'è un bug nel tuo codice. Il caso di test S=15non funziona.
Jakube,

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 xè 30, il programma dura 15 minuti e 29 secondi.

allargato

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

Solo curioso, quanto tempo ci vuole per S = 30?
Geobits,

Funziona solo con Java 8, giusto? L'utilizzo javac 1.7.0_79su Ubuntu mi ha dato molti errori di sintassi.
DLosc,

@DLosc Correct; Lo citerò nel post.
Ypnypn,

Limitare la condizione del terminale del loop a i > 1 && ++n <= x(puoi n++anche rilasciarlo ) sembra essere ancora più veloce solo per altri 5 caratteri ... circa 3 minuti per S = 30 per me. Se includo .parallel()anche questo, mi rendo al sicuro in meno di un minuto , ma dato che si tratta di code-golf ...
hjk

1

Python 2, 118 byte

Bene, ho pensato che non avrei raggiunto il miglior punteggio Python dopo aver visto la soluzione di @ Sp3000. Ma sembrava un piccolo problema divertente, quindi volevo comunque provare una soluzione indipendente:

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)

Stessa cosa prima di rimuovere gli spazi bianchi:

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)

Questa è un'implementazione molto diretta di una prima ricerca ampia. In ogni passaggio, abbiamo l'insieme con il tempo di arresto ke ricaviamo l'insieme con il tempo di arresto k + 1aggiungendo i possibili predecessori di ciascun valore tnell'insieme dal passaggio k:

  • 2 * t è sempre un possibile predecessore.
  • Se tpuò essere scritto come 3 * u + 1, dove uc'è un numero dispari che non lo è 1, allora uè anche un predecessore.

Sono necessari circa 0,02 secondi per l'esecuzione N = 30sul mio MacBook Pro.


In generale, s.add(x)non è necessario in un golf poiché di solito si può fare s|={x}invece. Inoltre, utilizzando ~-xinvece di (x+1)salvare tra parentesi. Ma per il resto, buon lavoro :)
Sp3000,

@ Grazie Sp3000. Non posso facilmente sostituire il secondo s.add()perché diventa un compito e non può più far parte dell'espressione. Funziona per il primo. Anche i forloop basati sui contatori sono sempre piuttosto prolissi. Ho pensato di accorciarlo usando un whileloop, ma si è rivelato essere esattamente lo stesso della stessa lunghezza.
Reto Koradi,

Invece di un forciclo, dal momento che non usi l'input in nessun altro modo, probabilmente puoi farlo exec"..."*input()invece :)
Sp3000,

1

PHP 5.4+, 178 byte

La funzione

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

Test e output

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) viene eseguito in 0,24 secondi * , restituisce 732 elementi. Un paio lo sono

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

* Per risparmiare sui byte, ho dovuto aggiungere ksorte array_keysad ogni passo. L'unica altra scelta che ho avuto è stata quella di fare una piccola funzione wrapper che chiama c()e poi chiama array_keyse ksortsul risultato una volta. Ma a causa del tempo ancora discretamente scattante, ho deciso di prendere il colpo di prestazione per il conteggio di byte basso. Senza l'ordinamento e l'elaborazione adeguati, il tempo è mediamente 0,07 secondi per S (30).

Se qualcuno ha modi intelligenti per ottenere l'elaborazione corretta una sola volta senza troppi byte aggiuntivi, per favore fatemelo sapere! (Memorizzo i miei numeri come chiavi dell'array, quindi l'uso di array_keyse ksort)


0

Linguaggio 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
Ciao e benvenuto in PPCG! Poiché si tratta di una competizione di code-golf , ti consigliamo di rendere il tuo codice il più breve possibile. Inoltre, inserisci il nome della lingua nel tuo post.
Alex A.

Puoi {}premere il pulsante per formattare il codice, cosa che ho fatto per te. Ma come dice Alex, per favore aggiungi il nome della lingua (C?) E prova a giocare a golf :) Ma benvenuto!
Sp3000

@ Sp3000 grazie per l'aiuto nel formattare il codice
ventoso

La funzione fnon si sta comportando correttamente. Con s=5, ottengo un sacco di risultati errati. if (r == s)return true;dovrebbe essere return (r==s), poiché fnon restituirà nulla di significativo quando (r < s). Inoltre, penso che si dovrebbe dichiarare iin fquanto long, dal momento che traboccherà abbastanza rapidamente per alcuni valori.
Dennis,

@Dennis grazie :) dovrebbe esserereturn (r==s);
ventoso
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.