Розрахунок Коллаца Кузена


21

Визначте функцію f (n) для натурального цілого n таким чином:

  • n / 2 , якщо n парне
  • 3 * n + 1 , якщо n непарне

Якщо ви неодноразово застосовуєте цю функцію до будь-якого n більше 0, результат завжди схоже на 1 (хоча це ще ніхто не зміг довести). Ця властивість відома під назвою Collatz .

Визначте час зупинки цілого числа як кількість разів, яке вам доведеться пройти через функцію Collatz f, перш ніж воно досягне 1. Ось час зупинки перших 15 цілих чисел:

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

Давайте назвемо будь-який набір чисел з однаковим часом зупинки двоюрідних братів Коллаца . Наприклад, 5 і 32 - двоюрідні брати Коллаца, час зупинки - 5.

Ваше завдання: написати програму або функцію, яка займає негативне ціле число і генерує набір двоюрідних братів Коллаца, час зупинки яких дорівнює цьому цілому.

Вхідні дані

Невід’ємне ціле число S, задане через STDIN, ARGV або аргумент функції.

Вихідні дані

Список усіх чисел, час зупинки яких S, відсортований у порядку зростання . Список може бути виведений вашою програмою, або повернутий або виведений вашою функцією. Формат виводу є гнучким: розділений пробілом, розділений новим рядком або будь-який стандартний формат списку вашої мови добре, якщо цифри легко відрізняються один від одного.

Вимоги

Ваша заявка повинна дати правильні результати для будь-яких S ≤ 30. Це має закінчитися за секунди або хвилини, а не години чи дні.

Приклади

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

Ось перелік виводу для S = 30 .

Це : найкоротша програма виграє в байтах. Удачі!


Що з циклів? Я не бачив згадки про уникнення циклу. Оскільки для S = 5, є 3 значення [4, 5, 32], тому що ви можете перейти "1 - 2 - 4 - 1 - 2- 4"
JPMC

1
@JPMC Уникнення циклу має на увазі визначення часу зупинки. Час зупинки 4 становить 2, а не 5, оскільки 2 - це "кількість разів, коли вам доведеться пройти його через функцію Колатца, перш ніж вона досягне 1."
DLosc

Ах, прости мене. Я думав, що число може мати кілька разів зупинки, оскільки до нього можуть призвести кілька шляхів. Але це було відносно розбудови з 1, не працюючи з Н. Пробачте про це.
JPMC

1
@DLosc Pyth, звичайно.
isaacg

1

Відповіді:


7

Pyth, 26 24 21 байт

Su+yMG-/R3fq4%T6G1Q]1

Цей код запускається миттєво для S=30. Спробуйте самі: Демонстрація

Завдяки @isaacg за збереження 5 байт.

Пояснення

Мій код починається з 1і скасовується функція Collatz. Він відображає всі номери dна S-1крок до 2*dі (d-1)/3. Останній не завжди дійсний.

                        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

Це прекрасне використання -F.
isaacg

1
Якщо ви покладете - ... 1навколо суми зменшення всередині зменшення, то вам не потрібно зменшення як а .u, ні -Fзовні. Зберігає 2 символи.
isaacg

@isaacg Дякую Я фактично мав це в попередній версії, але видалив під час налагодження помилки.
Якубе

3
Я запозичив @ isaacg's для власної відповіді. Я витратив години, намагаючись знайти найкоротший код для видалення дублікатів, але це, безумовно, найелегантніше рішення. Крім того, мені дуже подобається, що ти використовуєш кортеж, щоб відкинути недійсні квоти. На жаль, у CJam немає кортежів, але мені вдалося зіставити недійсні коефіцієнти до 1.
Денніс

@Jakube q4%d6еквівалентний !%hhd6, але на 1 символ коротший.
isaacg

8

Математика, 98 92 89 байт

Цей розчин вирішується S = 30негайно:

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

Це неназвана функція, яка бере Sсвій єдиний параметр і повертає список родичів Коллаца.

Алгоритм - це простий пошук на широту. Двоюрідні брати Коллаца для даного S- це цілі числа, до яких можна дістатися від двоюрідних братів Коллаца за S-1допомогою 2*nабо непарних чисел, до яких можна дістатися через (n-1)/3. Нам також потрібно переконатися, що ми виробляємо лише ті цілі числа, які були досягнуті вперше після Sкроків, тому ми стежимо за всіма попередніми родичами pта вилучаємо їх з результату. Оскільки ми все одно це робимо, ми можемо зберегти кілька байтів, обчисливши кроки від усіх попередніх родичів (не тільки тих, від яких S-1), щоб зберегти кілька байтів (це робить трохи повільніше, але не помітно для необхідного S).

Ось трохи читабельніша версія:

(
  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

Пітон 2, 86 83 75 73 71 байт

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

Телефонуйте як f(30). n = 30майже миттєво.

(Дякуємо @DLosc за ідею повторення, kбудучи числом, а не списком родичів і кількома байтами. Дякую @isaacg за скидання ~-.)

Цей варіант набагато коротший, але, на жаль, займає занадто довго через експоненціальне розгалуження:

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

Цікаво - моє оригінальне рішення дуже схоже, але ( з парою оптимізацій від вас) виходить 2 байт коротше: 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]. Це менш ефективно з функціональними дзвінками, але все-таки працює n = 30за секунду.
DLosc

1
@DLosc Мені сподобалась ваша ідея і зробила її кращою :)
Sp3000

Приємно! Ось ще 2 байти: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 Ахаха дякую. Я все ще клянусь, що повинен бути кращий спосіб короткого замикання ...
Sp3000,

Я думаю, що ~-це зайве, оскільки ви використовуєте ціле ділення.
isaacg

5

CJam, 29 26 байт

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

Кредит йде на @isaacg за його ідею видалити 1 після кожної ітерації, що врятувало мені два байти безпосередньо та ще один побічно.

Спробуйте його в Інтернеті в інтерпретаторі CJam (має закінчитися менше ніж за секунду).

Як це працює

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 байт

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

Пояснення найближчим часом. Це набагато швидша версія, ніж підхід "досить прямо" (див. Це в історії редагування).

Спробуйте онлайн тут за N = 30який працює в секундах на онлайн - версію і відразу ж в Java Compiler


Скільки часу буде потрібно для більших входів? It should finish in seconds or minutes, not hours or days.
DLosc

А, бачу.
Написана

Остання версія працює майже миттєво.
Оптимізатор

6
У вашому коді помилка. Тестовий S=15зразок не працює.
Якубе

3

Ява 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()

Коли x30, програма займає 15 хвилин і 29 секунд.

Розширено

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

Просто цікаво, скільки часу займає S = 30?
Геобіт

Це працює лише в Java 8, правда? Використання javac 1.7.0_79Ubuntu дало мені багато синтаксичних помилок.
DLosc

@DLosc Правильно; Я згадаю про це у дописі.
Ypnypn

Обмеження умови терміналу циклу i > 1 && ++n <= x(ви можете також відмовитись n++) здається, ще швидше, лише для ще 5 символів ... приблизно 3 хвилини для S = 30. Це спокуситься безпечно за хвилину, якщо я також включаю .parallel(), але оскільки це код-гольф ...
hjk

1

Python 2, 118 байт

Ну, я зрозумів, що не досягну найкращої оцінки Python, побачивши рішення @ Sp3000. Але це виглядало як весела маленька проблема, тому я хотів все-таки спробувати самостійне рішення:

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)

Те саме, що знімати пробіл:

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)

Це дуже пряма реалізація широкого першого пошуку. На кожному кроці ми маємо набір із часом зупинки k, і отримуємо набір із часом зупинки k + 1, додаючи можливих попередників кожного значенняt у наборі із кроку k:

  • 2 * t завжди можливий попередник.
  • Якщо tможна записати як 3 * u + 1, де uнепарне число, якого немає 1, то uє і попередником.

N = 30На моєму MacBook Pro потрібно запустити приблизно 0,02 секунди .


Загалом, s.add(x)в гольфі це зайве, оскільки s|={x}замість цього можна робити . Також використання ~-xзамість (x+1)економії на дужках. Але в іншому випадку хороша робота :)
Sp3000

@ Sp3000 Спасибі Я не можу легко замінити другий, s.add()тому що він стає завданням і більше не можу бути частиною виразу. Це працює для першого. В forпетлі на основі лічильників завжди вид багатослівний , а також. Я думав, що можу вкоротити це за допомогою whileпетлі, але виявилося точно такої ж довжини.
Рето Кораді

Замість forциклу, оскільки ви не використовуєте вхід будь-яким іншим способом, ви, ймовірно, можете зробити це exec"..."*input()замість цього :)
Sp3000

1

PHP 5.4+, 178 байт

Функція

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

Тест та вихід

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) працює за 0,24 секунди * , повертає 732 елементи. Пара є

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

* Щоб заощадити на байтах, мені довелося додавати ksortі array_keysна кожному кроці. Єдиний інший вибір, який я мав, - це зробити невелику функцію обгортки, яка дзвонить, c()а потім дзвонить array_keysі отримує ksortрезультат один раз. Але через те, що час все ще був пристойно спритним, я вирішив скористатися хітом для низької кількості байтів. Без належного сортування та обробки час становить 0,07 секунди в середньому для S (30) .

Якщо у когось є розумні способи отримати належну обробку лише один раз без занадто багато додаткових байтів, будь ласка, дайте мені знати! (Я зберігаю свої номери як ключі масиву, отже, використання array_keysта ksort)


0

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
Привіт і ласкаво просимо до PPCG! Оскільки це змагання з кодового гольфу , ви хочете зробити свій код якомога коротшим. Також, будь ласка, вкажіть назву мови у своєму дописі.
Олексій А.

Ви можете натиснути {}кнопку, щоб відформатувати свій код, що я зробив для вас. Але, як каже Алекс, будь ласка, додайте назву мови (С?) Та спробуйте гольф :) Але ласкаво просимо!
Sp3000

@ Sp3000 дякую за допомогу в формату коду
вітряний

Функція fведе себе неправильно. З s=5, я отримую купу неправильних результатів. if (r == s)return true;повинно бути return (r==s), оскільки fне поверне що-небудь значущого коли (r < s). Крім того , я думаю , ви повинні оголосити iв fякості long, так як він буде переповнення досить швидко для деяких значень.
Денніс

@Dennis спасибі :) це повинно бутиreturn (r==s);
вітряно
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.