是否有一个函数l3fp
可以直接计算阶乘,或者更好的是,二项式系数?
答案1
自发布以来1.2f
,xintexpr
具有用于精确和浮点评估的功能(和binomial
中分别有相应的宏)。xint
xintfrac
\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
是可扩展的,因此可以\num
在siunitx
或\numprint
numprint
,或\edef
或\write
,或在\ifnum
测试中。
它的参数被赋予给\numexpr
,因此它们可能是像 的中缀表达式2+3
,或者像 的计数器\value{mycount}
,等等。
该算法在宏的注释中描述\binomialb
。无需包。
然后,使用相同的算法,但使用长整数宏xint
。此宏的参数处理方式\binomialB
与 的参数处理方式完全相同\binomialb
。同样,此宏是 (f) 可展开的,并且分两步完全展开。
\binomialB
能够处理n = 1000
,k = 500
。
然后是一个\threebythree
宏,用于打印长数字,将数字按三位分组并允许换行。此宏只需要xinttools
对于它的\xintLength
宏xint
(加载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
。
使用宏将数字三三位分组并允许换行\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}