11 =(1 + 2 + 3 + 4 + 5)-(1 + 2 + 3)+(6)-(4)


35

给定正整数N,您的任务是返回以下算法达到N所需的步数:

  1. 找到最小的三角形的数目Ť 我使得Ť 我  ≥Ñ。建立相应的列表L = [1,2,...,i]。

  2. 当L的项之和大于N时,从列表中删除第一个项。

  3. 如果L项的总和现在小于N,则增加i并将其附加到列表中。继续执行步骤2。

一旦达到N,我们就会停止。系统只会执行第一步。步骤#2和#3可能根本不处理。

例子

以下是N = 11的示例:

例

所以预期输出用于N = 11是4。

其他例子:

  • N = 5-我们从 T 3 = 1 + 2 + 3 = 6开始,然后是 2 + 3 = 5。预期输出: 2。
  • N = 10-因为 10是一个三角数,所以只需要第一步: T 4 = 1 + 2 + 3 + 4 = 10。预期输出: 1。

前100个值

下面是结果1≤N≤100:

  1,  2,  1,  4,  2,  1,  2, 10,  2,  1,  4,  2,  6,  2,  1, 22,  8,  2, 10,  2,
  1,  2, 12,  6,  2,  4,  2,  1, 16,  2, 18, 50,  2,  6,  2,  1, 22,  6,  2,  4,
 26,  2, 28,  2,  1,  8, 30, 16,  2,  6,  4,  2, 36,  2,  1,  2,  4, 12, 40,  2,
 42, 14,  2,108,  2,  1, 46,  2,  6,  4, 50,  2, 52, 18,  2,  4,  2,  1, 56, 12,
  2, 20, 60,  4,  2, 22, 10,  2, 66,  2,  1,  4, 10, 24,  2, 40, 72,  8,  2,  6

规则

  • 您可以编写完整的程序或函数,以打印或返回结果。
  • 您需要在不到一分钟的时间内用中档硬件处理所有N≤65536。
  • 如果有足够的时间,则您的程序/函数理论上应该可以在语言所固有支持的N的任何值下工作。如果不是,请在您的答案中说明原因。
  • 这是代码高尔夫球,所以最短答案以字节为单位!

有关。(我怀疑您已经知道这一点,只是为了后代而发布)
— ETHproductions

我们需要处理的N的最大值是多少?
— 路加福音

@Luke请参阅更新的规则。
— Arnauld

Answers:


4

果冻,29 31 字节

ÆDµ’H+Ṛ%1$ÐḟṂ
>TḢ_@Ç}‘Ḥ
R+\ðċȯç

返回结果的单子链接(N = 65536花费不到两秒钟)。

在线尝试!

怎么样?

有关该算法的详尽说明,请参阅Martin Ender的精彩文章。

ÆDµ’H+Ṛ%1$ÐḟṂ - Link 1, smallest natural number, M, that satisfies the below*, N
              - * N = T(M) - T(i) for some non-negative integer i <= M
ÆD            - divisors of N
  µ           - monadic chain separation, call that d
   ’          - increment d (vectorises)
    H         - halve (vectorises
      Ṛ       - reverse d
     +        - add (vectorises)
          Ðḟ  - filter discard if:
       %1$    -   modulo one is truthy (those which are the result of even divisors)
            Ṃ - minimum

>TḢ_@Ç}‘Ḥ - Link 2, evaluate result for non-triangular: list of T(1) to T(N), N
>         - T(i) > N
 T        - truthy indexes
  Ḣ       - head (yields the first i for which T(i) > N)
     Ç}   - call last link (1) as a monad converted to a dyad using the right argument
   _@     - subtract with reverse @rguments
       ‘  - increment
        Ḥ - double 

R+\ðċȯç - Main link: N
R       - range -> [1,2,...,N]
 +\     - reduce with addition -> [1,3,6,10,...T(N)]
   ð    - dyadic chain separation, call that t
    ċ   - count occurrences of N in t (1 if N is triangular else 0)
      ç - call last link (2) as a dyad(t, N)
     ȯ  - or

我为描述的算法创建的29字节全程序实现在笔记本电脑上花费4分钟30的时间(对于N = 65536),因此我认为它不算在内。

Ṁ‘ṭµS<³µ¿
ḊµS>³µ¿
0®Ḃ‘©¤ĿÐĿL’

对于每个步骤3使用while循环并将其作为步骤1重复使用,其长度与初始化列表可以管理的长度相等,因为对步骤3的任何检查都意味着建立一个列表,直到没有任何内容,然后找到该值的第一个索引:

ḊµS>³µ¿
Ṁ‘ṭ
Ḥ½_.ĊR®Ḃ‘©¤ĿÐĿS€i

25

Mathematica,79个字节

Min[2#/(d=Divisors@#~Cases~_?OddQ)+d]-2⌊(2#)^.5+.5⌋+⌈Sqrt[8#+1]~Mod~1⌉&

说明

我不愿意在挑战中实现算法,所以我想寻找解决方案的捷径。虽然我找到了一个,但不幸的是它没有超过实现该算法的Mathematica答案。就是说,我敢肯定这还不是最理想的选择,并且可能还有其他语言可以从这种方法或在此过程中获得的一些见识中受益。

所以我声称我们应该计算的顺序是:

f(n)= 2 *(A212652(n)-A002024(n))+ 1 + A023532(n-1)

或者,如果n是一个三角数,则为f(n)= 1,否则为f(n)= 2 *(A212652(n)-A002024(n)+1)。

在第一个表达式中,A023532仅对这两种不同的情况进行编码。其他两个序列(加1)是将n最长分解为连续整数(k-i + 1)+(k-i + 2)+ ... + k = n时最大的整数k与最大整数j,使1 + 2 + ... + j <n。

在一定程度上更简单的话,这里是我们如何找到非三角形数字的答案:第一,找到最大的三角形数量牛逼Ĵ这是少比ñ。那么j是在第1步中添加的倒数第二个整数(因为添加j + 1之后,我们将超过n)。然后将n分解为尽可能多(或尽可能小的)连续整数,并在这些数字k中调用最大值。结果只是2 *(kj)。直观的原因是,分解的最大值每隔一步增加1,而当达到ķ。

我们需要展示四件事来证明这可行:

  1. 对于三角形,f(n)= 1。这很简单,因为第一步只是迭代所有三角数。如果在此过程中恰好达到n,就完成了,仅需一步即可计算。
  2. 对于所有其他数字,我们总是在删除步骤之后结束,而不是在插入步骤之后结束。这意味着所有其他f(n)都是偶数。
  3. 在第一个插入步骤之后的每个插入步骤中,我们仅添加一个数字。这保证了我们将在kj对步骤之后达到包括k的分解。
  4. 我们获得的n的最终分解始终是n分解为连续整数的最长可能分解,换句话说,它始终是总和中n最小的分解。换句话说,我们加和的最后一个数字始终是A212652(n)。

我们已经说明了为什么(1)是正确的。接下来,我们证明除了初始步骤(对于非三角数不会发生)以外的其他步骤,我们无法结束。

假设我们在插入步骤结束,在将和值p相加后达到n。这意味着在此插入步骤之前,该值为np(如果一次添加多个值,则为np 或更小)。但是此插入步骤之前是删除步骤(因为在步骤1中我们不能命中n)。由于算法的工作方式,我们在此删除步骤中删除的最后一个值q必然小于p。但是,这意味着我们取出之前q我们有N-P + Q(或更小),这是小于Ñ。但这是一个矛盾,因为我们在碰到n-p + q时不得不停止删除整数,而不是删除另一个q。这证明了上面的要点(2)。因此,现在我们知道我们总是在删除步骤上结束,因此所有非三角数均具有偶数输出。

接下来我们证明(3),每个插入步骤只能插入一个值。这本质上是(2)的推论。我们已经证明,在添加一个值之后,我们不能精确地达到n,并且由于证明使用的是不等式,所以我们也不能最终低于n(因为n-p + q仍小于n,因此我们不应该删除n那么多的价值)。因此,无论何时添加单个值,都可以保证超过n,因为我们通过删除较小的值使其低于n。因此,我们知道总和的上限每隔一步增加1。我们知道这上端的初始值(它是最小的米,使得T m > n)。现在,我们只需要弄清楚这个上限,即可达到最终金额。那么,步数就是差值的两倍(加1)。

为此,我们证明(4),最终的总和始终是n分解为尽可能多的整数,或者分解中的最大值最小的分解(即,它是最早的分解)。我们将再次通过矛盾来做到这一点(这一部分的措词可能会更严格一些,但是我已经花了太多时间在这上面了……)。

说的最早/最长可能分解ñ一些A +(A + 1)+ ...(B-1)+ B,A≤b ,并说该算法跳过它。这意味着在添加b时,a不再必须是总和的一部分。如果a是总和s的一部分,那么那一刻我们将有n≤s。因此,或者总和仅包含从a到b的值,等于n,然后我们停止(因此我们没有跳过此分解),或者总和中至少有一个比a小的值,在这种情况下,n <s并且该值将被删除,直到我们达到确切的总和(再次,不会跳过分解)。因此,我们必须摆脱的一个前加入b。但这意味着我们必须达到这样的情况,其中a是总和的最小组成部分,而最大的还不是b。但是,此时我们无法删除a,因为总和明显小于n(因为b缺失),因此我们需要先添加值,直到我们添加b并精确地命中n为止。这证明(4)。

因此,将这些内容放在一起:我们知道第一对步骤为我们提供了A002024(n)的最大值。我们知道最终分解的最大值是A212652(n)。而且我们知道,此最大值每两步增加一次。因此,最终表达式为2 *(A212652(n)-A002024(n)+1)。该公式几乎适用于三角数,只是对于那些三角数,我们只需要1步而不是2步,这就是为什么我们用三角数的指示函数(或其倒数,更方便)校正结果。

最后,至于执行。对于前一个序列,我使用的是OEIS中的公式MIN(奇数d | n; n / d +(d-1)/ 2)。事实证明,如果我们将因子2放入此表达式以获取MIN(odd d | n; 2n / d + d-1),则会节省一些字节,因为在我的第一个版本中-1随+1取消的F(n)的直接编码两种情况三角形和非三角形的数字。在代码中,这是:

Min[2#/(d=Divisors@#~Cases~_?OddQ)+d]

对于后一个序列(1, 2, 2, 3, 3, 3, ...),我们可以使用简单的封闭形式:

⌊(2#)^.5+.5⌋

最后,每当8n + 1是一个完美的正方形时,三角数的反指示函数为0。这可以在Mathematica中表示为

⌈Sqrt[8#+1]~Mod~1⌉

有很多方法可以表示这最后两个序列,并在它们之间移动一定的偏移量,因此,我敢肯定这不是最佳的实现,但是我希望这可以为其他人提供一个起点,以探索新的方法。他们自己的语言。

自从我解决了所有这些麻烦之后,这是一个序列图,最高可达n = 1000(我也可以在几秒钟内计算出100k,但实际上并没有显示任何其他见解):

在此处输入图片说明

研究那些非常直线的变化可能很有趣,但我将其留给其他人...


我终于花时间彻底阅读了您的答案。太好了 请注意,算法中已经假设了(3)(步骤#3是if,而不是一阵子),但是当然值得证明。
— Arnauld

@Arnauld谢谢。:)我一定已经忽略/误解了if / while部分。好东西没什么了。
— 马丁·恩德

7

Mathematica,72个字节

(For[l=u=c=k=0,k!=#,c++,If[#>k,While[#>k,k+=++u],While[#<k,k-=l++]]];c)&

纯函数采用整数参数。

怎么运行的

For[ ... ]

一个For循环。

l=u=c=k=0

初始化; 将l(下),u(上),c(计数器)和k(和)设置为0。

k!=#

条件; 重复while k不等于输入。

c++

增量; 增加计数器c。

If[#>k,For[,#>k,,k+=++u],For[,#<k,,k-=l++]]

身体

If[#>k, ... ]

如果输入大于k:

While[#>k,k+=++u]

当输入大于k,增量u和增量k的u。

如果输入不大于k:

While[#<k,k-=l++]

当输入小于k,减量k通过l和增量l。

( ... ;c)

c循环后返回。


1
For[,...]节拍While[...]。
— 马丁·恩德

5

Python 2,104个字节

N=input();i=s=0;l=()
while N!=sum(l):exec'while sum(l)'+['<N:i+=1;l+=i,','>N:l=l[1:]'][s%2];s+=1
print s

在线尝试!

在将术语添加到列表的末尾与从列表的开头除去术语之间进行切换。


5

Haskell,70 63 68 64字节

编辑:

  • -7个字节:通过消除的意义来消除空格,两个符号和一些括号a。修复了解释中的一一错误。
  • +5个字节:Argh,完全错过了65536的要求,事实证明(1)2的幂特别昂贵,因为只有在您到达数字本身(2)时它们才会被击中,所以对长距离求和(环绕)零)。用数学公式代替了总和。
  • -4个字节:调整a并b线性地获得要删除的求和公式中的项。

1#1 是获取并返回整数的匿名函数。

用作(1#1) 100。

1#1
(a#b)n|s<-a*a-b*b=sum$[a#(b+2)$n|s>8*n]++[(b#a)(-n)+1|s<8*n]

在线尝试!

怎么运行的

  • (a#b)n表示当前计算步骤。a, b是中的数字1, 3, 5, ..,而n根据步长可以是正数或负数。
    • 在步骤1或3中,它表示列表[(a+1)/2,(a+3)/2..(b-1)/2]和目标编号-n。
    • 在步骤2中,它表示列表[(b+1)/2,(b+3)/2..(a-1)/2]和目标编号n。
  • a, b和列表之间的奇异对应是为了能够用短表达式求和s=a*a-b*b。
    • 在步骤1和3中,这与相同s= -8*sum[(a+1)/2..(b-1)/2]。
    • 在步骤2中,此操作与相同s=8*sum[(b+1)/2..(a-1)/2]。
  • 分支是通过使列表理解仅在一种情况下生成元素,然后对结果求和来完成的。
    • 如果为s>8*n,则b在递归之前增加2。
      • 在步骤1和3中,这会增加列表,而在步骤2中,这会缩小列表。
    • 如果为s<8*n,则递归通过交换a和b,以及来改变步长n,并将1添加到结果中。
    • 如果为s==8*n,则两个列表推导中的任何一个都不给出任何元素,因此总和为0。
  • (1#1) n表示开始之前的虚拟“阶段2”,该虚拟阶段立即更改为步骤1,从中构建列表[1..0]=[]。

4

PHP> = 7.0,74字节

while($i=$r<=>$argn)for($s++;($r<=>$argn)==$i;)$r+=$i+1?-++$y:++$x;echo$s;

使用飞船操作员

在线尝试!

展开式

while($i=$r<=>$argn) # if input is not equal sum of array
  for($s++;  # raise count steps
  ($r<=>$argn)==$i;)
  # so long as value compare to input has not change to lower/higher to higher/lower or equal  
    $r+=$i+1
      ?-++$y # if $i was higher remove the first integer
      :++$x;} # if $i was lower add the next highest integer     
echo$s; # Output steps

什么$argn啊
— chx

@chx一个变量,当您从命令行使用-R选项php.php
— net / manual /

哇。我从来没有听说过的-R要少得多argv或argi。我当然知道argc和argv。非常有趣,谢谢。
— chx

4

C,94 91字节

在线尝试

c;s;m;M;f(n){while(s-n){while(s<n)s+=++M;c++;if(s==n)break;while(s>n)s-=++m;c++;}return c;}

大量使用未初始化的变量oO
— YSC

C中的@YSC,未初始化的全局声明的整数在编译时设置为零。阅读更多
— Khaled.K

忘了这个 感谢您的提醒。
— YSC

仅供参考,我发布了另一个C答案。我使用的技巧中至少有一个不能与其他编译器一起使用(缺少的return),但是对于那些有用的技巧,请随时将其合并到您的答案中。
— 高清

3

JavaScript(ES6),82个字节

D=(o,a=0,b=1,d=1,c=0)=>a<o?D(o,a+=b,b+1,d,c+(a>=o)):a>o?D(o,a-=d,b,d+1,c+(a<=o)):c

测试片段


抱歉,每个ↄ计为3个字节。我想这没关系,因为它很容易重命名。
— 与Orjan约翰森

@ØrjanJohansen感谢您的提醒。我真的应该记得按字节而不是按长度对代码评分。我认为对于“普通可重命名的[变量]”尚无社区共识,因此我编辑了该帖子。那好吧。
— 卡普

3
代码段在6553上显示“递归过多”失败。6553在本地节点(6.9.1)中工作,但在65536上不工作(“超出最大调用堆栈大小”)。
— eush77

3

直流电,61字节

dsN[ddd9k4*d2*1+dv-0k2/-d1+*2/-d[q]s.0=.-lN-0lN-sNlFx]dsFxz2-

在线尝试!

说明

主要的递归宏:

ddd9k4*d2*1+dv-0k2/-d1+*2/-d[q]s.0=.-lN-0lN-sNlFx
   9k4*d2*1+dv-0k2/-                              # Compute triangular root
                    d1+*2/                        # Compute triangular number
  d                       -d[q]s.0=.              # Check if sum is exact
 d                                  -lN-          # Compute S-N or S+N
                                        0lN-sN    # Update N := -N
d                                             lFx # Leave the trail and recurse

这个宏:

  1. 查找超过堆栈上当前数量的最小三角形数(使用修改后的三角形根公式)。
  2. 检查三角和是否S正好代表当前数字。如果退出,则退出。
  3. 使用S+N(过度逼近)或S-N(接近逼近)进入步骤1 ,选择在迭代之间交替。

当它确实退出时,堆栈上的尾迹告诉主程序它进行了多少次迭代。


3

Python 3中,150个 138字节

n=int(input())
S=sum
l=[1]
i=s=1
while S(l)<n:i+=1;l+=[i]
while S(l)!=n:
 while S(l)>n:l.pop(0)
 s+=1
 if S(l)<n:i+=1;l+=[i];s+=1
print(s)

变更日志:

  • 将追加更改为+ =,删除其他(感谢musicman523,Loovjo; -12个字节)

1
步骤#2可以一次从列表中删除一个或几个项(例如对于N = 11,在示例中为1、2和3),但是无论哪种方式都被视为一个步骤。
— Arnauld

@Arnauld忽略了这一点;固定。
— L3viathan '17

1
您能解释为什么else必须这样做吗?我相信else每次都会运行,因为循环总是会正常终止(不带break),并且没有循环它似乎可以正常工作。
— musicman523

您可以跳过该A=l.append部分而l+=[x]改为使用。
— Loovjo

3

批处理,126字节

@echo off
set/an=s=l=u=0
:l
if %s% lss %1 set/as+=u+=1,n+=!!l&goto l
if %s% gtr %1 set/as-=l+=1&goto l
cmd/cset/an+n+2-!l

说明:l如果从未执行过步骤2,则为零。这允许n跟踪步骤3的迭代次数。由于算法永远不会在步骤3处停止,因此它必须已运行步骤1一次,并且步骤2已运行n+1了总共n+n+2步骤。但是,如果参数是三角数,则步骤2永远不会执行,因此我们需要减去一个步骤。


3

Python 2,86 81字节

n=input()
l=u=i=s=0
while n:k=n>0;i+=k^s;s=k;l+=k;n-=l*k;u+=k^1;n+=u*-~-k
print i

在线尝试!

在TIO上计算65536测试用例0.183s。


这个84字节的递归版本无法计算所有值,直到65536:

def f(n,l=[0],m=1):k=n>sum(l);return n==sum(l)or f(n,[l[1:],l+[l[-1]+1]][k],k)+(m^k)

在线尝试!


2

Mathematica,92个字节

(For[q=a=b=0;t={},t~AppendTo~q;q!=#,If[q<#,q+=++b,q-=++a]];Length@Split@Sign@Differences@t)&

接受整数参数并返回整数的纯函数。

变量a和b代表正在考虑的总和中(不断变化的)开始和结束数字,而q代表运行总计(从a+1到的数字b);t跟踪q到目前为止遇到的所有值。初始化这些变量后,For循环继续执行If[q<#,q+=++b,q-=++a],这会按照规范的要求在末尾添加一个新数字,或在最前面减去该数字,直到q等于输入为止。

现在,我们只需要从中提取步数,就可以找到沿途遇到t的q值。例如,当输入为时11,For循环以tequals 退出{0,1,3,6,10,15,14,12,9,15,11}。我发现计算此步数的最好方法是计算差异从上升到下降的次数。这就是冗长命令的Length@Split@Sign@Differences@t功能,但是我怀疑可以改进。


2

C(tcc),71个字节(61 + 10)

命令行参数(包括空格):

-Dw=while

资源:

c,m,M,s;f(n){w(++c,s-n){w(c&s<n)s+=++M;w(~c&s>n)s-=m++;}--c;}

怎么运行的:

c计算步数。m并M存储范围的最小值和最大值,s即和。最初,它们全为零。

不断c增加,并与s进行比较n。只要它们不相等:

  • 如果c是奇数,则只要s<n增加:,添加的整数范围的端部M由一个,并s通过M。

  • 如果c是偶数,则只要s>n从范围的开始删除一个整数:减少s通过m,并增加m一。

当循环退出时,c已经增加了太多次。递减可得出正确的结果,并且恰好在正确的寄存器中计算得出它作为返回值。

有趣的是,它使用与Khaled.K的C答案完全相同的变量名。它们不会被复制。


1

Perl 6、114字节

{((0,0,1),->(\a,\b,\c){b,(a..*).first(->\d{(d,b).minmax.sum*c>=$_*c}),-c}...->(\a,\b,\c){(a,b).minmax.sum==$_})-1}

(受早期Haskell实现的启发)

尝试一下
在我的计算机上,它可以在65秒内输入65536来运行,但是我无法通过TIO.run在60秒内运行它。
我有Rakudo v2017.04 +,那里有v2017.01。
Rakudo / NQP / MoarVM几乎每天都会进行优化,因此在过渡期间可能需要进行任何数量的优化才能及时投入使用。


展开式

{
  (

    # generate a sequence

    (0,0,1),           # initial value 

    -> (\a,\b,\c) {
      b,               # swap the first two values

      (a..*)
      .first(          # find the first number that brings us to or past the input

        -> \d {
          (d,b).minmax # get a Range object regardless of which is larger
          .sum * c     # sum it, and negate it every other time

          >=           # is it equal to or greater than

          $_ * c       # negate the original input every other time
        }

      ),

      -c               # invert for next round
    }

    ...                # keep doing that until

    -> (\a,\b,\c) {
     (a,b).minmax.sum == $_ # it finally reaches the input
    }

  ) - 1 # count the number of elements in the sequence
        # and subtract one for the initializer
}

请注意,Rakudo已针对进行了优化,Range.sum因此不必迭代所有值。

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.