Deranged! Combinatorics: Tính toán Subfactorial


25

Các subfactorial hoặc số rencontres ( A000166 ) là một chuỗi các con số tương tự như những con số thừa mà hiển thị trong tổ hợp hoán vị. Đặc biệt là n thứ subfactorial ! N cho biết số Loạn một tập hợp các n yếu tố. Một sự loạn trí là một hoán vị trong đó không có phần tử nào ở cùng một vị trí. Các cấu trúc con có thể được xác định thông qua quan hệ lặp lại sau đây:

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

Trong thực tế, mối quan hệ lặp lại tương tự giữ cho giai thừa, nhưng đối với các cấu trúc con chúng ta bắt đầu từ:

!0 = 1
!1 = 0

(Tất nhiên, đối với giai thừa chúng ta sẽ có, 1! = 1. )

Nhiệm vụ của bạn là tính toán ! N , cho n .

Quy tắc

Giống như giai thừa, tiểu phần phát triển rất nhanh. Nó là tốt nếu chương trình của bạn chỉ có thể xử lý đầu vào n như vậy ! N có thể được biểu diễn theo loại số tự nhiên của ngôn ngữ của bạn. Tuy nhiên, về mặt lý thuyết , thuật toán của bạn phải hoạt động cho n tùy ý . Điều đó có nghĩa là, bạn có thể cho rằng kết quả tích hợp và giá trị trung gian có thể được biểu thị chính xác bằng ngôn ngữ của bạn. Lưu ý rằng điều này loại trừ hằng số e nếu nó được lưu trữ hoặc tính toán với độ chính xác hữu hạn.

Kết quả cần phải là một số nguyên chính xác (cụ thể, bạn không thể tính gần đúng kết quả bằng ký hiệu khoa học).

Bạn có thể viết một chương trình hoặc một chức năng và sử dụng bất kỳ phương pháp tiêu chuẩn nào để nhận đầu vào và cung cấp đầu ra.

Bạn có thể sử dụng bất kỳ ngôn ngữ lập trình nào , nhưng lưu ý rằng các lỗ hổng này bị cấm theo mặc định.

Đây là , vì vậy câu trả lời hợp lệ ngắn nhất - được đo bằng byte - thắng.

Các trường hợp thử nghiệm

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

Câu trả lời:


19

Funciton , 336 byte

Số lượng byte giả định mã hóa UTF-16 bằng BOM.

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

Điều này xác định một hàm flấy một số nguyên và xuất ra một số nguyên khác ở góc 90 độ sang trái. Nó hoạt động cho đầu vào lớn tùy ý.

Hãy thử trực tuyến!

Xem xét đây là Funciton, nó thậm chí còn khá nhanh (n = 20 mất khoảng 14 giây trên TIO). Sự chậm lại chính xuất phát từ đệ quy kép, vì tôi không nghĩ trình thông dịch Funciton tự động ghi nhớ các chức năng.

Thật không may, một số phông chữ đơn cách không chính xác đơn cách hóa và / hoặc chèn các khoảng trống nhỏ giữa các dòng. Đây là một ảnh chụp màn hình của mã từ TIO với tất cả vẻ đẹp của nó:

nhập mô tả hình ảnh ở đây

Tôi nghĩ rằng có thể chơi golf thêm một số thứ nữa, ví dụ bằng cách thay đổi điều kiện từ >0sang <1và đổi các nhánh của điều kiện, để tôi có thể sử dụng lại số theo nghĩa đen, hoặc có thể bằng cách sử dụng một công thức hoàn toàn khác, nhưng tôi khá hạnh phúc với nó nhỏ gọn như thế nào rồi.

Giải trình

Điều này về cơ bản thực hiện đệ quy được đưa ra trong thử thách, mặc dù nó sử dụng trường hợp cơ sở ! (- 1) =! 0 = 1 . n-1n-2 được tính toán với chức năng tiền nhiệm và kết quả trung gian n-1 được sử dụng lại ở ba vị trí. Không có nhiều hơn thế, vì vậy tôi sẽ nhanh chóng đi qua luồng điều khiển:

               ─┐
               ╓┴╖
               ║f║
               ╙─╜

Đây là tiêu đề chức năng phát ra đầu vào của hàm n dài dòng đính kèm. Điều này ngay lập tức đạt đến ngã ba, chỉ đơn giản là nhân đôi giá trị.

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

Các 0hộp chỉ là một chữ số. Giao lộ 4 chiều tính hai hàm: đường dẫn ra khỏi đáy sẽ tính 0 <n , chúng ta sẽ sử dụng để xác định trường hợp cơ sở. Đường đi bên trái tính riêng 0 << n (dịch chuyển trái), nhưng chúng tôi loại bỏ giá trị này với cấu trúc Starkov .

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

Chúng tôi dẫn điều này vào điều kiện ba chiều ?. Nếu giá trị là sai, chúng tôi trả về kết quả không đổi 1. Đầu cuối bên phải lỏng lẻo ?là đầu ra chức năng. Tôi đang xoay nó 180 độ ở đây, để định hướng tương đối của đầu vào và đầu ra fthuận tiện hơn trong phần còn lại của chương trình.

Nếu điều kiện là đúng, thì giá trị khác sẽ được sử dụng. Hãy nhìn vào con đường dẫn đến chi nhánh này. (Lưu ý rằng đánh giá của Funciton thực sự lười biếng vì vậy nhánh này sẽ không bao giờ được đánh giá nếu không cần thiết, điều này làm cho phép đệ quy có thể xảy ra ngay từ đầu.)

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

Trong nhánh khác, trước tiên chúng ta tính n-1 và sau đó chia đường dẫn hai lần để chúng ta có được ba bản sao của giá trị (một cho hệ số tái phát, một cho hệ thống con đầu tiên, cuối cùng cho n-2 ).

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

Như tôi đã nói, chúng tôi giảm một bản sao một lần nữa với một bản sao khác , sau đó chúng tôi cung cấp cả n-1n-2 theo cách đệ quy fvà cuối cùng cộng hai kết quả lại với nhau trong +.

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

Tất cả những gì còn lại là nhân n-1 với ! (N-1) +! (N-2) .


13

Ốc đảo , 5 byte

Sử dụng công thức được đưa ra bởi Martin. Mã số:

+n<*X

Phiên bản mổ xẻ:

+n<*

với a(0) = 1a(1) = 0.

Giải thích , a(n) =:

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

Hãy thử trực tuyến!


Thủ thuật hay bằng cách sử dụng X:-) BTW, bạn đã thực hiện điều này chưa? Một trong những ngày này chúng tôi sẽ không thể thoát khỏi việc chỉ thay đổi các giá trị ban đầu
Luis Mendo

@LuisMendo Có tôi đã làm! Nó được sử dụng làm cờ lệnh ( đây là liên kết đến trang thông tin). Cảm ơn vì những đề suất của bạn :).
Ad Nam


7

Thạch , 7 byte

R=Œ!Ḅċ0

Cách tiếp cận này xây dựng các loạn trí, vì vậy nó khá chậm.

Hãy thử trực tuyến!

Làm thế nào nó hoạt động

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 byte

⟦₁{p:?\≠ᵐ}ᶜ

Hãy thử trực tuyến!

Giải trình

Về cơ bản, đây chỉ là bản dịch trực tiếp từ thông số kỹ thuật từ tiếng Anh sang Brachylog (và do đó có lợi thế là nó có thể dễ dàng được sửa đổi để xử lý các thay đổi nhỏ đối với thông số kỹ thuật, chẳng hạn như tìm số lượng các biến động của một danh sách cụ thể).

⟦₁{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

Ngôn ngữ với các giải pháp tích hợp

Theo gợi ý của xnor, đây là câu trả lời CW trong đó các giải pháp tầm thường dựa trên một tích hợp duy nhất để tính toán cấu trúc con hoặc tạo ra tất cả các biến động cần được chỉnh sửa.

Toán học, 12 byte

Subfactorial


5

Python 3 , 35 32 byte

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

Điều này sử dụng mối quan hệ lặp lại ! N = n! (N-1) + (-1) n từ câu trả lời Haskell của @ Laikoni , với trường hợp cơ sở ! 0 = 1 .

Hãy thử trực tuyến!


Tôi nghĩ bạn cũng có thể sử dụng phương trình khác được đưa ra ở đây , sẽ tiết kiệm hai byte : f=lambda n:n<1or n*f(n-1)+(-1)**n.
Ad Nam

1
Ba byte với một chút sắp xếp lại. ;)
Dennis

1
Điều thú vị của sự tái diễn này là nếu bạn đẩy trường hợp cơ sở trở lại n=-1, việc bạn sử dụng giá trị nào không quan trọng. Điều đó có thể hữu ích cho một số ngôn ngữ (ví dụ: trong Mathicala, bạn thực sự có thể để nó không xác định nếu điều đó lưu bất kỳ byte nào).
Martin Ender

5

M , 9 byte

o2!÷Øe+.Ḟ

Như bạn có thể thấy bằng cách loại bỏ , M sử dụng toán học tượng trưng, ​​do đó sẽ không có vấn đề chính xác.

Hãy thử trực tuyến! Không phải là giải pháp ngắn nhất đã được đăng, nhưng nhanh chóng .

Làm thế nào nó hoạt động

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

MATL , 9 8 byte

:tY@-!As

Tương tự như câu trả lời Jelly của @Dennis , điều này thực sự xây dựng các hoán vị và tính xem có bao nhiêu trong số chúng là loạn trí; vì vậy nó chậm

Hãy thử trực tuyến!

:     % 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

Toán học , 21 byte

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

Tôi rất mới với điều này và không biết tôi đang làm gì ...

Hãy thử trực tuyến!


1
Hai lựa chọn thay thế có cùng số byte: Round[(#/. 0->2)!/E]&±0=1;±n_:=Round[n!/E](mặc dù tôi không biết liệu Mathics có hỗ trợ mã hóa byte đơn cho các tệp nguồn như Mathicala không).
Martin Ender

Cái đầu tiên hoạt động tốt (tôi nghĩ tôi biết nó làm gì), nhưng Mathics không xuất hiện để hỗ trợ ±trong cái thứ hai. Nó sẽ làm việc với f, nhưng với giá của hai byte.
Dennis

Một cái khác có cùng số byte : Round[#!/E]+1-Sign@#&. Làm phiền các giá trị ban đầu ...!
Greg Martin

3

Ruby, 27 byte

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

~0**nngắn hơn (-1)**n!


3

CJam (10 byte)

1qi{~*)}/z

Bản demo trực tuyến .

Điều này sử dụng sự tái phát !n = n !(n-1) + (-1)^n, mà tôi bắt nguồn từ n! / evà sau đó được phát hiện đã có trong OEIS.

Mổ xẻ

Vòng lặp tính toán (-1)^n !n, vì vậy chúng ta cần lấy giá trị tuyệt đối ở cuối:

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 byte

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

Hàm ẩn danh sử dụng công thức trong Phần 3 của các ứng dụng và ứng dụng của Mehdi Hassani.

Ví dụ sử dụng:

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

2

JavaScript (ES6), 26 byte

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

Sử dụng mối quan hệ tái phát từ câu trả lời của @ Laikoni. Trong ES7, bạn có thể lưu một byte bằng cách sử dụng +(-1)**nthay vì -(~n%2|1).


2

PostScript, 81 76 69 byte

Dưới đây là việc thực hiện của cả hai công thức.

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

Phiên bản đó xuất ra một float. Nếu cần xuất một số nguyên:

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

có trọng lượng 73 byte.

Công thức khác dài hơn một chút: 81 byte.

(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

Các hàm này nhận được đối số của chúng từ ngăn xếp và để lại kết quả trên ngăn xếp.

Bạn có thể kiểm tra các chức năng, trong một tệp hoặc tại dấu nhắc PostScript tương tác (ví dụ GhostScript) với

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

đầu ra

[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]

Đây là một tệp PostScript hoàn chỉnh để hiển thị đầu ra ra màn hình hoặc trang máy in. (Nhận xét trong PostScript bắt đầu bằng %).

%!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 explà một chút công bằng ngắn hơn exch 2 mod 2 mul 1 sub.
Peter Taylor

@PeterTaylor Vậy đó! :) Tôi quên mất exp: oops: Tuy nhiên, nó trả về một số float và tôi nghĩ rằng tôi cần xuất một số nguyên để phù hợp với câu hỏi.
PM 2Ring

1
Tôi nghĩ bạn vẫn có thể tặc lưỡi cvivà tiết kiệm. (Lưu ý: chưa được kiểm tra, nhưng từ khi đọc tài liệu tôi nghĩ nó sẽ hoạt động).
Peter Taylor

@PeterTaylor Có, cvihoạt động và nó vẫn ngắn hơn phiên bản trước của tôi.
PM 2Ring

1

PHP, 69 byte

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

sử dụng cách này a(n) = n*a(n-1) + (-1)^n


1
Bạn chỉ cần cung cấp chức năng, không phải chương trình đầy đủ, vì vậy bạn có thể bỏ 17 ký tự cuối cùng. Có một khoản tiết kiệm hơn nữa bằng cách không nhập dữ liệu đặc biệt 1. Tôi nghĩ rằng hai khoản tiết kiệm giảm xuống còn 47 byte.
Peter Taylor

1

PHP, 50 44

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

Chạy với echo <n> | php -nR '<code>

Vẻ đẹp của a(n) = n*a(n-1) + (-1)^nnó chỉ phụ thuộc vào giá trị trước đó. Điều này cho phép nó được thực hiện lặp đi lặp lại thay vì đệ quy. Điều này tiết kiệm một khai báo hàm dài .

-6 byte bởi @Titus. Cảm ơn !


-1 byte : $i++<$argv[1]. -2 byte : for(;$i++<$argv[1];)$n=++$n*$i-$i%2*2;echo$n+1;. (-3 byte có -R$argn.)
Titus

@Titus có ai chán? : D bạn có phiền cho tôi một ví dụ cho -R$argn?
Christoph

1
Không chán, nhưng háo hức với golf. Xem php.net/manual/de/features.commandline.options.php: echo <input> | php -nR '<code>'. ví dụ: codegolf.stackexchange.com/a/113046
Tít

1
@Titus tôi đã hiểu đúng chưa? ;-)
Christoph

0

Toán học, 40 byte

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

Có 31 byte theo mã hóa ISO 8859-1 mặc định.


0

C, 34 byte

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

Giải trình:

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 byte

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

Sử dụng công thức tương tự như câu trả lời của Mego .

Phương pháp thay thế, 52 byte sử dụng PerMallowsthư viện

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

0

Trên thực tế , 18 byte

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

Hãy thử trực tuyến!

Giải trình:

;!@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

Phiên bản 12 byte sẽ hoạt động nếu Thực tế có độ chính xác cao hơn:

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

Hãy thử trực tuyến!

Không giống như tất cả các câu trả lời khác (kể từ khi đăng), giải pháp này không sử dụng công thức đệ quy cũng như công thức tính tổng. Thay vào đó, nó sử dụng công thức sau:

công thức loạn trí

Công thức này tương đối dễ thực hiện trong Thực tế:

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

Bây giờ, vấn đề duy nhất là công thức chỉ giữ cho tích cực n. Nếu bạn cố gắng sử dụng n = 0, công thức mang lại không chính xác 0. Tuy nhiên, điều này dễ dàng được sửa chữa: bằng cách áp dụng phủ định boolean cho đầu vào và thêm nó vào đầu ra của công thức, đầu ra chính xác được đưa ra cho tất cả không âm n. Do đó, chương trình với sự điều chỉnh đó là:

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

Tiếp tục đưa ra câu trả lời tiêu cực cho tôi ...
Leaky Nun

@LeakyNun Đó là vì giới hạn chính xác. Đối với các đầu vào lớn (xung quanh n = 100), (-1)**n/n!không thể được biểu diễn bằng các phao IEEE 754 có độ chính xác kép. Đó là chấp nhận được theo thách thức.
Mego

Ngay cả đối với n=4...
Leaky Nun

@LeakyNun ơi. Tôi không biết tại sao tôi lại sử dụng bộ phận nổi. Sửa nó ngay.
Mego



0

Alice , 20 18 byte

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

Hãy thử trực tuyến!

Giải trình

Điều này sử dụng đệ quy từ câu trả lời của Laikoni , ! N = n! (N-1) + (-1) n . Tương tự như câu trả lời của Funciton, tôi đang sử dụng trường hợp cơ sở ! (- 1) = 1 . Chúng tôi đặt 1 trên stack với 1.. Thì đây...

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

... Chỉ là khung I / O thập phân thông thường. Mã chính là

&wq*eqE+]k

Hỏng:

&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.
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.