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.