Deranged!Combinatorics:计算子因子


25

因子rencontres数字A000166)是一个数字序列,类似于排列组合中显示的因子阶数字。特别是ñ个subfactorial N!给出的数量紊乱一套的ñ元素。排列是一种排列,其中没有元素保持在相同位置。可以通过以下递归关系定义子因子:

!n = (n-1) (!(n-1) + !(n-2))

实际上,相同的递归关系也适用于阶乘,但是对于子阶,我们从以下内容开始:

!0 = 1
!1 = 0

(对于阶乘,我们当然有1!= 1。

给定n,您的任务是计算n

规则

像阶乘一样,子阶数增长非常快。如果您的程序只能处理输入n以便可以用您的语言的本机数字类型表示!n,那是很好的。但是,理论上,您的算法必须对任意n有效。这意味着,您可以假设积分结果和中间值可以用您的语言准确表示。请注意,如果常数e以有限精度存储或计算,则排除常数e

结果必须是一个精确的整数(尤其是您不能用科学计数法近似结果)。

您可以编写程序或函数,并使用接收输入和提供输出的任何标准方法

您可以使用任何编程语言,但是请注意,默认情况下,这些漏洞是禁止的。

这是,因此以字节为单位的最短有效答案为准。

测试用例

n     !n
0     1
1     0
2     1
3     2
4     9
5     44
6     265
10    1334961
12    176214841
13    2290792932
14    32071101049
20    895014631192902121
21    18795307255050944540
100   34332795984163804765195977526776142032365783805375784983543400282685180793327632432791396429850988990237345920155783984828001486412574060553756854137069878601

Answers:


19

函数,336字节

字节数假定使用BOM进行UTF-16编码。

┌─╖┌─╖  ┌─╖ 
│f╟┤♭╟┐┌┤♭╟┐
╘╤╝╘═╝├┘╘═╝├────┐
 │┌─╖ │ ┌┐┌┘╔═╗╓┴╖
 ││f╟─┴┐└┴┼─╢0║║f║
 │╘╤╝  │  │ ╚═╝╙─╜
 │┌┴╖ ┌┴╖┌┴╖ ╔═╗
 ││+╟┐│×╟┤?╟┐║1║
 │╘╤╝│╘╤╝╘╤╝┘╚╤╝
 └─┘ └─┘  └───┘

这定义了一个函数f,该函数采用一个整数,并向左旋转90度输出另一个整数。它适用于任意大的输入。

在线尝试!

考虑到这是功能可按它甚至相当快的(N = 20需要约14秒TIO)。主要的放慢是由于双重递归,因为我认为Funciton解释器不会自动记住函数。

不幸的是,某些等宽字体不能正确地等距字体和/或在行之间插入小间隙。以下是TIO的所有代码的屏幕截图:

enter image description here

我认为可能有更多的选择,例如通过将条件从更改>0<1并交换条件的分支,以便我可以重用数字文字,或者可以使用完全不同的公式,但是我相当对它已经非常紧凑感到满意。

说明

尽管它使用基本情况!(-1)=!0 = 1,但这基本上实现了质询中给出的递归。n-1n-2用前置函数计算,中间结果n-1在三个地方重用。它没有更多的内容,因此我将快速浏览控制流程:

               ─┐
               ╓┴╖
               ║f║
               ╙─╜

这是函数头发射函数的输入Ñ长所连接的线路。这会立即到达T接点,该接点将简单地复制该值。

        ┌┐┌┘╔═╗
        └┴┼─╢0║
          │ ╚═╝

0框只是一个数字文字。4向结点计算两个函数:引出底部的路径计算0 <n,我们将使用它来确定基本情况。左移的路径分别计算出0 << n(左移),但是我们使用Starkov构造放弃了该值。

         ┌┴╖ ╔═╗
         ┤?╟┐║1║
         ╘╤╝┘╚╤╝
          └───┘

我们将其引入三向条件?。如果该值为false,则返回常量结果1。的右端?是函数输出。我在这里将其扭曲180度,以便f在程序的其余部分中输入和输出的相对方向更加方便。

如果条件为真,则将使用其他值。让我们看一下通往该分支的路径。(请注意,Funciton的评估实际上是惰性的,因此,如果不需要此分支,将永远不会对其进行评估,这使得递归可以首先实现。)

        ┌─╖ 
      ┐┌┤♭╟┐
      ├┘╘═╝
      │
     ─┴┐

在另一个分支中,我们首先计算n-1,然后将路径拆分两次,以便获得该值的三个副本(一个用于递归系数,一个用于第一个子因子,最后一个用于n-2)。

┌─╖┌─╖
│f╟┤♭╟
╘╤╝╘═╝
 │┌─╖
 ││f╟
 │╘╤╝
 │┌┴╖
 ││+╟
 │╘╤╝
 └─┘ 

就像我说的那样,我们将一个副本与另一个副本递减,然后将n-1n-2递归馈入f,最后将两个结果加到+

       ┐
       │
      ┌┴╖
     ┐│×╟
     │╘╤╝
     └─┘

剩下的就是将n-1!(n-1)+!(n-2)相乘。


13

绿洲,5字节

使用马丁给出的公式。码:

+n<*X

解剖版:

+n<*

a(0) = 1a(1) = 0

说明a(n) =

+       # Add the previous two terms, a(n - 1) + a(n - 2).
 n<     # Compute n - 1.
   *    # Multiply the top two elements.

在线尝试!


尼斯招用X:-)顺便说一句,你有没有实现这个了吗?这些日子之一,我们将无法仅更改初始值就可以摆脱
困境

@LuisMendo是的,我做到了!它用作命令标志(是指向信息页面的链接)。感谢您的建议 :)。
阿德南


7

果冻,7个字节

R=Œ!Ḅċ0

这种方法会造成混乱,因此相当慢。

在线尝试!

怎么运行的

R=Œ!Ḅċ0  Main link. Argument: n

R        Range; yield [1, ..., n].
  Œ!     Yield all permutations of [1, ..., n].
 =       Perform elementwise comparison of [1, ..., n] and each permutation.
    Ḅ    Unbinary; convert each result from base 2 to integer. This yields 0 for
         derangements, a positive value otherwise.
     ċ0  Count the number of zeroes.

7

Brachylog(2),11个字节

⟦₁{p:?\≠ᵐ}ᶜ

在线尝试!

说明

这基本上只是规范从英语到Brachylog的直接翻译(因此具有可以轻松地对其进行修改以应对规范的细微变化(例如查找特定列表的排列数)的优势。

⟦₁{p:?\≠ᵐ}ᶜ
⟦₁           Start with a list of {the input} distinct elements
  {      }ᶜ  Then count the number of ways to
   p         permute that list
      \      such that taking corresponding elements
    :?       in {the permutation} and the list of distinct elements
       ≠     gives different elements
        ᵐ    at every position

5

内置解决方案的语言

按照xnor的建议,这是CW答案,应在其中编辑基于单个内置函数以计算子因子或生成所有排列的琐碎解决方案。

Mathematica,12个字节

Subfactorial

叹了口气 Mathematica ...
epicbob57 '17

5

Python 3中35 32个字节

f=lambda n:n<1or(-1)**n+n*f(n-1)

此用途的递推关系!N = N!(N-1)+(-1)ñ@ Laikoni的Haskell的答案,与基础案例!0 = 1

在线尝试!


我认为您也可以使用此处给出的其他公式,这样可以节省两个字节:f=lambda n:n<1or n*f(n-1)+(-1)**n
阿德南

1
三个字节,稍有重新排序。;)
丹尼斯

1
重复执行的有趣之处在于,如果将基本案例推回n=-1,则使用哪个值都没有关系。这对于某些语言可能很有用(例如,在Mathematica中,如果保存了任何字节,则实际上可以使它保持未定义状态)。
马丁·恩德

5

M,9个字节

o2!÷Øe+.Ḟ

如您所见,通过删除M使用符号数学,因此不会出现精度问题。

在线尝试!不是已经发布的最短的解决方案,而是快速的

怎么运行的

o2!÷Øe+.Ḟ  Main link. Argument: n

o2         Replace input 0 with 2, as the following formula fails for 0.
  !        Compute the factorial of n or 2.
   ֯e     Divide the result by e, Euler's natural number.
      +.   Add 1/2 to the result.
        Ḟ  Floor; round down to the nearest integer.

5

MATL9 8字节

:tY@-!As

@Dennis的Jelly答案类似,这实际上会建立排列并计算其中有多少是排列错位。太慢了

在线尝试!

:     % Input n implicitly: Push [1 2 ... n]
t     % Duplicate 
Y@    % Matrix of all permutations, each on a row
-     % Element-wise subtract. A zero in a row means that row is not a derangement
!     % Transpose
A     % True for columns that don't contain zeros
s     % Sum. Implicitly display

3

数学,21字节

Round@If[#>0,#!/E,1]&

我对此陌生,不知道我在做什么...

在线尝试!


1
具有相同字节数的两种选择:Round[(#/. 0->2)!/E]&±0=1;±n_:=Round[n!/E](尽管我不知道Mathics是否像Mathematica一样支持源文件的单字节编码)。
马丁·恩德

第一个很好用(我我知道它能做什么),但是第二个似乎不支持Mathics ±。它可以与一起使用f,但是要花两个字节。
丹尼斯

在相同字节数下的另一个:Round[#!/E]+1-Sign@#&。烦人的初始值...!
格雷格·马丁

3

Ruby,27个字节

f=->n{n<1?1:n*f[n-1]+~0**n}

~0**n(-1)**n!短


3

CJam(10字节)

1qi{~*)}/z

在线演示

这使用了递归!n = n !(n-1) + (-1)^n,我从n! / e OEIS中已经发现。

解剖

循环计算(-1)^n !n,因此我们需要在末尾取绝对值:

1     e# Push !0 to the stack
qi{   e# Read an integer n and loop from 0 to n-1
  ~   e#   Bitwise not takes i to -(i+1), so we can effectively loop from 1 to n
  *   e#   Multiply
  )   e#   Increment
}/
z     e# Take the absolute value

2

05AB1E,8个字节

΃N*®Nm+

在线尝试!

说明

Î         # initialize stack with 0 and input
 ƒ        # for N in range [0 ... input]:
  N*      # multiply top of stack with N
    ®Nm   # push (-1)^N
       +  # add

2

MATLAB,33个字节

@(n)(-1)^n*hypergeom([1 -n],[],1)

Anonympus函数,使用“排列和应用程序”第3节中的公式 Mehdi。

使用示例:

>> @(n)(-1)^n*hypergeom([1 -n],[],1)
ans = 
    @(n)(-1)^n*hypergeom([1,-n],[],1)
>> ans(6)
ans =
   265

2

JavaScript(ES6),26个字节

f=n=>!n||n*f(n-1)-(~n%2|1)

使用@Laikoni的答案中的递归关系。在ES7中,您可以使用+(-1)**n代替来保存一个字节-(~n%2|1)


2

PostScript,81岁 76 69字节

这是两个公式的实现。

n * f(n-1)+(-1)^ n

/ f {dup 0 eq {pop 1} {dup dup 1 sub f mul exch 2 mod 2 mul 1 sub sub} ifelse} def

/f{dup 0 eq{pop 1}{dup dup 1 sub f mul -1 3 2 roll exp add}ifelse}def

该版本输出浮点数。如果需要输出整数:

/f{dup 0 eq{pop 1}{dup dup 1 sub f mul -1 3 2 roll exp cvi add}ifelse}def

重达73个字节。

另一个公式更长一些:81个字节。

(n-1)*(f(n-1)+ f(n-2))

/f{dup 1 le{1 exch sub}{1 sub dup f exch dup 1 sub f 3 -1 roll add mul}ifelse}def

这些函数从堆栈获取其参数,并将结果保留在堆栈中。

您可以在文件中或在交互式PostScript提示符(例如GhostScript)下使用以下命令测试功能:

0 1 12{/i exch def [i i f] ==}for

输出

[0 1]
[1 0.0]
[2 1.0]
[3 2.0]
[4 9.0]
[5 44.0]
[6 265.0]
[7 1854.0]
[8 14833.0]
[9 133496.0]
[10 1334961.0]
[11 14684570.0]
[12 176214848.0]

这是一个完整的PostScript文件,可将输出呈现到屏幕或打印机页面。(PostScript中的注释以开头%)。

%!PS-Adobe-3.0

% (n-1)*(f(n-1)+f(n-2))
% /f{dup 1 le{1 exch sub}{1 sub dup f exch dup 1 sub f 3 -1 roll add mul}ifelse}def

% n*f(n-1)+(-1)^n
/f{dup 0 eq{pop 1}{dup dup 1 sub f mul -1 3 2 roll exp add}ifelse}def

% 0 1 12{/i exch def [i i f] ==}for

/FS 16 def              %font size
/LM 5 def               %left margin
/numst 12 string def    %numeric string buffer

/Newline{currentpoint exch pop FS sub LM exch moveto}def
/Courier findfont FS scalefont setfont
LM 700 moveto

(Subfactorials) Newline
0 1 12{
    dup numst cvs show (: ) show f numst cvs show Newline
}for
showpage
quit

1
-1 3 2 roll exp比...短一点exch 2 mod 2 mul 1 sub
彼得·泰勒

@PeterTaylor就是这样!:)我忘了exp:oops:但是,它返回一个浮点数,我我需要输出一个整数以符合问题。
下午17年

1
我认为您仍然可以cvi节省开支。(注意:未经测试,但是通过阅读文档,我认为它应该可以工作)。
彼得·泰勒

@PeterTaylor是的,cvi可以,并且比我以前的版本还短。
下午17年

1

PHP,69字节

function f($i){return$i>1?$i*f($i-1)+(-1)**$i:1-$i;}echo f($argv[1]);

用这种方式 a(n) = n*a(n-1) + (-1)^n


1
您只需要提供函数,而不是完整程序,因此可以删除最后的17个字符。通过不使用特殊输入框1可以进一步节省空间。我认为这两次节省可以将其减少到47个字节。
彼得·泰勒

1

PHP,50 44

for(;$i++<$argn;)$n=++$n*$i-$i%2*2;echo$n+1;

与运行 echo <n> | php -nR '<code>

其优点a(n) = n*a(n-1) + (-1)^n在于,它仅取决于先前的值。这使得它可以迭代而不是递归地实现。这样可以节省较长的函数声明。

@Titus -6个字节。谢谢 !


-1个字节:$i++<$argv[1]。-2个字节:for(;$i++<$argv[1];)$n=++$n*$i-$i%2*2;echo$n+1;。(带有- -R$argn
Titus

@Titus有人无聊吗?:D您介意为-R和给我一个例子$argn吗?
克里斯多夫(Christoph)

1
不无聊,但渴望打高尔夫球。参见php.net/manual/de/features.commandline.options.php:echo <输入> | php -nR'<code>'。示例:codegolf.stackexchange.com/a/113046
Titus

1
@Titus我说对了吗?;-)
克里斯托夫(Christoph)

0

Mathematica,40个字节

±0=1;±1=0;±n_:=(n-1)(±(n-1)+±(n-2))

在默认的ISO 8859-1编码下为31字节。


0

C,34个字节

a(n){return n?n*a(n-1)-n%2*2+1:1;}

说明:

a(n){                            } define a function called a of n
     return                     ;  make the function evaluate to...
            n?                :1   set the base case of 1 when n is 0
              n*a(n-1)             first half of the formula on the page
                      -n%2*2+1     (-1)**n

0

R,47个字节

n=scan();`if`(!n,1,floor(gamma(n+1)/exp(1)+.5))

使用与Mego答案相同的公式

备用方法,使用PerMallows库为52个字节

n=scan();`if`(!n,1,PerMallows::count.perms(n,n,'h'))

0

其实是18个位元组

;!@ur⌠;!@0Dⁿ/⌡MΣ*≈

在线尝试!

说明:

;!@ur⌠;!@0Dⁿ/⌡MΣ*≈
;                   duplicate input
 !                  n!
  @ur               range(0, n+1) (yields [0, n])
     ⌠;!@0Dⁿ/⌡M     for each i in range:
      ;               duplicate i
       !              i!
        @0Dⁿ          (-1)**i
            /         (-1)**i/i!
               Σ    sum
                *   multiply sum by n!
                 ≈  floor into int

如果实际精度更高,则可以使用的12字节版本:

;!╠@/1½+L@Y+

在线尝试!

与所有其他答案(截至发布时)不同,此解决方案既不使用递归公式也不使用求和公式。而是使用以下公式:

derangement formula

该公式相对容易实现:

!╠@/1½+L
!         n!
 ╠        e
  @/      divide n! by e
    1½+   add 0.5
       L  floor

现在,唯一的问题是该公式仅适用于positive n。如果尝试使用n = 0,该公式将错误地产生0。但是,这很容易解决:通过将布尔取反应用于输入并将其添加到公式的输出,可以为所有非负数提供正确的输出n。因此,经过更正的程序是:

;!╠@/1½+L@Y+
;             duplicate input
 !            n!
  ╠           e
   @/         divide n! by e
     1½+      add 0.5
        L     floor
         @Y   boolean negate the other copy of the input (1 if n == 0 else 0)
           +  add

一直给我负面答案...
Leaky Nun

@LeakyNun这是因为精度限制。对于较大的输入(大约n = 100),(-1)**n/n!不能用双精度IEEE 754浮点数表示。根据挑战,这是可以接受的。
Mego

即使是n=4……
Leaky Nun

@LeakyNun哦。我不知道为什么要使用地板分割。立即修复。
Mego



0

爱丽丝 20 18字节

1/o
k\i@/&wq*eqE+]

在线尝试!

说明

这使用来自递归Laikoni的回答!N = N!(N-1)+( - 1)ñ。与Funciton答案类似,我使用的是基本情况!(-1)= 1。我们用将该1放入堆栈1.。那这个

.../o
...\i@/...

...只是通常的十进制I / O框架。主要代码实际上是

&wq*eqE+]k

细分:

&w    Push the current IP address N times to the return address stack, which
      effectively begins a loop which will be executed N+1 times.
  q     Push the position of the tape head, which we're abusing as the
        iterator variable n.
  *     Multiply !(n-1) by n.
  e     Push -1.
  q     Retrieve n again.
  E     Raise -1 to the nth power.
  +     Add it to n*!(n-1).
  ]     Move the tape head to the right.
k     Jump back to the w, as long as there is still a copy of the return
      address on the return address stack. Otherwise, do nothing and exit
      the loop.
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.