易裂变数字


47

我在进行OEIS的开发时发现了这个序列,但是从来没有绕过它作为答案。在用Mathematica编写了参考实现之后,我认为这是一个有趣的练习,但是要作为一个单独的挑战来做,所以我们开始吧。

让我们建造一个数字裂变反应堆!考虑一个正整数N。举例来说,我们来看一下24。要分解该数字,我们必须找到相加为的最大连续正整数N。在这种情况下,即为7 + 8 + 9 = 24。因此,我们24分为三个新数字。但是,如果没有链式反应,裂变反应堆就不会多了。因此,让我们递归地重复这些组件的过程:

       24
       /|\
      / | \
     /  |  \
    7   8   9
   / \     /|\
  3   4   / | \
 / \     /  |  \
1   2   2   3   4
           / \
          1   2

请注意,只要数字不能分解为较小的连续整数,我们就会停止该过程。另请注意,我们本来可以将编写94 + 5,但2 + 3 + 4具有更多组件。的裂变数N现在定义为在此过程中,包括获得整数的个数N本身。上面的树有13个节点,所以F(24) = 13

此序列是OEIS条目A256504

从开始的前40个词N = 1

1, 1, 3, 1, 5, 6, 5, 1, 6, 7, 12, 10, 12, 11, 12, 1, 8, 16, 14, 17, 18, 18,
23, 13, 21, 18, 22, 23, 24, 19, 14, 1, 22, 20, 23, 24, 31, 27, 25, 26

可以在此pastebin中找到前1000个术语。

挑战

给定正整数N,确定其裂变数F(N)。(因此,您无需涵盖0OEIS上列出的领先企业。)

您可以编写程序或函数,通过STDIN(或最接近的替代方案),命令行自变量或函数自变量获取输入,并通过STDOUT(或最接近的替代方案),函数返回值或函数(out)参数输出结果。

这是代码高尔夫球,因此最短的答案(以字节为单位)获胜。

奖励问题:您能找到此序列的任何有趣的特性吗?


我注意到OEIS似乎在n = 34时出错:从n = 32开始,它(当前)列出了1、22、22、23、24、31,而不是1、22、20、23、24, 31.
mathmandan

1
@mathmandan很好,我可能会提出一个更正(以及第一个图表)。
马丁·恩德


@mathmandan仅供参考,我现在已经纠正了序列和示例,还添加了我的参考实现和前10k项。
Martin Ender 2015年

看起来不错!感谢您的工作!
mathmandan 2015年

Answers:


16

Pyth,23 22 21字节

Lh&lJfqbsT.:tUb)syMeJ

这定义了一个递归函数y。在线尝试:演示

说明:

L                      define a function y(b): return ...
            tUb          the list [1, 2, ..., b-1]
          .:   )         generate all consecutive sub-sequences
     f                   filter for sub-sequences T, which satisfy:
      qbsT                   b == sum(T)
    J                    and store them in J

                         return 
   lJ                        len(J)
  &                        and (if len(J) == 0 then 0 else ...)
                    eJ       last element of J (=longest sub-sequence) 
                  yM         recursive calls for all these numbers
                 s           sum
 h                         incremented by one (counting the current node)

52

裂变1328 989 887 797字节

这个答案有点长(我希望我们有可折叠的区域)...请不要忘记滚动过去,并向其他答案展示一些爱!

编写此代码是激发这一挑战的原因。我想在Fission中为EOEIS添加一个答案,这使我进入了这个序列。但是,实际上学习裂变并实施此过程需要花费几周的时间来完成。在此期间,这个序列确实对我产生了影响,所以我决定为此提出一个单独的挑战(再者,无论如何,在EOEIS上都不会特别困难)。

因此,我向您介绍Monstrosity:

 R'0@+\
/  Y@</ /[@ Y]_L
[? % \  / \ J
   \$@  [Z/;[{+++++++++L
UR+++++++++>/;
9\   ;    7A9
SQS  {+L  /$     \/\/\/\/\/   5/ @  [~ &@[S\/ \  D /8/
~4X /A@[  %5                   /; &    K  } [S//~KSA /
  3    \  A$@S  S\/  \/\/\/   \/>\ /S]@A  /  \ { +X
W7           X  X    /> \      +\ A\ /   \ /6~@/ \/
        /   ~A\;     +;\      /@
    ZX [K    / {/  / @  @ }  \ X @
       \AS   </      \V /    }SZS S/
         X   ;;@\   /;X  /> \ ; X X
 ;       \@+  >/ }$S SZS\+;    //\V
           / \\  /\; X X @  @  \~K{
           \0X /     /~/V\V /   0W//
    \        Z      [K \  //\
W       /MJ $$\\ /\7\A  /;7/\/ /
       4}K~@\ &]    @\  3/\
 /     \{   }$A/1 2  }Y~K <\
[{/\  ;@\@  /   \@<+@^   1;}++@S68
@\ <\    2        ;   \    /
$  ;}++ +++++++L
%@A{/
M  \@+>/
~     @
SNR'0YK
  \  A!/

它期望输入中没有结尾的换行符,因此您可能希望将其命名为echo -n 120 | ./Fission oeis256504.fis

布局很可能仍然要高效得多,所以我觉得还是有很多可以改进的空间在这里(例如,这包含911 581 461 374位)。

在进行解释之前,请先进行测试说明:官方口译员不能完全按原样工作。a)Mirror.cpp不能在许多系统上编译。如果遇到此问题,只需注释掉有问题的行-此代码中未使用受影响的组件(随机镜像)。b)有一些错误可能导致不确定的行为(这种复杂的程序很可能会导致错误)。您可以应用此修补程序来修复它们。完成此操作后,您应该可以使用以下命令编译解释器

g++ -g --std=c++11 *.cpp -o Fission

有趣的事实:该程序几乎使用了Fission提供的所有组件,除了#(随机镜),:(半镜)-|(普通镜)和"(打印模式)。

到底怎么回事?

警告:这将是很长的时间...我假设您对Fission的工作方式以及如何编程的想法非常感兴趣。因为如果您不这样做,我不确定该如何总结。(尽管下一段给出了对该语言的一般描述。)

裂变是一种二维编程语言,其中数据和控制流都由在网格中移动的原子表示。如果您以前曾经看过或使用过Marbelous,则这个概念应该很熟悉。每个原子都有两个整数性质:非负质量和任意能量。如果质量变为负数,原子将从网格中移出。在大多数情况下,您可以将质量视为原子的“值”,而将能量视为某种元属性,该元属性被多个组件用来确定原子的流动(即,大多数开关取决于原子的符号)。能量)。(m,E)如有必要,我将用表示原子。在程序开始时,网格以一堆(1,0)原子从四个位置上的任意位置放置UDLR(字母表示原子最初移动的方向)。然后,板上装有一堆组件,这些组件会改变原子的质量和能量,改变其方向或执行其他更复杂的操作。有关完整列表,请参见esolangs页面,但在本说明中将介绍其中的大多数内容。另一个重要点(程序会多次使用该点)是网格是环形的:撞到任一侧的原子重新出现在相反的一侧,朝相同的方向移动。

我用几个较小的部分编写了程序,并在最后将它们组装起来,所以这就是我的解释方法。

atoi

这个组件看似没有意思,但是它很好而且很简单,可以让我介绍Fission的算术和控制流程的许多重要概念。因此,我将详细介绍这一部分,以便减少其他部分,以介绍新的Fission机制,并指出更高级的组件,您应该可以对其进行详细的控制。

裂变只能读取单个字符的字节值,而不能读取整数。虽然这是可以接受的做法,但我想着可以做到这一点,并且可以正确地解析STDIN上的实际整数。这是atoi代码:

     ;
 R'0@+\
/  Y@</ /[@ Y]_L
[? % \  / \ J 
   \$@  [Z/;[{+++++++++L
UR+++++++++>/;
           O

裂变反应堆是聚变反应堆中最重要的两个组成部分。裂变反应堆是任何一种V^<>(上述代码使用<>)。裂变反应堆可以存储原子(通过将其发送到角色的楔子中),默认值为(2,0)。如果一个原子碰到角色的顶点,则将两个新原子发送到侧面。通过将入射质量除以存储质量(即默认减半)来确定其质量-左原子获得该值,右原子获得质量的其余部分(即,裂变中的质量守恒) 。两个出射原子的入射能量都为储存的能量。这意味着我们可以使用裂变反应堆进行算术运算-进行减法和除法运算。如果从该地点撞上裂变反应堆,则原子将简单地沿对角线反射,然后沿角色顶点的方向移动。

聚变反应堆是任何一种YA{}(以上代码使用Y{)。它们的功能相似:它们可以存储一个原子(默认值(1,0)),当从顶点撞击时,两个新原子将被发送到侧面。但是,在这种情况下,两个原子将是相同的,始终保留输入能量,并将输入质量乘以存储的质量。也就是说,默认情况下,聚变反应堆只是复制任何撞击其顶点的原子。当从侧面撞击时,聚变反应堆会稍微复杂一些:原子(独立于其他内存)存储,直到原子撞到另一侧。当发生这种情况时,一个新原子向顶点方向释放,其质量和能量是两个旧原子的总和。如果一个新原子在匹配原子到达另一侧之前撞到同一侧,则旧原子将被简单地覆盖。聚变反应堆可用于实现加法和乘法。

我想忽略的另一个简单组件是[]它只需将原子的方向分别设置为右和左(无论传入方向如何)。垂直等效项是M(向下)和W(向上),但是它们未用于atoi代码。释放初始原子后UDLR也充当WM][

无论如何,让我们看看那里的代码。该程序以5个原子开始:

  • RL在底部简单地得到它们的质量增量(与+)成为(10,0)然后分别存储在裂变和聚变反应堆,。我们将使用这些反应堆来解析以10为基的输入。
  • L在右上角被它的质量递减(用_),以成为(0,0)与被存储在聚变反应堆的侧Y。这是为了跟踪正在读取的数字-在读取数字时,我们将逐渐增加并乘以该数字。
  • R设置左上角的质量为0(48)的字符代码'0,然后与质量和能量交换,@最后质量增加一次,+得到(1,48)。然后用对角镜将其重定向\/存储在裂变反应堆中。我们将使用48for减法将ASCII输入转换为数字的实际值。我们还必须增加质量1以避免被除以0
  • 最后,U左下角的实际使一切运动,最初仅用于控制流。

重定向到右侧后,控制原子命中?。这是输入组件。它读取一个字符,并将原子的质量设置为读取的ASCII值,将能量设置为0。如果我们改为EOF,则能量将设置为1

原子继续,然后撞击%。这是一个镜像开关。对于非正能量,这就像/一面镜子。但是对于正能量,它的作用类似于\(并且也将能量减1)。因此,当我们读取字符时,原子将向上反射,我们可以处理字符。但是,当我们完成输入后,原子将向下反射,我们可以应用不同的逻辑来检索结果。仅供参考,相反的成分是&

因此,我们现在有了一个前进的原子。我们要为每个字符执行的操作是读取其数字值,将其添加到我们的运行总计中,然后将该运行总计乘以10以准备下一个数字。

角色原子首先击中(默认)聚变反应堆Y。这会拆分原子,我们使用左手的副本作为控制原子,以循环回到输入组件并读取下一个字符。正确的副本将被处理。考虑一下我们已经读过字符的情况3。我们的原子将会(51,0)。我们与交换质量和能量@,以便可以利用下一个裂变反应堆的减法。反应器减去48了能量(不改变质量),因此它发出了两个副本(0,3)-能量现在对应于我们已读取的数字。即将复制的副本;仅用丢弃(一个会破坏所有传入原子的组件)。我们将继续处理后续副本。您需要遵循其路径/\镜像一点。

@之前的核聚变反应堆再次交换物质和能量,这样我们将添加(3,0)到我们的运行总和的Y。请注意,运行总计本身将始终具有0能量。

现在J是一个跳跃。它的作用是通过其能量使任何传入的原子向前跳。如果是0,原子将一直继续前进。如果是1,它将跳过一个单元格,如果它将2跳过两个单元格,依此类推。能量是花在跳跃中的,所以原子总是以能量结束0。由于运行中的总能量确实为零,因此暂时忽略该跳跃,并将原子重定向到聚变反应堆{,该反应堆的质量乘以10。下降的副本将被丢弃,;而上升的副本将Y作为新的运行总量反馈到反应堆中。

上面的代码一直重复(以一种有趣的流水线方式,在处理完新数字之前先处理新数字),直到我们达到EOF为止。现在,%将向下发射原子。这个想法是(0,1)在击中正在运行的总反应堆之前将这个原子变成现在,这样a)总原子不会受到影响(零质量),并且b)我们获得了1跳过原子核的能量[。我们可以轻松地利用来照顾能量$,这会增加能量。

问题是,?当您按下EOF时,不会重设质量,因此质量仍将是读取的最后一个字符的质量,并且能量将保持不变0(因为%减小10)。因此,我们想摆脱那种麻烦。为此,我们@再次交换了质量和能量。

在完成本节之前,我需要再介绍一个组件:Z。这基本上与%或相同&。不同之处在于,它使正能量原子直接通过(同时减少能量)并使非正能量原子向左偏转90度。我们可以使用它通过Z反复遍历一个原子来消除它的能量-一旦能量消失,原子就会发生偏转并离开回路。这就是这种模式:

/ \
[Z/

一旦能量为零,原子将向上移动。在程序的其他部分,我将以一种形式或另一种形式多次使用此模式。

所以,当原子离开这个小圈,这将是(1,0)与交换到(0,1)@击打的核聚变反应堆释放输入的最终结果之前。但是,运行总计将减少10倍,因为我们已经尝试将其乘以另一个数字。

所以现在有了能量1,这个原子将跳过[并跳入原子/。这会将其偏转到裂变反应堆中,我们已经准备好将其除以10并修复无关的乘法。再次,我们丢弃带有的一半,;而保留另一半作为输出(在此表示O,它将简单地打印相应的字符并破坏原子-在完整程序中,我们将继续使用原子)。

itoa

           /     \
input ->  [{/\  ;@
          @\ <\   
          $  ;}++ +++++++L
          %@A{/
          M  \@+>/
          ~     @
          SNR'0YK
            \  A!/

当然,我们还需要将结果转换回字符串并打印出来。这就是本部分的目的。假设输入没有在第10个滴答声之前到达,但是在完整的程序中很容易给出。该位可以在完整程序的底部找到。

该代码引入了一个非常强大的Fission新组件:stack K。堆栈最初是空的。当具有非负能量的原子撞击电池堆时,该原子被简单地推到电池堆上。当具有负能量的原子撞击堆栈时,其质量和能量将被堆栈顶部的原子替换(从而被弹出)。如果堆栈为空,则原子的方向相反,其能量变为正(即乘以-1)。

好了,回到实际的代码。该itoa代码段的想法是反复对输入取10取模以找到下一个数字,同时将输入除以10进行下一次迭代。这将产生相反顺序的所有数字(从最低有效到最高有效)。为了固定顺序,我们将所有数字推入堆栈,最后将它们逐一弹出以进行打印。

该代码的上半部分执行数字计算:L加号的加号为10,我们将其克隆并输入到裂变和聚变反应堆中,因此我们可以除以10,然后乘以10。循环基本上从[左上角的。当前值被分割:一个副本除以10,然后乘以10并存储在裂变反应堆中,然后在顶点处被另一个副本击中。计算结果i % 10i - ((i/10) * 10)。还要注意,在A除法之后和乘法之前拆分中间结果,这样我们就可以i / 10进入下一个迭代。

%一旦迭代变量达到0 ,中止循环。由于这或多或少是一个do-while循环,因此该代码甚至可以用于打印0(否则将不创建前导零)。离开循环后,我们想清空堆栈并打印数字。S与的相反Z,因此它是一个开关,它将使非正能量的入射原子向右偏转90度。因此,原子实际上在S直线上从边缘移到,K从而弹出一个数字(请注意,~这确保了传入的原子具有能量-1)。该数字将递增48以获取相应数字字符的ASCII码。该A分裂数字打印一个副本!并将另一份副本反馈到Y反应堆中以获取下一位数字。所打印的副本将用作堆栈的下一个触发器(请注意,镜像还会将其发送到边缘周围,以M从左侧命中)。

当堆栈为空时,K会反射原子并将其能量转化为+1,使其直接穿过SN打印换行符(只是因为它很整洁:))。然后原子R'0再次通过原子到达原子的侧面Y。由于周围没有其他原子,因此它将永远不会释放,并且程序将终止。

计算裂变数:框架

让我们看一下程序的实际内容。该代码基本上是我的Mathematica参考实现的一部分:

fission[n_] := If[
  (div = 
    SelectFirst[
      Reverse@Divisors[2 n], 
      (OddQ@# == IntegerQ[n/#] 
       && n/# > (# - 1)/2) &
    ]
  ) == 1,
  1,
  1 + Total[fission /@ (Range@div + n/div - (div + 1)/2)]
]

其中div是最大分区中的整数数。

主要区别在于我们无法处理Fission中的半整数值,因此我正在做很多事情乘以2的事情,并且Fission中没有递归。要解决此问题,我将所有整数推送到队列中的分区中,以便稍后处理。对于我们处理的每个数字,我们将计数器增加1,一旦队列为空,我们将释放该计数器并将其发送出去进行打印。(队列的Q工作方式与完全相同K,只是按FIFO顺序。)

这是此概念的框架:

                      +--- input goes in here
                      v 

                     SQS ---> compute div from n          D /8/
                     ~4X               |                /~KSA /
                       3               +----------->    { +X
initial trigger ---> W                               6~@/ \/
                              4                   
                     W        ^                     /
                              |              3
                     ^     generate range    |
                     |     from n and div  <-+----- S6
                     |         -then-      
                     +---- release new trigger

最重要的新组件是数字。这些是传送器。具有相同数字的所有传送器属于同一个人。当原子击中任何传送器时,它将立即移动同一组中的下一个传送器,其中下一个按通常的从左到右,从上到下的顺序确定。这些不是必需的,但有助于布局(因此可以打一点高尔夫球)。还有X一个简单地复制一个原子,直接向前发送一个副本,向后发送另一个副本。

到目前为止,您也许可以自己整理出大部分框架。左上角的值队列仍待处理,并一次释放一个n。一个副本n被传送到底部,因为在计算范围时我们需要它,另一个副本进入顶部的块中进行计算div(这是代码中最大的单个部分)。一旦div已经计算出来,它被复制-一份递增右上角,这是存储在一个柜台K。另一份副本被传送到底部。如果div为was 1,我们将立即向上偏转它,并将其用作下一次迭代的触发器,而无需排队任何新值。否则,我们使用divn 在底部的部分中生成新的范围(即具有相应质量的原子流,随后将其放入队列中),然后在范围完成后释放新的触发器。

队列为空后,触发器将被反射,直接穿过S并再次出现在右上角,从那里释放计数器(最终结果)A,然后将其传送到itoavia 8

计算裂变数:环体

因此,剩下的就是计算div和生成范围的两个部分。计算div是这一部分:

 ;    
 {+L  /$     \/\/\/\/\/   5/ @  [~ &@[S\/ \
/A@[  %5                   /; &    K  } [S/
   \  A$@S  S\/  \/\/\/   \/>\ /S]@A  /  \ 
         X  X    /> \      +\ A\ /   \ /
    /   ~A\;     +;\      /@
ZX [K    / {/  / @  @ }  \ X @
   \AS   </      \V /    }SZS S/
     X   ;;@\   /;X  /> \ ; X X
     \@+  >/ }$S SZS\+;    //\V
       / \\  /\; X X @  @  \~K{
       \0X /     /~/V\V /   0W//
\        Z      [K \  //\
           \ /\7\A  /;7/\/

您现在可能已经看够了,可以耐心为自己解决这个问题。高层细分是这样的:前12列左右产生的除数流2n。接下来的10列会过滤掉那些不满足的列OddQ@# == IntegerQ[n/#]。接下来的8列过滤掉那些不满足的列n/# > (# - 1)/2)。最后,我们将所有有效除数推入堆栈,完成后,将整个堆栈清空到聚变反应堆中(覆盖除最后一个/最大除数之外的所有除数),然后释放结果,然后消除其能量(不是-从检查不等式为零)。

那里有很多疯狂的路径实际上并没有做任何事情。主要\/\/\/\/是顶部的疯狂(5s也是它的一部分)和围绕底部的一条路径(穿过7s)。我不得不添加这些以应对某些恶劣的比赛条件。裂变可能会使用延迟组件...

从生成新的范围的所述代码ndiv是这样的:

 /MJ $$\
4}K~@\ &]    @\  3/\
\{   }$A/1 2  }Y~K <\
 \@  /   \@<+@^   1;}++@
  2        ;   \    /

我们首先进行计算n/div - (div + 1)/2(两个条件下限,得出相同的结果)并存储以备后用。然后,我们生成一个从div下到下的范围1,并将存储的值添加到每个范围。

在这两种方法中,我应该提到两种新的常见模式:一种是“ 从下方” SX或“ ZX点击”(或旋转版本)。如果您想直接复制一个原子,这是复制原子的一种好方法(因为重定向聚变反应堆的输出有时很麻烦)。的SZ旋转原子入X,然后旋转镜像副本回传播的初始方向。

另一种模式是

[K
\A --> output

如果我们在其中存储任何值,K则可以通过K从顶部击中负能量来反复检索它。A重复项将复制我们感兴趣的值,然后将下次复制的内容立即发送回堆栈。

好吧,那真是个书呆子……但是,如果您真的了解了这一点,我希望您能想到Fissioni͝s̢̘̗̗͢i̟nç̮̩r̸̭̬̱͔e̟̹̟̜͟d̟̹̟̜͟i̠͙͎̖͓̯b̘̠͎̭̰̼l̶̪̙̮̥̮y̠̠͎̺͜ ͚̬̮f̟͞u̱̦̰͍n͍̜̠̙t̸̳̩̝o̫͉̙͠p̯̱̭͙̜͙͞ŕ̮͓̜o̢̙̣̭g̩̼̣̝r̤͍͔̘̟ͅa̪̜͇m̳̭͔̤̞ͅ ͕̺͉̫̀ͅi͜n̳̯̗̳͇̹。


1
Now with 100% fewer scrollbars.所以你说..,它仍然要继续...
优化器

13
我们的初级开发人员比大多数代码更具可读性。
corsiKa 2015年

作为Fission的创造者,甚至我还没有编写过这么大的程序!给我留下深刻的印象!您的解释非常壮观,可以肯定可以用作该语言的教程。
C0deH4cker 2015年

另外,答案的最后一行有点像裂变程序;)
C0deH4cker

12

CJam,42 41字节

ri]{_{:X,:)_few:+W%{1bX=}=}%{,(},e_}h]e_,

简单的广度优先遍历和空下一个水平的停止条件。

运作方式

ri]                                       e# This represents the root of the fissile tree
   {                               }h     e# Now we run a do-while loop
    _{                    }%              e# Copy the nodes at the current level and map them
                                          e# via this code block to get next level nodes
      :X,:)                               e# Store the node value in X and get array [1..X]
           _few                           e# Copy the array and get continuous slices of
                                          e# length 1 through X from the array [1..X]
               :+W%                       e# Right now, we have an array of array with each
                                          e# array containing slice of same length. We join
                                          e# those arrays and reverse them to get slices of
                                          e# higher length in front of lower lengths
                   {1bX=}=                e# Choose the first slice whose sum is same as X
                                          e# The reversal above makes sure that we give
                                          e# preference to slice of higher length in case of
                                          e# multiple slices add up to X
                            {,(},         e# Filter out slices of length 1 which basically
                                          e# mean that the current node cannot be split up
                                 e_       e# Join all slices in a single array. This is our
                                          e# next level in the Fissile tree. If this is empty
                                          e# it means that all no further node can be
                                          e# decomposed. In an {}h do-while loop, this fact
                                          e# itself becomes the stopping condition for the
                                          e# loop
                                     ]e_, e# Wrap all levels in an array. Flatten the array
                                          e# and take its length

在这里在线尝试


这大概可以打到35个字节左右。我正在尝试找出方法..
优化器

10

Python 3,112字节

def f(n,c=0):
 d=n-c;s=(n-d*~-d/2)/d
 return(s%1or s<1)and f(n,c+1)or+(d<2)or-~sum(f(int(s)+i)for i in range(d))

@FryAmTheEggman节省了4个字节。

解释会在后面...

奖励事实: 2的所有幂的裂变数为1。这是因为偶数长度序列的总和始终是两个中间数字的总和(奇数)乘以序列长度的一半(整数) 。奇数长度序列的总和是中间数乘以序列长度,即奇数。因此,由于2的幂没有奇数除数,因此只能将其表示为其自身的和。


2 + 3 + 4 + 5 = 14这并不奇怪。您将偶数长度序列的参数更改为“偶数长度序列的总和是两个中间数字的和,这是奇数,乘以长度的一半”。您的其余陈述不受影响。
布鲁诺·勒弗洛赫

@BrunoLeFloch谢谢!已更正。
randomra 2015年

您的标题不应该反映出这些改进吗?即<strike> 117 </ strike> <strike> 113 </ strike> 112
corsiKa

@corsiKa我通常只这样做以作重大改进。否则会有太多的罢工数字。
randomra 2015年

8

Python 2中,111个 102 97字节

有点可读:

def f(n,c=0):a=n-c;b=n-a*~-a/2;return 1/a or-~sum(map(f,range(b/a,b/a+a)))if b>b%a<1else f(n,c+1)

不太可读:

def f(n,a=0):b=n-a*~-a/2;return b>0and(f(n,a+1)or b%a<1and(1/a or-~sum(map(f,range(b/a,b/a+a)))))

两者均为97个字节。

bn减去(a-1)th三角数。如果是b % a == 0,则na从开始的连续数字的总和b


8
在加入PPCG之前,我曾经将Python视为一种通用的语言。
Alex A.

我认为您需要改善对可读性的定义。:P
Optimizer

Python 2不允许1else。仅第二种解决方案有效。直到Python 3 else可以立即跟随数字。
mbomb007

@ mbomb007据我所知,它工作正常,从2.7.8开始
SP3000

好的,我使用的是2.7.2。
mbomb007

7

蟒蛇2,86

f=lambda n,R={1}:n-sum(R)and f(n,R^{[min(R),max(R)+1][n>sum(R)]})or-~sum(map(f,R-{n}))

少打高尔夫球:

def f(n,R={1}):
    d=sum(R)-n
    if d==0:return (sum(map(f,R-{n}))
    if d<0:return f(n,R|{max(R)+1})
    if d>0:return f(n,R-{min(R)})

这个想法是测试相加为的连续整数的潜在游程n。运行直接直接存储为一组,R而不是通过其端点存储。

我们检查当前运行总和如何n通过它们的差与所需总和进行比较。

  • 如果总和太大,则会在运行中切出最小的元素。
  • 如果总和太小,我们通过将其最大值增大1来扩展行程。
  • 如果总和正确,则进行递归,映射f到运行,求和,然后为当前节点加1。如果运行为{n},我们已经尝试了所有非平凡的总和,n请先删除来停止递归。

感谢Sp3000节省3个字符。


7

Python 2,85

我对这个答案感到非常自豪,因为n = 9已经花费了数十秒,而n = 10则花费了5-10分钟。在代码高尔夫中,这被认为是程序的理想属性。

f=lambda n,a=1,d=1:a/n or[f(a)+f(n-a,1+1%d*a)+1/d,f(n,a+d/n,d%n+1)][2*n!=-~d*(2*a+d)]

还有一个短路版本,它不需要那么长时间并且使用相同数量的字节:

f=lambda n,a=1,d=1:a/n or~d*(2*a+d)+n*2and f(n,a+d/n,d%n+1)or f(a)+f(n-a,1+1%d*a)+1/d 

它可能会更快,但是一旦n略高于40,它至少会超过默认递归限制。

这个想法是用暴力搜寻数字ad使得a + a+1 + ... + a+d == n数值在1到nf(n,a+d/n,d%n+1)递归的分支遍历这些(a, d)对。在满足相等性的情况下,map(range(...))无论序列有多长,我都设法分成两个分支,从而避免了昂贵的通话。通过设置参数,可以将数字a+1直通d集中到一个调用中fa这样就不能使用另一种分割序列的方式。


这是如何运作的?
xnor 2015年

“我对这个答案感到非常自豪,因为对于n = 9,它已经花费了数十秒,对于n = 10,它已经花费了5-10分钟。在代码高尔夫中,这被认为是程序的理想属性。” 为此+1了。
Soham Chowdhury

6

Haskell,76 69字节

f x=head$[1+sum(map f[y..z])|y<-[1..x-1],z<-[y..x],sum[y..z]==x]++[1]

用法:

*Main> map f [1..40]
[1,1,3,1,5,6,5,1,6,7,12,10,12,11,12,1,8,16,14,17,18,18,23,13,21,18,22,23,24,19,14,1,22,20,23,24,31,27,25,26]

这个怎么运作:

[  [y..z] |y<-[1..x-1],z<-[y..x],sum[y..z]==x]

           make a list of lists with all consecutive integers (at least 2 elements)
           that sum up to x, sorted by lowest number, e.g. 9 -> [[2,3,4],[4,5]].

1+sum(map f[...]) 

           recursively calculate the Fission Number for each list

[...]++[1]

           always append the 1 to the list of Fission Numbers.

head

           take the first element, which is either the Fission Number of the
           longest list or if there's no list, the 1 appended in the step before.  

3

视网膜,66字节

^|$
,
(`,(1+?)(?=(?<1>1\1)+\D)
$0;
+)`,(1*);1\1
,$1,1$1;
^,|1

.
1

接受输入并以一元打印输出。

您可以将每一行放在一个文件中,也可以按原样使用该-s标志运行代码。例如:

> echo -n 1111111|retina -s fission
11111

说明:

  • 我们保留要分割的数字的逗号分隔列表。
  • 对于每个数字,我们采用最小的起始值,该值可以创建有效的拆分,并用分号将其与其余的分隔。
  • 如果数字内有分号,我们将其更改为逗号,并界定数字的下一个适当大小的部分(前一个元素的长度+ 1)。
  • 重复步骤2和3,直到发生更改为止。
  • 我们为每个叶子得到一个逗号,为每个内部节点得到一个分号,再加上一个额外的逗号,因为我们从两个逗号开始。因此,我们删除逗号和数字的部分(1),然后将其余部分转换为1

整个输入过程中字符串的状态11111111111111 (unary 14)

,11111111111111,
,11;111111111111,
,11,1;11,1111,11;111;,
,11,1,11;,1111,11,1;11;;,
,11,1,11;,1111,11,1,11;;;,
,,;,,,,;;;,
11111111111

非常感谢@MartinButtner提供的聊天帮助!


3

CJam(43字节)

qi,{):X),_m*{{_)*2/}/-X=}=~\,>0\{~$+}/)}%W=

在线演示

我敢肯定,我错过了一些技巧与先进的循环,但这并整齐地开拓CJam属性(先前已经惹恼了我),一个内部%地图结果还是栈上,并因此可以使用访问$带有负偏移量。


明天我会仔细看,但是一开始您不需要,一开始。/还有%一些其他隐式地将数字转换为范围。
马丁·恩德

,_m*可以替换为2m*。算术级数公式可以替换~,>:Y_,+:+~\,>0\ !Y。最后,如果使用{}#代替{}=,则不需要)after X。全部放在一起:ri{):X2m*{~,>:Y_,+:+X=}#!Y{~$+}/)}%W=
丹尼斯

2

Go,133个字节

func 算(n int)int{Σ:=1;for i:=0;i<n;i++{for j:=0;j<n;j++{if i*i+i-j*j-j==2*n{for k:=j+1;k<=i;k++{Σ+=算(k)};j,i=n,n}}};return Σ}

这是我第一次打高尔夫球,如果我犯了任何错误,对不起。

这使用了裂变“组成”也可以看作是两个有序整数序列之间的差异的想法。例如,将易裂变的“组成”作为数字13。它是6,7。但这可以看作是整数1 ... 7的总和减去整数1 ... 5的总和

  A: 1 2 3 4 5 6 7  sum = 28
  B: 1 2 3 4 5      sum = 15 
A-B:           6 7  sum = 13, which is also 28-15 = 13

回忆高斯上学日的公式,求和1 ... n =(n ^ 2 + n)/ 2。因此,要找到给定n的连续整数的组成,我们也可以说,我们正在沿着1 ... n范围搜索“端点” p和q,以便(p ^ 2 + p)/ 2-( q ^ 2 + q)/ 2 = n。在上面的示例中,我们将一直在搜索“端点” 5和7,因为7 ^ 2 + 7 = 56 / 2、5 ^ 2 + 5 = 30 / 2、56 / 2-30 / 2 = 28-15 = 13。

现在有多种可能的方法来裂变组成一个数字,如Martin指出的9 = 2 + 3 + 4以及4 +5。但是显然“最低”的启动顺序也将是最长的,因为它花费了更多的时间。小数字相加得出的数字要比中号数字大。(不幸的是我没有证据)

因此,要找到9的组成,请测试每个“端点对” p和q,分别将p和q从0迭代到9,并测试p ^ p + p / 2-q ^ 2 + q / 2 = 9.或者,更简单地说,将方程乘以2,以避免舍入的除法问题,并将所有数学保持为整数。然后我们寻找p和q使得(p ^ p + p)-(q ^ q + q)= 9 * 2。我们发现的第一个匹配项将是Fissile合成端点,因为如上所述,最低的一组数字也将是最长的,并且我们正在从低到高(0到9)进行搜索。一旦找到匹配项,我们就会跳出循环。

现在,递归函数找到给定n的那些“易裂终点” p和q,然后为从p到q的树中的每个“孩子”回忆自己。对于9,它将找到1和4(20-2 = 18),然后它将在2、3和4上重新调用自身,将结果相加。对于4之类的数字,它根本找不到匹配项,因此返回“ 1”。这可能是可以缩短的,但是就像我的第三个go程序一样,我也不是递归专家。

谢谢阅读。


干得好!但是为什么要使用Unicode函数/变量名称。那会浪费不必要的字节,而且您肯定可以使用一些普通的字母吗?
马丁·恩德

多谢您的留言。但是我问自己,为什么不算字符而不是字节呢:)
唐明亮

因为这些是此处的默认规则。:)我们通常计算字节数而不是字符数的原因是因为否则会发生这种情况,这对所有参与方都没有什么乐趣。;)(也就是说,任何挑战作者都可以自由指定按字符而不是字节进行计数,但我没有特别指定。)
Martin Ender

1

CJam,40 35 33字节

ri{__,f-_few:+{1b1$=}=|{F}*}:F~],

感谢@Optimizer的建议few,节省了2个字节。

CJam解释器中在线尝试。

这个怎么运作

ri      e# Read an integer from STDIN.
{       e# Define function F(X):
  _     e# Push X.
  _,    e# Push [0 ... X-1].
  f-    e# Subract each from X. Pushes Y := [X ... 1].
  _few  e# Push all overlapping slices of Y of length in Y.
  :+    e# Consolidate the slices of different lenghts in a single array.
  {     e# Find the first slice S such that...
    1b  e#   the sum of its elements...
    1$= e#   equals X.
  }=    e#   Since Y is in descending order, the first matching slice is also the longest.
  |     e# Set union with [X]. This adds X to the beginning of the S if S != [X].
  {F}*  e# Execute F for each element of S except the first (X).
}:F     e#
~       e# Execute F for the input.
],      e# Count the integers on the stack.

如果将我的上半部分与下半部分结合起来,则得到34:ri{_,:)_few:+W%{1b1$=}=|{F}*}:F~],
优化器

@Optimizer:很好。这样可以进行其他改进。谢谢!
丹尼斯
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.