Q,47字节
m:{*/1_-':|(0<){y-x x bin y}[*+60(|+\)\1 0]\x}
测试
+(i;m'i:1 2 3 4 5 6 7 8 9 42 1000 12345)
将其成对读取(i,map(m,i)),其中m是计算函数,而i是不同的args
写
1 1
2 2
3 3
4 3
5 5
6 5
7 10
8 8
9 8
42 272
1000 12831
12345 138481852236
说明
n funtion\arg
n次应用function(function(function(...(function(args))))(内部使用tal递归),并返回结果序列。我们将fibonnaci系列的60个第一项计算为*+60(|+\)\1 0
。在这种情况下,函数为( | +):加在序列上的+ \会计算部分和(ex + \ 1 2 3为1 3 6),|反转seq。因此,每次“迭代”时,我们都会计算前两个斐波那契数的部分和,并返回部分求和后求和,60(|+\)\1 0
生成序列1 0,1 1,2 1,3 *+
2,5 3,8 5,13 8,21 13,... 应用于结果翻转(置位)并取第一个,结果为序列1 1 2 3 5 8 13 21 34 55 ..
(cond)function\args
在cond true时应用function(function(.. function(args))),并返回部分结果的序列
function[arg]
在一个以上参数的函数上应用会创建一个投影(部分应用)
我们可以给args命名,但是隐式名称是x,y,z
{y-x x bin y}[*+60(|+\)\1 0]
声明带有部分投影的args x,y的lambda(arg x是斐波那契数列,计算为* + 60(| +)\ 1 0)。x代表斐波那契值,y代表要处理的数字。二进制搜索(bin)用于定位更大的斐波那契数<= y(x bin y
)的索引,并减去x的对应值。
要从部分结果计算乘积,我们将它们反转并计算每对(-':|
)的差,删除第一个(1_
因为是0)并乘以(*/
)。
如果我们对累计和感兴趣,则代码是相同的,但是用+/
代替*/
。我们还可以使用任何其他双向运算符代替+或*
关于执行效率
我知道在这场比赛中效率不是问题。但是在这个问题上,我们可以从线性成本到指数成本不等,所以我对此感到很好奇。
我开发了第二个版本(不包括注释,长度为48字节),并在两个版本上重复测试了1000次电池。
f:*+60(|+\)\1 0;m:{*/1_-':|(0<){x-f f bin x}\x} /new version
执行时间是:原始版本0'212段,新版本0'037段
原始版本每个功能应用程序一次计算fibbonaci序列;新版本只计算斐波那契。
在这两种情况下,斐波那契数列的计算均使用尾递归
2
可以分解为-1 + 3
。Zeckendorf定理的正确说法是,正斐波那契数可以唯一分解为具有正指数的非连续斐波那契数之和。