之前只会用ifthenelse
循环for
,很少用复杂的数据结构,不知道有什么方便的方法可以写长一点的代码。
举个例子,我想生成一个从 0 到 9 的随机数字排列。这不是一个复杂的问题,但我不知道如何轻松解决它。有没有可以让编程变得轻松的包?
答案1
通过LuaTeX您可以使用脚本语言Lua:
\documentclass{article}
\usepackage{luacode}
\begin{luacode}
function permute(n)
local tab = {}
for i = 1, n do tab[i] = i end
for i = 1, n do
local j = math.random(i, n)
tab[i], tab[j] = tab[j], tab[i]
end
for i = 1, n do
tex.print("\\shortstack{"..i.."\\\\"..tostring(tab[i]).."} ")
end
end
\end{luacode}
\begin{document}
\directlua{permute(20)}
\end{document}
答案2
以下是Knuth 洗牌算法和expl3
。
\documentclass{article}
\usepackage{amsmath,xparse}
\input{random}
\ExplSyntaxOn
\cs_new_eq:NN \knuthshuffle_get_random:Nnn \setrannum
\tl_new:N \l_knuthshuffle_tempa_tl
\tl_new:N \l_knuthshuffle_tempb_tl
\int_new:N \l_knuthshuffle_random_int
\prop_new:N \l_knuthshuffle_newperm_prop
\prop_new:N \g_knuthshuffle_identity_prop % the identity
\seq_new:N \l_knuthshuffle_permutation_seq
\int_step_inline:nnnn { 1 } { 1 } { 100 }
{
\prop_gput:Nnn \g_knuthshuffle_identity_prop { #1 } { #1 }
}
\NewDocumentCommand{\generatepermutation}{m}
{
\knuthshuffle_generate:n { #1 }
}
\NewDocumentCommand{\printpermutation}{}
{
\left(
\int_step_inline:nnnn { 1 } { 1 } { \seq_count:N \l_knuthshuffle_permutation_seq }
{
\begin{array}{c}
##1 \\ \seq_item:Nn \l_knuthshuffle_permutation_seq { ##1 }
\end{array}
}
\right)
}
\cs_new_protected:Nn \knuthshuffle_generate:n
{
\prop_set_eq:NN \l_knuthshuffle_newperm_prop \g_knuthshuffle_identity_prop
\int_step_inline:nnnn { #1 } { -1 } { 2 }
{
\knuthshuffle_get_random:Nnn \l_knuthshuffle_random_int { 1 } { ##1 }
\prop_get:NnN \l_knuthshuffle_newperm_prop { ##1 } \l_knuthshuffle_tempa_tl
\prop_get:NVN \l_knuthshuffle_newperm_prop \l_knuthshuffle_random_int \l_knuthshuffle_tempb_tl
\prop_put:NnV \l_knuthshuffle_newperm_prop { ##1 } \l_knuthshuffle_tempb_tl
\prop_put:NVV \l_knuthshuffle_newperm_prop \l_knuthshuffle_random_int \l_knuthshuffle_tempa_tl
}
\seq_clear:N \l_knuthshuffle_permutation_seq
\int_step_inline:nnnn { 1 } { 1 } { #1 }
{
\seq_put_right:Nx \l_knuthshuffle_permutation_seq
{
\prop_item:Nn \l_knuthshuffle_newperm_prop { ##1 }
}
}
%\seq_show:N \l_knuthshuffle_permutation_seq % for debugging
}
\ExplSyntaxOff
\begin{document}
\generatepermutation{20}
\[
\printpermutation
\]
\end{document}
排列存储在一个序列中,然后由您来决定如何处理它。我添加了一个\printpermutation
宏只是为了展示如何打印最近生成的排列。
我使用的工具是
表示 1 至 1000 之间的数字的恒等排列的固定属性列表
从最后一处向下循环;在步骤钾,一个随机数r1 至钾由 D. Arsenau生成
random.tex
(希望它能很快集成到expl3
);元素位于钾与位置处的元素交换r;另一个循环加载具有确定元素的序列,以供进一步处理。
身份最多定义为 100,这应该是一个足够的界限,并且可以缩短处理时间。
有一种更快的方法(尽管会浪费更多内存),使用一个\csname
技巧。以前的解决方案旨在通过一个玩具问题展示可用的工具,而不是寻找有效的实现。
\documentclass{article}
\usepackage{amsmath,xparse}
\input{random}
\ExplSyntaxOn
\cs_new_eq:NN \knuthshuffle_get_random:Nnn \setrannum
\tl_new:N \l_knuthshuffle_tempa_tl
\tl_new:N \l_knuthshuffle_tempb_tl
\int_new:N \l_knuthshuffle_random_int
\seq_new:N \l_knuthshuffle_permutation_seq
\NewDocumentCommand{\generatepermutation}{m}
{
\knuthshuffle_generate:n { #1 }
}
\NewDocumentCommand{\printpermutation}{}
{
\left(
\int_step_inline:nnnn { 1 } { 1 } { \seq_count:N \l_knuthshuffle_permutation_seq }
{
\begin{array}{c}
##1 \\ \seq_item:Nn \l_knuthshuffle_permutation_seq { ##1 }
\end{array}
}
\right)
}
\cs_new_protected:Nn \knuthshuffle_generate:n
{
\int_step_inline:nnnn { 1 } { 1 } { #1 }
{
\tl_clear_new:c { l_knuthshuffle_##1_element_tl }
\tl_set:cn { l_knuthshuffle_##1_element_tl } { ##1 }
}
\prop_set_eq:NN \l_knuthshuffle_newperm_prop \g_knuthshuffle_identity_prop
\int_step_inline:nnnn { #1 } { -1 } { 2 }
{
\knuthshuffle_get_random:Nnn \l_knuthshuffle_random_int { 1 } { ##1 }
\tl_set_eq:Nc \l_knuthshuffle_tempa_tl
{ l_knuthshuffle_##1_element_tl }
\tl_set_eq:Nc \l_knuthshuffle_tempb_tl
{ l_knuthshuffle_ \int_to_arabic:n \l_knuthshuffle_random_int _element_tl }
\tl_set_eq:cN { l_knuthshuffle_##1_element_tl }
\l_knuthshuffle_tempb_tl
\tl_set_eq:cN { l_knuthshuffle_ \int_to_arabic:n \l_knuthshuffle_random_int _element_tl }
\l_knuthshuffle_tempa_tl
}
\seq_clear:N \l_knuthshuffle_permutation_seq
\int_step_inline:nnnn { 1 } { 1 } { #1 }
{
\seq_put_right:Nv \l_knuthshuffle_permutation_seq { l_knuthshuffle_##1_element_tl }
}
% \seq_show:N \l_knuthshuffle_permutation_seq % for debugging
}
\ExplSyntaxOff
\begin{document}
\generatepermutation{20}
\[
\printpermutation
\]
\end{document}
这些值使用标记列表变量数组存储在关联数组中,这使得寻址速度更快,但会占用大量内存。
在 Plain TeX 中也是一样:
\input random
\newcount\myrandom
\newcount\tempcount
\def\generatepermutation#1{%
\def\lastlength{#1}%
\tempcount=0
\loop\ifnum\tempcount<#1\relax
\advance\tempcount 1
\expandafter\edef\csname shuffle\the\tempcount element\endcsname{\the\tempcount}%
\repeat
\loop\ifnum\tempcount>1
\setrannum\myrandom{1}{\tempcount}
\edef\tempa{\csname shuffle\the\tempcount element\endcsname}%
\edef\tempb{\csname shuffle\the\myrandom element\endcsname}%
\expandafter\edef\csname shuffle\the\tempcount element\endcsname{\tempb}%
\expandafter\edef\csname shuffle\the\myrandom element\endcsname{\tempa}%
\advance\tempcount -1
\repeat
}
\long\def\gobble#1{}
\def\printpermutation{%
\left(
\def\tempa{\gobble}%
\def\tempb{\gobble}%
\tempcount=0
\loop\ifnum\tempcount<\lastlength
\advance\tempcount 1
\edef\tempa{\tempa & \the\tempcount}%
\edef\tempb{\tempb & \csname shuffle\the\tempcount element\endcsname}%
\repeat
\vcenter{\tabskip=3pt\halign{&\hfil##\hfil\cr\tempa\cr\tempb\cr}}
\right)
}
\generatepermutation{20}
$$
\printpermutation
$$
\bye