创建找到非负整数的阶乘的最短程序或函数。
用表示的阶乘!
定义为
用简单的英语来说,阶乘0为1,阶乘n为n,n大于0时是阶乘n小于n的阶乘。
您的代码应使用标准方法执行输入和输出。
要求:
- 不使用任何可以计算阶乘的内置库(包括的任何形式
eval
) - 最多可以计算125的阶乘
- 可以计算数字0的阶乘(等于1)
- 一分钟内完成,数字最多125
提交时间最短者获胜,如果平局,则以当时票数最多的答案获胜。
创建找到非负整数的阶乘的最短程序或函数。
用表示的阶乘!
定义为
用简单的英语来说,阶乘0为1,阶乘n为n,n大于0时是阶乘n小于n的阶乘。
您的代码应使用标准方法执行输入和输出。
要求:
eval
)提交时间最短者获胜,如果平局,则以当时票数最多的答案获胜。
Answers:
{,1\{)*}/}:f
这是为那些试图学习高尔夫球手的人准备的。前提条件是对golfscript有基本的了解,并具有阅读golfscript文档的能力。
因此,我们想尝试一下我们的新工具golfscript。从简单的事情开始总是好的,所以我们从阶乘开始。这是基于简单的命令式伪代码的初步尝试:
# pseudocode: f(n){c=1;while(n>1){c*=n;n--};return c}
{:n;1:c;{n 1>}{n c*:c;n 1-:n;}while c}:f
在golfscript中很少使用空格。摆脱空白的最简单技巧是使用不同的变量名。每个令牌都可以用作变量(请参见语法页)。有用的标记使用的变量是特殊字符,例如|
,&
,?
-通常在代码的其他地方使用任何没有。这些始终被解析为单个字符标记。相反,像这样的变量n
将需要一个空格才能将数字压入堆栈。数字本质上是预初始化的变量。
与往常一样,在不影响最终结果的情况下,将有一些可以更改的语句。在golfscript,一切都计算为true除0
,[]
,""
,和{}
(见本)。在这里,我们可以将循环退出条件更改为简单{n}
(循环附加时间,并在n = 0时终止)。
与任何语言打高尔夫球一样,它有助于了解可用功能。幸运的是,这份清单对于golfscript很简短。我们可以更改1-
为(
保存另一个字符。目前,代码如下所示:(如果需要,我们可以使用1
而不是|
此处,这将删除初始化。)
{:n;1:|;{n}{n|*:|;n(:n;}while|}:f
重要的是,充分使用堆栈以获得最短的解决方案(实践练习)。通常,如果仅在一小段代码中使用值,则可能没有必要将它们存储到变量中。通过删除运行的产品变量并仅使用堆栈,我们可以节省很多字符。
{:n;1{n}{n*n(:n;}while}:f
这是其他要考虑的问题。我们要n
在循环体末尾从堆栈中删除变量,然后在之后立即将其推入。实际上,在循环开始之前,我们还将其从堆栈中删除。相反,我们应该将其保留在堆栈上,并且可以将循环条件保留为空白。
{1\:n{}{n*n(:n}while}:f
也许我们甚至可以完全消除该变量。为此,我们需要始终将变量保留在堆栈中。这意味着在条件检查结束时,我们需要在堆栈上存储变量的两个副本,因此在检查之后我们不会丢失它。这意味着0
在循环结束后,我们将在堆栈上有一个冗余,但这很容易解决。
这使我们找到了最佳的while
回路解决方案!
{1\{.}{.@*\(}while;}:f
现在我们仍然想使它更短。明显的目标应该是单词while
。查看文档,有两种可行的选择- 展开并执行。当您选择不同的路线时,请尝试权衡两者的好处。展开“几乎是一个while循环”,因此我们估计会将5个字符while
减少4个/
。至于do
,我们减少while
了3个字符,并合并了两个块,这可能会节省一个或两个字符。
使用do
循环实际上有一个很大的缺点。由于条件检查是在主体执行一次之后0
进行的,因此的值将是错误的,因此我们可能需要if语句。现在,我告诉您展开的时间更短(do
最后提供了一些解决方案)。继续尝试,我们已经拥有的代码需要最少的更改。
{1\{}{.@*\(}/;}:f
大!我们的解决方案现在很短,我们在这里完成了,对吧?不。这是17个字符,而J有12个字符。永远不要承认失败!
使用递归意味着我们必须使用分支结构。不幸的是,由于阶乘可以如此简洁地递归表示,这似乎是迭代的可行替代方案。
# pseudocode: f(n){return n==0?n*f(n-1):1}
{:n{n.(f*}1if}:f # taking advantage of the tokeniser
嗯,这很容易-如果我们较早尝试过递归,则可能甚至没有考虑使用while
循环!不过,我们只有16个字符。
数组通常以两种方式创建-使用[
和]
字符,或者使用,
函数。如果在堆栈顶部使用整数执行,,
返回该长度的数组,其中arr [i] = i。
为了遍历数组,我们有三个选择:
{block}/
:推,挡,推,挡,...{block}%
:[push,block,push,block,...](这有些细微差别,例如,在每次push之前从堆栈中删除中间值){block}*
:推,推,挡,推,挡,...golfscript文档提供了一个{+}*
用于对数组内容求和的示例。这表明我们可以{*}*
用来获取数组的乘积。
{,{*}*}:f
不幸的是,它并不是那么简单。所有元素都以一([0 1 2]
而不是[1 2 3]
)分开。我们可以{)}%
用来纠正此问题。
{,{)}%{*}*}:f
好吧,不完全是。这不能正确处理零。我们可以计算(n + 1)!/(n + 1)来纠正此问题,尽管这会花费太多。
{).,{)}%{*}*\/}:f
我们还可以尝试在与n = 1相同的存储桶中处理n = 0。这实际上是非常短暂的事情,请尝试并尽力而为。
排序不太好,只能使用7个字符:
[1\]$1=
。请注意,这种排序技术确实具有有用的目的,例如在数字上加边界(例如`[0 \ 100] $ 1 =)。
这是赢家,只有3个字符:。!+
如果要在同一块中具有增量和乘法,则应遍历数组中的每个元素。由于我们不是在构建数组,所以这意味着我们应该使用{)*}/
,这使我们可以进行最简单的factorial golfscript实现!长度为12个字符时,该字符与J关联!
{,1\{)*}/}:f
从一个简单if
的do
循环解决方案开始:
{.{1\{.@*\(.}do;}{)}if}:f
我们可以从中挤出更多。有点复杂,所以您必须说服自己这些工作。确保您了解所有这些。
{1\.!!{{.@*\(.}do}*+}:f
{.!{1\{.@*\(.}do}or+}:f
{.{1\{.@*\(.}do}1if+}:f
更好的替代方法是计算(n + 1)!/(n + 1),这消除了对if
结构的需要。
{).1\{.@*\(.}do;\/}:f
但是,do
这里最短的解决方案需要几个字符才能将0映射到1,并将其他所有内容映射到自身-因此我们不需要任何分支。这种优化非常容易错过。
{.!+1\{.@*\(.}do;}:f
对于任何有兴趣的人,这里提供了一些与上述长度相同的替代递归解决方案:
{.!{.)f*0}or+}:f
{.{.)f*0}1if+}:f
{.{.(f*}{)}if}:f
*注意:我实际上尚未测试本文中的许多代码段,因此请随时告知是否存在错误。
f n=product[1..n]
[1..0] ==> []
和product [] ==> 1
f 0=1;f n=n*f$n-1
也是17个字符。
product
((*)
或说(-)
“可以计算阶乘”)都通过Prelude进行定义。为什么一个很酷而不是另一个呢?
×/∘⍳
用作匿名函数:
×/∘⍳ 5
120
如果要命名,请使用6个字符:
f←×/∘⍳
⍳
创建一个索引向量,即⍳5
is 1 2 3 4 5
。×
是(显然)乘,/
是减少,∘
是函数组成。因此,×/∘⍳
是一个带有参数x
并给出数字乘积的函数[1..x]
。
J中的标准定义:
f=:*/@:>:@i.
125秒不到1秒!
例如:
f 0
1
f 5
120
f 125x
1882677176888926099743767702491600857595403
6487149242588759823150835315633161359886688
2932889495923133646405445930057740630161919
3413805978188834575585470555243263755650071
31770880000000000000000000000000000000
([:*/1+i.)
10点,甚至8点一样,括号仅在调用函数时需要,而在定义时不需要。
f 125x
该x
怎么办?这是一种特殊的数字吗?
定义功能 !
{),()\{*}/}:! # happy robot version \{*}/
备用13个字符的版本
{),()+{*}*}:!
整个程序版本为10个字符
~),()+{*}*
测试用例不到1/10秒:
输入:
0!
输出
1
输入
125!
输出
188267717688892609974376770249160085759540364871492425887598231508353156331613598866882932889495923133646405445930057740630161919341380597818883457558547055524326375565007131770880000000000000000000000000000000
$f={[*]1..$_}
[*]
与Haskell相同product
,并且1..$_
是从1到$_
参数的递增计数。
[*]
(“连续两个术语”错误消息)。
:p
解释:
: % generate list 1,2,3,...,i, where i is an implicit input
p % calculate the product of of all the list entries (works on an empty list too)
f=->n{n>1?n*f[n-1]:1}
irb(main):009:0> f=->n{n>1?n*f[n-1]:1}
=> #<Proc:0x25a6d48@(irb):9 (lambda)>
irb(main):010:0> f[125]
=> 18826771768889260997437677024916008575954036487149242588759823150835315633161
35988668829328894959231336464054459300577406301619193413805978188834575585470555
24326375565007131770880000000000000000000000000000000
/f{1 exch -1 1{mul}for}def
例:
GS> 0 f =
1
GS> 1 f =
1
GS> 8 f =
40320
该函数本身仅需21个字符;其余的是将其绑定到变量。要保存一个字节,还可以将其绑定到一个数字,如下所示:
GS> 0{1 exch -1 1{mul}for}def
GS> 8 0 load exec =
40320
1.#INF
。(我使用了为x64 Windows编译的通用GNU Ghostscript 9.0.7。)
function f(n)!n||n*f(n-1)
f=(n)->!n||n*f(n-1)
true
在n = 0的情况下返回,但是JavaScript仍会将其强制键入1。
return
在JavaScript函数中不需要语句吗?
return
!但是为什么不呢?
function f(n)n?n*f(--n):1
f=n=>!n||n*f(n-1)
拿,CoffeeScript!
def f(n)(1..n).inject 1,:*end
测试
f(0) -> 1
f(5) -> 120
end
直接在后面加上:*
换行符或分号。
(1..10).inject :*
#=> 3628800
f(0)
呢?
f=->n{(1..n).inject 1,:*}
。用调用f[n]
。
作为传统的实例方法(39个字符;在此进行了测试):
double f(int x){return 2>x?1:x*f(x-1);}
作为lambda表达式(20个字符,但请参见免责声明;已在此处测试):
f=x=>2>x?1:x*f(x-1);
我们必须使用,double
因为125!== 1.88 * 10209,远高于ulong.MaxValue
。
如果使用C#lambda进行递归,则显然必须将lambda存储在一个命名变量中,以便它可以调用自身。但是与(例如)JavaScript不同,自引用lambda必须已声明并初始化在上一行。您不能在声明和/或初始化变量的同一条语句中调用该函数。
换句话说,这不起作用:
Func<int,double> f=x=>2>x?1:x*f(x-1); //Error: Use of unassigned local variable 'f'
但这确实是:
Func<int,double> f=null;
f=x=>2>x?1:x*f(x-1);
没有足够的理由进行此限制,因为f
在运行时永远无法取消分配。的必要性Func<int,double> f=null;
行是C#的怪癖。这是否使在字符计数中忽略它是否公平取决于读者。
f=(x)->+!x||x*f x-1
在这里测试: http : //jsfiddle.net/0xjdm971/
通过设定范围并乘以
-1字节的坦克给ovs的想法是使用max()函数
;1⌉⟦₁×
;1 -- If n<1, use n=1 instead (zero case)
⟦₁ -- Construct the range [1,n]
× -- return the product of said range
递归
≤1|-₁↰;?×
--f(n):
≤1 -- if n ≤ 1: return 1
| -- else:
-₁↰ -- f(n-1)
;?× -- *n
;
代替代替,
仅允许常规的数字输入。-1byte无论如何
double f(int n){return n<2?1:n*f(n-1);}
double f(n){return!n?1:n*f(n-1);}
-33个字符。
f(125)
将会溢出
T f(T)(T n){return n < 2 ? 1 : n * f(n - 1);}
更清晰:
T f(T)(T n)
{
return n < 2 ? 1 : n * f(n - 1);
}
较凉的(虽然是更长的版本)是模板化的,它在编译时就全部完成了(64个字符):
template F(int n){static if(n<2)enum F=1;else enum F=n*F!(n-1);}
更清晰:
template F(int n)
{
static if(n < 2)
enum F = 1;
else
enum F = n * F!(n - 1);
}
尽管同名模板非常冗长,所以您不能在代码编写中很好地使用它们。D在字符数方面已经足够冗长,以至于无法进行代码高尔夫(尽管实际上在减小较大程序的整体程序大小方面确实做得很好)。尽管这是我最喜欢的语言,所以我认为我不妨尝试看看如何在代码高尔夫上做到这一点,即使喜欢GolfScript之类的人也一定会喜欢它。
f=n=>n?n*f(n-1):1
ES6:
(使用过滤器而非函数保存2个字符)
filter f($x){if(!$x){1}else{$x*(f($x-1))}}
输出:
PS C:\> f 0
1
PS C:\> f 5
120
PS C:\> f 1
1
PS C:\> f 125
1.88267717688893E+209
filter f($x){if($x){$x*(f($x-1))}else{1}}
。如果通过管道调用它,则可以进一步减少到36个字符,因为它是一个过滤器(例如125|f
):filter f{if($_){$_*($_-1|f)}else{1}}
我今天感觉就像在学习一门新语言,这就是我的目标...(我为什么要对自己这样做?)该条目不会获得任何奖励,但是到目前为止,使用同样的语言!
Take Northern Line to Bank
Take Central Line to Holborn
Take Piccadilly Line to Heathrow Terminals 1, 2, 3
Take Piccadilly Line to Acton Town
Take District Line to Acton Town
Take District Line to Parsons Green
Take District Line to Bank
Take District Line to Parsons Green
Take District Line to Acton Town
Take District Line to Hammersmith
Take Circle Line to Aldgate
Take Circle Line to Aldgate
Take Metropolitan Line to Chalfont & Latimer
Take Metropolitan Line to Aldgate
Take Circle Line to Hammersmith
Take District Line to Acton Town
Take Piccadilly Line to Bounds Green
Take Piccadilly Line to Acton Town
Take Piccadilly Line to Bounds Green
Take Piccadilly Line to Acton Town
Take District Line to Acton Town
Take District Line to Bank
Take Circle Line to Hammersmith
Take District Line to Upminster
Take District Line to Parsons Green
Take District Line to Notting Hill Gate
Take Circle Line to Notting Hill Gate
Take Circle Line to Bank
Take Circle Line to Temple
Take Circle Line to Aldgate
Take Circle Line to Aldgate
Take Metropolitan Line to Chalfont & Latimer
Take Metropolitan Line to Chalfont & Latimer
Take Metropolitan Line to Aldgate
Take Circle Line to Hammersmith
Take District Line to Upminster
Take District Line to Bank
Take District Line to Upney
Take District Line to Upminster
Take District Line to Hammersmith
Take District Line to Upminster
Take District Line to Upney
Take District Line to Bank
Take Circle Line to Embankment
Take Circle Line to Embankment
Take Northern Line to Angel
Take Northern Line to Moorgate
Take Metropolitan Line to Chalfont & Latimer
Take Metropolitan Line to Moorgate
Take Circle Line to Moorgate
Take Northern Line to Mornington Crescent
当然,任何在伦敦旅行的人都会立即明白这一点,因此,我确定我不需要给出完整的解释。
一开始的大部分工作是处理0案件。在将乘积初始化为1之后,我可以利用0来计算max(input,1)以获取新的输入!= 1!然后可以开始主循环。
(编辑:通过从“希思罗机场1、2、3”航站楼剥离1来保存一整趟旅程,而不是通过将7(姐妹)本身除以生成它。我还使用了一种更便宜的方法来生成-1下一步。)
在Mornington Crescent中,递减是昂贵的(尽管比Tube本身便宜)。为了使事情更有效率,我通过将解析后的0的NOT取值来生成-1,并将其存储在Hammersmith中进行大部分循环。
0\:#v_# 1#<\$v *\<
>:1-:#^_$>\:#^_$
这是一个功能,因为它是不使用环绕的独立代码块。您必须将参数放置在堆栈的顶部,然后从左上角向右输入,该函数将从右下角退出,并且结果在堆栈顶部。
例如计算125的阶乘
555** 0\:#v_# 1#<\$v *\<
>:1-:#^_$>\:#^_$ .@
测试0
0 0\:#v_# 1#<\$v *\<
>:1-:#^_$>\:#^_$ .@
&:!#@_>:# 1# -# :# _$>\# :#* _$.@
其中&应该由输入替换)。它是32个字符/字节
*/1.+!
K从右到左工作(大部分情况下),因此我们枚举x
(从0
到组成一个列表/数字数组x-1
),将1
其相加(列表范围0
到x
),然后将所有数字相乘。如果不需要计算125!
,我可以通过删除.
旁边的内容再节省1个字节1
。无论如何,是125!以毫秒为单位计算:
*/1.+!125.
1.882677e+209
*/1.+!
:6个字节。