並べ替えられた!Combinatorics:Subfactorialの計算


25

subfactorial又はrencontres番号A000166は)置換の組合せ論に表示階乗番号と同様の番号のシーケンスです。特に、n番目の階乗!nは、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

回答:


19

機能、336バイト

バイトカウントは、BOMを使用したUTF-16エンコーディングを前提としています。

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

これは、f1つの整数を取り、左に90度回転して別の整数を出力する関数を定義します。任意の大きな入力に対して機能します。

オンラインでお試しください!

これがFuncitonであると考えると、かなり高速です(n = 20はTIOで約14秒かかります)。Funcitonインタプリタが自動的に関数を記憶するとは思わないので、主なスローダウンは二重再帰によるものです。

残念ながら、一部の等幅フォントは、行間を正しく等幅にしたり、行間に小さな隙間を挿入したりしません。以下に、TIOのすべての美しさを示すコードのスクリーンショットを示します。

ここに画像の説明を入力してください

たとえば、条件をから>0に変更し、条件<1のブランチを交換して、数字リテラルを再利用できるようにするか、完全に異なる式を使用することで、これをさらにゴルフすることが可能になると思いますが、既にコンパクトに満足しています。

説明

基本的に!(-1)=!0 = 1を使用しますが、これは基本的にチャレンジで指定された再帰を実装します。n-1n-2は先行機能で計算され、中間結果n-1は3箇所で再利用されます。それ以上のことはないので、すぐに制御フローを見ていきます。

               ─┐
               ╓┴╖
               ║f║
               ╙─╜

これは、関数の入力を発する機能ヘッダであるN長い取り付けライン。これはすぐにTジャンクションに到達し、値を単純に複製します。

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

0ボックスには、ちょうど数値リテラルです。4ウェイジャンクションは2つの関数を計算します。ボトムから続くパスは0 <nを計算し、これを使用してベースケースを決定します。別々に左に移動するパスは、0 << n(左シフト)を計算しますが、この値をStarkov構造で破棄します。

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

これを3方向条件付きに導きます?。値がfalseの場合、定数resultを返し1ます。の右端が?関数出力です。ここでfは、プログラムの残りの部分で入力と出力の相対的な向きがより便利になるように、ここで180度回転させています。

条件が真の場合、他の値が使用されます。このブランチにつながるパスを見てみましょう。(Funcitonの評価は実際には遅延であるため、このブランチが必要でない場合は評価されないため、そもそも再帰が可能になることに注意してください。)

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

もう一方のブランチでは、最初にn-1を計算し、次にパスを2回分割して、値のコピーを3つ取得します(1つは再帰の係数、1つは最初のサブファクター、1つはn-2の最後)。

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

先ほど言ったように、あるコピーを別のコピーでデクリメントし、次にn-1n-2の両方を再帰的にフィードしf、最後に2つの結果を一緒に追加し+ます。

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

あとは、n-1!(n-1)+!(n-2)を掛けるだけです。


13

オアシス、5バイト

Martinによって与えられた式を使用します。コード:

+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:-) BTW を使用した素晴らしいトリック、これはまだ実装しましたか?私たちは初期値を変更して逃げることができなくなり、これらの日のいずれか
ルイスMendo

@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

Pythonの335の、32バイト

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

これは、@ LaikoniのHaskell回答からの再帰関係!n = n!(n-1)+(-1)nを使用し、ベースケース!0 = 1です。

オンラインでお試しください!


ここに記載されている他の式も使用できると思いますf=lambda n:n<1or n*f(n-1)+(-1)**n
アドナン

1
少し並べ替えた3バイト。;)
デニス

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 answerと同様に、これは実際に順列を構築し、それらのうちどれが混乱であるかを数えます。遅いです。

オンラインでお試しください!

:     % 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
同じバイトカウントでの2つの選択肢:Round[(#/. 0->2)!/E]&および±0=1;±n_:=Round[n!/E](MathicsがMathematicaのようにソースファイルのシングルバイトエンコーディングをサポートするかどうかはわかりませんが)。
マーティンエンダー

最初のものはうまく機能します(それが何をするは知っていると思います)が、Mathicsは±2番目のものではサポートしていないようです。で動作しfますが、2バイトのコストがかかります。
デニス

同じバイトカウントの別:Round[#!/E]+1-Sign@#&。迷惑な初期値...!
グレッグマーティン

3

ルビー、27バイト

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

~0**nより短い(-1)**n


3

CJam(10バイト)

1qi{~*)}/z

オンラインデモ

これはrecurrenceを使用します。これは、OEIS !n = n !(n-1) + (-1)^nから派生しn! / e、すでに発見されています。

解剖

ループはを計算する(-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

MATLAB、33バイト

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

Mehdi Hassaniによる混乱と応用のセクション3の式を使用する匿名関数。

使用例:

>> @(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バイトです。

もう1つの式は少し長く、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:を忘れましたが、floatを返します。質問に適合するために整数を出力する必要があると思います
PM 2Ring

1
私はあなたがまだにつかまっcviて節約をすることができると思います。(注:テストされていませんが、ドキュメントを読むことで動作するはずです)。
ピーターテイラー

@PeterTaylorはい、cvi動作します。以前のバージョンよりもまだ短いです。
PM 2Ring

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を特別なものにしないことで、さらに節約できます。2つの節約により、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;。(3バイト-R$argn。)
タイタス

@タイタス誰かが退屈しましたか?:D -Randの例を挙げて$argnいただけますか?
クリストフ

1
退屈ではありませんが、ゴルフに熱心です。php.net/manual/de/features.commandline.options.phpを参照してください:echo <input> | php -nR '<コード>'。例:codegolf.stackexchange.com/a/113046
タイタス

1
@タイタスは私がそれを正しくしましたか?;-)
クリストフ

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+

オンラインでお試しください!

他のすべての回答(投稿時点)とは異なり、このソリューションは再帰式も合計式も使用しません。代わりに、次の式を使用します。

混乱式

この式は、実際には比較的簡単に実装できます。

!╠@/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

...私のために、負の答えを与え続けて
漏れ修道女

@LeakyNunこれは、精度の制限によるものです。大きな入力(約n = 100)の場合、(-1)**n/n!倍精度IEEE 754 floatで表すことはできません。それは挑戦に応じて受け入れられます。
Mego

でものためのn=4...
漏れ修道女

@LeakyNunああ。フロア分割を使用していた理由がわかりません。今すぐ修正。
Mego



0

アリス20 18バイト

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

オンラインでお試しください!

説明

この用途からの再帰Laikoniの回答!N = N!(N-1)+(-1)のn。Funcitonの答えと同様に、ベースケース!(-1)= 1を使用しています。私たちはそれを置きます 1をスタックにます1.。それからこれ...

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

...は、通常の10進数の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.
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.