阶乘(或者更好的是二项式系数)函数

阶乘(或者更好的是二项式系数)函数

是否有一个函数l3fp可以直接计算阶乘,或者更好的是,二项式系数?

答案1

自发布以来1.2fxintexpr具有用于精确和浮点评估的功能(和binomial中分别有相应的宏)。xintxintfrac

在此处输入图片描述

\documentclass[a4paper]{article}
\usepackage[hscale=0.83]{geometry}

\usepackage{xintexpr}[2016/03/12]% release 1.2f or later

\usepackage[english]{babel}
\usepackage[np, autolanguage]{numprint}
\usepackage{amsmath}
\begin{document}
\begingroup

\hrule\medskip

  \edef\N{\xinttheiiexpr 0..[30]..300\relax}% 0, 30, 60, ..., 270, 300

  \xintFor #1 in\N\do{$\binom{300}{#1}=\xinttheiiexpr binomial(300,#1)\relax$\par\smallskip}%

\medskip\hrule\medskip

  \edef\N{\xinttheiiexpr [\N][1:-1]\relax}% cut out first and last

  \xintFor #1 in\N\do{$\binom{300}{#1}\approx\np{\xintthefloatexpr binomial(300,#1)\relax}$\par\smallskip}%

\medskip\hrule\medskip

  \xintFor #1 in {500, 1000, 1500, 2000, 2500}\do
  {$\binom{3000}{#1}\approx\np{\xintthefloatexpr binomial(3000,#1)\relax}$\par\smallskip}

\medskip\hrule\medskip
\endgroup

\end{document}

这个答案的其余部分现在已经过时了。


编辑 2015-11-05 因为最近版本的 xint 不加载 xinttools不再。

首先,其实现binomial(n,k) = n choose k仅使用 \numexpr。如果实际值至少为 ,则将失败2^31(第一个太大的是2203961430=binomial(34,16)2333606220= binomial(34,17))。2 参数宏\binomialb是可扩展的,因此可以\numsiunitx\numprintnumprint,或\edef\write,或在\ifnum测试中。

它的参数被赋予给\numexpr,因此它们可能是像 的中缀表达式2+3,或者像 的计数器\value{mycount},等等。

该算法在宏的注释中描述\binomialb。无需包。

然后,使用相同的算法,但使用长整数宏xint。此宏的参数处理方式\binomialB与 的参数处理方式完全相同\binomialb。同样,此宏是 (f) 可展开的,并且分两步完全展开。

\binomialB能够处理n = 1000k = 500

然后是一个\threebythree宏,用于打印长数字,将数字按三位分组并允许换行。此宏只需要xinttools对于它的\xintLengthxint(加载xinttools)来说是不需要的。

最后,一个宏\binomialA,这是我的第一个答案;对于大结果,它的速度会稍微慢一些n*(n-1)*..*(n-k+1),因为它先除以 的阶乘k。例如,factorial(500)有 1135 位数字,而binomial(1000,500)结果只有 300 位数字。因此,通过进行一次巨大的除法计算\binomialA,结果2.2x比使用小除数的 499 次除法要慢\binomialB。(用 观察xint 1.2)。

\documentclass {article}
\usepackage{xint, xinttools}

% for testing of line breaks + digits grouping
% \usepackage{siunitx}  % no line breaks
% \usepackage{numprint} % no line breaks in math, line breaks in text possible

% Expandably computing  binomial(n,k)=n choose k

% after having replaced k by the smallest of k and n-k, and checked if
% k=0, either one of the following products produces integers at each
% mutiply/divide steps: 

% n * (n-1)/2 * (n-2)/3 * .... * (n-k+1)/k

% or

% (n-k+1) * (n-k+2)/2 * (n-k+3)/3 * ... * n/k

% eTeX \numexpr does multiply/divide in one "double-precision" step,
% thus arithmetic overflow should not happen, as long as the result is <
% 2^31 (and naturally the initial n, binomial (2147483648,0) will not
% work

% For no special reason I chose the second product. 
% (notice that as k<n-k+1 also the first product is increasing, no 
% intermediate thing can cause overflow if the final thing does not)

% Each (n-k+j)/j step could be seen as (n-k)/j + 1, thus only j would need 
% incrementing;  up to the price of an extra addition, and I preferred to 
% carry around both an u=n-k+j and a v=j

% ALGORITHM
% replace k by the smallest of k and n-k
% if k=0 return 1
% else set w=n-k+1
%          u=n-k+2
%          v=2
% endif
% if v>k return w
% else
%        w<-w*u/v
%        u<-u+1
%        v<-v+1
% repeatif

% Constraint: expandability. Adding +1 has a cost and fetching a list of
% tokens also has one. To use one macro less, or not do twice u->u+1, 
% u and v are shifted from the start by  1 to be usable directly in the 
% updating of w.

% no check on validity of inputs

%-----------------------------------------------------------
% expandable macro \binomialb. No package needed. 

\catcode`_ 11

\def\binomialb #1#2{\romannumeral0\expandafter
    \binomialb_a\the\numexpr #1\expandafter.\the\numexpr #2.}

\def\binomialb_a #1.#2.{\expandafter\binomialb_b\the\numexpr #1-#2.#2.}

\def\binomialb_b #1.#2.{\ifnum #1<#2 \expandafter\binomialb_ca
                            \else   \expandafter\binomialb_cb
                            \fi {#1}{#2}}

\def\binomialb_ca #1{\ifnum#1=0 \expandafter \binomialb_one\else 
                    \expandafter \binomialb_d\fi {#1}}

\def\binomialb_cb #1#2{\ifnum #2=0 \expandafter\binomialb_one\else
                      \expandafter\binomialb_d\fi {#2}{#1}}

\def\binomialb_one #1#2{ 1}

\def\binomialb_d #1#2{\expandafter\binomialb_e \the\numexpr #2+1.#1!}

% n-k+1.k! -> u=n-k+2.v=2.w=n-k+1.k!
\def\binomialb_e #1.{\expandafter\binomialb_f \the\numexpr #1+1.2.#1.}

% u.v.w.k!
\def\binomialb_f #1.#2.#3.#4!%
{\ifnum #2>#4 \binomialb_end\fi
 \expandafter\binomialb_f
 \the\numexpr #1+1\expandafter.%
 \the\numexpr #2+1\expandafter.%
 \the\numexpr #1*#3/#2.#4!}

\def\binomialb_end #1*#2/#3!{\fi\space #2}
\catcode`_ 8

%-----------------------------------------------------------
% expandable macro \binomialB. Requires package xint

\catcode`_ 11

\def\binomialB #1#2{\romannumeral0\expandafter
    \binomialB_a\the\numexpr #1\expandafter.\the\numexpr #2.}

\def\binomialB_a #1.#2.{\expandafter\binomialB_b\the\numexpr #1-#2.#2.}

\def\binomialB_b #1.#2.{\ifnum #1<#2 \expandafter\binomialB_ca
                            \else   \expandafter\binomialB_cb
                            \fi {#1}{#2}}

\def\binomialB_ca #1{\ifnum#1=0 \expandafter \binomialB_one\else 
                    \expandafter \binomialB_d\fi {#1}}

\def\binomialB_cb #1#2{\ifnum #2=0 \expandafter\binomialB_one\else
                      \expandafter\binomialB_d\fi {#2}{#1}}

\def\binomialB_one #1#2{ 1}

\def\binomialB_d #1#2{\expandafter\binomialB_e \the\numexpr #2+1.#1!}

% n-k+1.k! -> u=n-k+2.v=2.w=n-k+1.k!
\def\binomialB_e #1.{\expandafter\binomialB_f \the\numexpr #1+1.2.#1.}

% u.v.w.k!
\def\binomialB_f #1.#2.#3.#4!%
{\ifnum #2>#4 \binomialB_end\fi
 \expandafter\binomialB_f
 \the\numexpr #1+1\expandafter.%
 \the\numexpr #2+1\expandafter.%
 \romannumeral0\xintiiquo{\xintiiMul{#1}{#3}}{#2}.#4!}

\def\binomialB_end #1\romannumeral0\xintiiquo #2#3!%
 {\fi\binomialB_enda #2}

\def\binomialB_enda\xintiiMul #1#2{ #2}

\catcode`_ 8

%-----------------------------------------------------------
% The three by three macro. Requires xinttools (not automatically loaded by xint)

% \threebythree for printing 3 by 3
% argument supposed to be f-expandable

% defines the thousand separator, for example:
\DeclareRobustCommand*{\threebythreesep}{\ifmmode 
  \allowbreak\mskip 6mu plus 1mu minus 1mu 
  \else
  \hskip .3333em plus 0.0555em minus 0.0555em \fi  }

\catcode`_ 11
    % submitting the #1 to an \edef could be better

\newcommand*{\threebythree}[1]{\expandafter\threebythree_a 
                               \romannumeral-`0#1.}

\def\threebythree_a #1{\if#1-\expandafter\xint_firstoftwo\else
                             \expandafter\xint_secondoftwo\fi 
                       {-\threebythree_b}{\threebythree_b #1}}

% #1 is assumed to be not empty
\def\threebythree_b #1.{\expandafter\threebythree_c\expandafter
                        {\romannumeral0\xintlength {#1}}#1...}

\def\threebythree_c #1{\ifcase \numexpr #1+3-3*((#1+2)/3)\relax
                       \expandafter\threebythree_da
                       \or\expandafter\threebythree_db
                       \or\expandafter\threebythree_dc
                       \fi }

\def\threebythree_da #1#2#3{#1#2#3\threebythree_e}
\def\threebythree_db #1{#1\threebythree_e}
\def\threebythree_dc #1#2{#1#2\threebythree_e}

\def\threebythree_e #1#2#3{\if#1.\else \threebythreesep
                            #1#2#3\expandafter\threebythree_e\fi}

\catcode`_ 8


%-----------------------------------------------------------
% a less efficient algorithm doing n(n-1)...(n-k+1) divided by k!:
% expandable, expands in two steps, requires xint

\catcode`_ 11
\def\binomialA #1#2{\romannumeral0%
   \expandafter\binomialA_a\the\numexpr #1\expandafter.\the\numexpr #2.}
\def\binomialA_a #1.#2.{\expandafter\binomialA_b\the\numexpr #1-#2.#2.{#1}}
\def\binomialA_b #1.#2.{\ifnum #1<#2 \expandafter\binomialA_c
                            \else   \expandafter\binomialA_d
                            \fi {#1}{#2}}
\def\binomialA_c #1#2#3{\xintiiquo {\xintiiPrd {\xintSeq [-1]{#3}{#2+1}}}%
                                {\xintiFac {#1}}}
\def\binomialA_d #1#2#3{\xintiiquo {\xintiiPrd {\xintSeq [-1]{#3}{#1+1}}}%
                                {\xintiFac {#2}}}

\catcode`_ 8

%-----------------------------------------------------------


\begin{document}\thispagestyle{empty}
\fdef\test {\binomialB {10}{5}}
\noindent
\texttt{\string\binomialB \{10\}\{5\} expanded twice: \meaning\test!!!
  (<-space check)}

\xintFor* #1 in {\xintSeq {0}{20}}
\do
{\xintFor* #2 in {\xintSeq {0}{#1}}
 \do {\binomialb {#1}{#2}\xintifForLast{\par}{, \hskip 0pt plus 1pt}}%
% comparison for checking everything ok
% \xintFor* #2 in {\xintSeq {0}{#1}}
%  \do {\binomialB {#1}{#2}\xintifForLast{\par}{, \hskip 0pt plus 1pt}}%
% \xintFor* #2 in {\xintSeq {0}{#1}}
%  \do {\binomialA {#1}{#2}\xintifForLast{\par}{, \hskip 0pt plus 1pt}}%
}

% \noindent\pdfresettimer
% \fdef\test {\binomialB {1000}{500}}
% (algorithm B: \the\pdfelapsedtime)\par

% text mode
${1000 \choose 500}={}$\threebythree{\binomialB {1000}{500}}
% or \threebythree {\test} etc...

% math mode
${1000 \choose 575}=\threebythree{\binomialB {1000}{575}}$

% \medskip

% \noindent\pdfresettimer
% \fdef\test {\binomialA {1000}{500}}
% (algorithm A: \the\pdfelapsedtime)\par

% % ${1000 \choose 500}={}$\threebythree{\test}

% Testing printing with package siunitx or numprint
% the \num of siunitx does not allow line breaks

% the \numprint of numprint allows line breaks in text mode, but NOT in
% math mode, if its thousand separator is suitably configured.

% \npthousandsep {\hskip .1666em plus .5pt minus .5pt}
% \numprint {\binomialB {1000}{500}}

\end{document}


备注 2015/11/05:下面算法 B 与算法 A 的时间对比已经过时;随着xint 1.2发布的发布,\binomialA{1000}{500} 不再存在,12x但只2.2x比 \binomialB{1000}{500} 慢。

备注 2016/03/15:看来 \binomialB 使用 1.2f 后速度提高了约 20%,但无论如何 1.2f 的 binomial(1000,500) 仍然更快2x


二项式 B

使用宏将数字三三位分组并允许换行\threebythree

三乘三


第一个答案:

二项式系数

\input xint.sty
\input xinttools.sty

\hsize 19cm
\vsize 27.7cm
\hoffset \dimexpr -1in+1cm\relax
\voffset \dimexpr -1in+1cm\relax
\nopagenumbers

\catcode`_ 11
\def\binomial #1#2{\expandafter\binomial_a\the\numexpr #1\expandafter.%
                                          \the\numexpr #2.}
\def\binomial_a #1.#2.{\expandafter\binomial_b\the\numexpr #1-#2.#2.{#1}}
\def\binomial_b #1.#2.{\ifnum #1<#2 \expandafter\binomial_c
                            \else   \expandafter\binomial_d
                            \fi {#1}{#2}}
\def\binomial_c #1#2#3{\xintiiQuo {\xintiiPrd {\xintSeq [-1]{#3}{#2+1}}}%
                                {\xintiFac {#1}}}
\def\binomial_d #1#2#3{\xintiiQuo {\xintiiPrd {\xintSeq [-1]{#3}{#1+1}}}%
                                {\xintiFac {#2}}}
\catcode`_ 8

\xintFor* #1 in {\xintSeq {0}{20}}
\do
{\xintFor* #2 in {\xintSeq {0}{#1}}
 \do {\binomial {#1}{#2}\xintifForLast{\par}{, \hskip 0pt plus 1pt}}%
}

\def\allowsplits #1{\ifx #1\relax \else #1\hskip 0pt plus 1pt\relax
\expandafter\allowsplits\fi}%
\def\printnumber #1{\expandafter\allowsplits \romannumeral-`0#1\relax }%

\xintFor* #1 in {\xintSeq {0}{50}}\do 
{\printnumber{\binomial {50}{#1}}\xintifForLast{\par}{\unskip, }}%

\xintFor* #1 in {\xintSeq {0}{100}}\do 
{\printnumber{\binomial {100}{#1}}\xintifForLast{\par}{\unskip, }}%

\bye

答案2

这是一个使用 Lua(La)TeX 数学功能的解决方案。TeX 端宏\mchoose{n}{k}和 Lua 端函数完成mchoose(n,k)大部分工作。辅助 Lua 函数fwrite允许打印任意长度的整数;但是,此示例中没有规定在打印过长的数字时引入换行符...

在此处输入图片描述

% !TEX TS-program = lualatex
\documentclass{article}
\usepackage[a4paper,margin=2cm]{geometry} % just for this example
\usepackage{luacode}

%%% Lua-side code
\begin{luacode}
-- 'mchoose' is patterned after the posting 
--     in http://stackoverflow.com/a/15302448.
-- Thanks, @egreg, for providing the pointer to this posting!
   function mchoose ( n , k ) -- 'n' should be no smaller than 'k'
     if ( k == 0 or k == n ) then
       return 1 
     else 
       return ( n * mchoose ( n-1, k-1 ) / k )
     end
   end
-- Print an arbitrary-length integer as a string
   function fwrite ( z ) 
      tex.sprint ( string.format("%.0f" , z ) )
   end
\end{luacode}

%%% LaTeX-side code
\newcommand{\mchoose}[2]{\directlua{fwrite(mchoose(#1,#2))}}

\begin{document}
\verb+\mchoose{6}{3}+: \mchoose{6}{3}.

\verb+\mchoose{1000}{70}+: \tiny \mchoose{1000}{70}.
\end{document}

相关内容