计算Collat​​z表兄弟


21

为正整数n定义函数f(n),如下所示:

  • n / 2,如果n是偶数
  • 3 * n +1,如果n为奇数

如果您将此功能重复应用到任何 大于0的 n,则结果似乎总是收敛到1(尽管尚无人能​​够证明这一点)。此属性称为Collat​​z猜想

将整数的停止时间定义为必须通过Collat​​z函数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

让我们用相同的停止时间呼叫任何一组数字 Collat​​z表兄弟的数字。例如,5和32是Collat​​z表兄弟,停止时间为5。

您的任务:编写一个程序或函数,该程序或函数采用一个非负整数并生成一组Collat​​z表兄弟,其停止时间等于该整数。

输入项

通过STDIN,ARGV或函数参数指定的非负整数S。

输出量

它的停止时间是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
@JPMC停止时间的定义暗示了避免循环。停止时间4是2,而不是5,因为2是“必须达到Collat​​z函数才能通过1的次数”。
DLosc

啊,原谅我 我当时以为,一个数字可能会有多个停止时间,因为可能会导致多个路径。但这是关于从1开始构建的问题,而不是N。
JPMC

1
@DLosc Pyth,当然。
isaacg 2015年

Answers:


7

Pyth,26 24 21字节

Su+yMG-/R3fq4%T6G1Q]1

此代码可立即运行S=30。自己尝试:演示

感谢@isaacg节省了5个字节。

说明

我的代码以开头1并撤消Collat​​z函数。它映射所有数字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 2015年

1
如果将- ... 1总和放在减少内,则不需要使减少成为a .u,也不需要-F外部。保存2个字符。
isaacg 2015年

@isaacg谢谢。我实际上在以前的版本中有此功能,但是在调试错误时将其删除。
雅库布2015年

3
我已经借了@isaacg来回答自己的问题。我花了数小时试图找到删除重复项的最短代码,但这是迄今为止最优雅的解决方案。另外,我真的很喜欢您使用元组来丢弃无效商。可悲的是,CJam没有元组,但我还是设法无效商映射到1
丹尼斯

@Jakube q4%d6等效于!%hhd6,但短1个字符。
isaacg 2015年

8

Mathematica,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,仅将其作为参数并返回Collat​​z表兄弟的列表。

该算法是一个简单的广度优先搜索。给定S的Collat​​z表兄弟是S-1via 可以从Collat​​z表兄弟获得的所有整数,也可以是via 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

Python 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 2015年

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 2015年

@DLosc啊哈哈,谢谢。我仍然发誓,尽管有一种更好的短路方法……
Sp3000,2015年

我认为这~-是不必要的,因为您使用的是整数除法。
2015年

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编译器


较大的输入将花费多长时间? It should finish in seconds or minutes, not hours or days.
DLosc

知道了 在Python版本我写看着约需5小时N = 30
DLosc

最新版本几乎立即运行。
Optimizer

6
您的代码中有一个错误。测试用例S=15不起作用。
2015年

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

x为30时,程序花费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需要多长时间?
Geobits,2015年

这仅适用于Java 8,对吗?javac 1.7.0_79在Ubuntu上使用会给我带来很多语法错误。
DLosc

@DLosc正确;我会在帖子中提及。
Ypnypn

将循环终端条件限制为i > 1 && ++n <= x(也可以放下n++)似乎对于仅增加5个字符来说甚至更快……对于我来说,对于S = 30,大约3分钟。如果我也包括在内.parallel(),则可以安全地在一分钟之内剃光,但是由于这是代码高尔夫……
hjk 2015年

1

Python 2,118字节

好吧,我发现看到@ Sp3000的解决方案后,我无法获得最佳的Python分数。但这似乎是一个有趣的小问题,因此我还是想尝试一个独立的解决方案:

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循环来缩短它,但事实证明它的长度完全一样。
Reto Koradi

而不是for循环,因为您不以其他任何方式使用输入,所以您可以exec"..."*input()改用:)
Sp3000,2015年

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

*为了节省字节数,我必须添加 ksortarray_keys在每一步中和。唯一的其他选择,我是做小包装函数来调用c(),然后调用array_keysksort对结果一次。但是由于时间还算不错,我决定降低字节数来降低性能。如果没有适当的分类和处理,S(30)的平均时间为0.07秒

如果有人有任何聪明的方法只获得一次正确的处理而又没有太多的额外字节,请告诉我!(我将数字存储为数组键,因此使用array_keysksort


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!由于这是一场代码高尔夫竞赛,因此您需要使代码尽可能短。另外,请在帖子中添加语言名称。
Alex A.

您可以按{}一下按钮来设置代码格式,这是我为您完成的。但是正如亚历克斯所说,请添加语言名称(C?)并尝试打高尔夫球:)但欢迎您!
Sp3000

@ Sp3000感谢您帮助格式化代码
刮风

功能f表现不正常。使用s=5,我得到了很多错误的结果。if (r == s)return true;应该是这样return (r==s),因为f不会在什么时候返回有意义的结果(r < s)。另外,我觉得你应该声明if作为long,因为它会溢出很快对一些价值观。
丹尼斯

@丹尼斯,谢谢:)应该是return (r==s);
有风
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.