我正在尝试创建一个可以生成随机、不重复、排序的数字列表的文件,以便我可以生成各种家庭作业。我设法找到了一种在 latex3 中生成随机序列的方法,但我无法弄清楚如何对其进行排序。以下是用于生成列表的 Latex3 代码:
%enable the latex3 coding language
\ExplSyntaxOn
% just for using all items
\NewDocumentCommand{\myforeach}{m +m}
{
\int_step_inline:nn { #1 } { #2 }
}
%%%%
\NewDocumentCommand{\declarerandomlist}{mO{1}m}
{% #1 = list name
% #2 = start point (default 1)
% #3 = end point
\egreg_randomlist_declare:nnn { #1 } { #2 } { #3 }
}
\NewDocumentCommand{\usefromrandomlist}{mm}
{% #1 = list name
% #2 = template
\egreg_randomlist_use:nn { #1 } { #2 }
}
\cs_new_protected:Nn \egreg_randomlist_declare:nnn
{
\seq_clear_new:c { l_egreg_randomlist_#1_seq }
\bool_do_until:nn { \int_compare_p:n { \seq_count:c { l_egreg_randomlist_#1_seq } = #3-#2+1 } }
{
\seq_put_left:cx { l_egreg_randomlist_#1_seq } { \int_rand:nn { #2 } { #3 } }
\seq_remove_duplicates:c { l_egreg_randomlist_#1_seq }
}
}
\cs_new_protected:Nn \egreg_randomlist_use:nn
{
\cs_set:Nn \__egreg_randomlist:n { #2 }
\seq_if_empty:cTF { l_egreg_randomlist_#1_seq }
{
\msg_warning:nnn { randomlist } { exhausted } { #1 }
}
{
\seq_pop_left:cN { l_egreg_randomlist_#1_seq } \l__egreg_randomlist_item_tl
\__egreg_randomlist:V \l__egreg_randomlist_item_tl
}
}
\tl_new:N \l__egreg_randomlist_item_tl
\cs_new_protected:Nn \__egreg_randomlist:n { } % initialize
\cs_generate_variant:Nn \__egreg_randomlist:n { V }
\msg_new:nnn { randomlist } { exhausted } {List ~ #1 ~ exhausted}
\ExplSyntaxOff
\newcommand{\randomincludefile}[3]{%
% #1 = list name
% #2 = name before numbering system
\usefromrandomlist{#1}{\input{\string#2##1.tex}}
}
问题出现在 \cs_new_protected:Nn \egreg_randomlist_declare:nnn 方法中。我想在生成序列 l_egreg_randomlist_#1_seq 后对其进行排序,但我似乎不知道该怎么做。我尝试使用 \seq_sort,但我的编译器似乎不喜欢我这样做的方式。任何帮助都将不胜感激,谢谢!
答案1
此答案提供了新的代码,它根本不需要对序列进行排序,因为它是按顺序构建的。有关对序列进行排序的代码,请参见下文。
构建随机排序序列
此处显示的代码将构建一个包含指定数字范围内所有数字的排序序列。然后它将随机从该序列中删除项目,直到剩下所需数量的项目。这样,我们不需要任何排序,仍然可以获得范围的随机子样本,每个元素都是唯一的。该代码不需要计算量大的函数\seq_remove_duplicates:N
,\seq_sort:Nn
因此性能应该比问题中提供的代码好得多。
\documentclass{article}
%enable the latex3 coding language
\ExplSyntaxOn
\seq_new:N \l__peabody_random_seq
\int_new:N \l__peabody_items_int
\int_new:N \l__peabody_length_int
\msg_new:nnn { peabody } { range-too-small }
{ range~ [#2,~#3]~ too~ small~ for~ random~ list~ with~ #1~ elements }
\msg_new:nnn { peabody } { unknown-seq } { no~ seq~ with~ name~ #1~ defined }
\cs_new_protected:Npn \__peabody_set_random_seq:nnn #1#2#3
{
% #1: number of subset elements
% #2: start of range
% #3: end of range
\int_set:Nn \l__peabody_items_int {#1}
\int_set:Nn \l__peabody_length_int { #3 - (#2) + \c_one_int }
\seq_clear:N \l__peabody_random_seq
\int_compare:nNnT \l__peabody_items_int > \l__peabody_length_int
{
\msg_error:nnxxx { peabody } { range-too-small }
{ \int_eval:n{#1} }
{ \int_eval:n{#2} }
{ \int_eval:n{#3} }
}
% build a sorted sequence with the full range
\int_step_inline:nnn {#2} {#3}
{ \seq_put_right:Nn \l__peabody_random_seq {##1} }
% remove the extra items (randomly)
\int_step_inline:nnnn
\l__peabody_length_int
{ -1 }
{ \l__peabody_items_int + \c_one_int }
{
\seq_pop_item:NnN
\l__peabody_random_seq { \int_rand:n {##1} } \l_tmpa_tl
}
% result is sorted
}
\cs_new_protected:Npn \peabody_loop_random_seq:nnnn #1#2#3#4
{
% #1: number of subset elements
% #2: start of range
% #3: end of range
% #4: code to do on each element
\__peabody_set_random_seq:nnn {#1} {#2} {#3}
\seq_map_inline:Nn \l__peabody_random_seq {#4}
}
\NewDocumentCommand \loopoverrandomsubsetofrange { m O{1} m +m }
{ \peabody_loop_random_seq:nnnn {#1} {#2} {#3} {#4} }
\cs_new_protected:Npn \peabody_set_named_random_seq:nnnn #1#2#3#4
{
% #1: name
% #2: number of subset elements
% #3: start of range
% #4: end of range
\__peabody_set_random_seq:nnn {#2} {#3} {#4}
\seq_clear_new:c { l__peabody_ #1 _random_seq }
\seq_set_eq:cN { l__peabody_ #1 _random_seq } \l__peabody_random_seq
}
\NewDocumentCommand \setrandomsubsetofrange { m m O{1} m }
{ \peabody_set_named_random_seq:nnnn {#1} {#2} {#3} {#4} }
\cs_new_protected:Npn \peabody_loop_named_random_seq:nn #1#2
{
\seq_if_exist:cTF { l__peabody_ #1 _random_seq }
{ \seq_map_inline:cn { l__peabody_ #1 _random_seq } {#2} }
{ \msg_error:nnn { peabody } { unknown-seq } {#1} }
}
\NewDocumentCommand \userandomsubsetofrange { m +m }
{ \peabody_loop_named_random_seq:nn {#1} {#2} }
\ExplSyntaxOff
\setrandomsubsetofrange{files}{50}[0]{100}% 50 random items in [0, 100]
\newcommand\randomincludefile[2]
{%
% #1: list name
% #2: name before numbering system
\userandomsubsetofrange{#1}{\input{#2##1.tex}}%
}
\begin{document}
\userandomsubsetofrange{files}{#1, }
\loopoverrandomsubsetofrange{10}[0]{20}{#1, }
\end{document}
对序列进行排序
下面扩展了宏,\egreg_randomlist_declare:nnn
以便在构建序列后对其进行排序。
%enable the latex3 coding language
\ExplSyntaxOn
% just for using all items
\NewDocumentCommand{\myforeach}{m +m}
{
\int_step_inline:nn { #1 } { #2 }
}
%%%%
\NewDocumentCommand{\declarerandomlist}{mO{1}m}
{% #1 = list name
% #2 = start point (default 1)
% #3 = end point
\egreg_randomlist_declare:nnn { #1 } { #2 } { #3 }
}
\NewDocumentCommand{\usefromrandomlist}{mm}
{% #1 = list name
% #2 = template
\egreg_randomlist_use:nn { #1 } { #2 }
}
\cs_new_protected:Nn \egreg_randomlist_declare:nnn
{
\seq_clear_new:c { l_egreg_randomlist_#1_seq }
\bool_do_until:nn { \int_compare_p:n { \seq_count:c { l_egreg_randomlist_#1_seq } = #3-#2+1 } }
{
\seq_put_left:cx { l_egreg_randomlist_#1_seq } { \int_rand:nn { #2 } { #3 } }
\seq_remove_duplicates:c { l_egreg_randomlist_#1_seq }
}
\seq_sort:cn { l_egreg_randomlist_#1_seq }
{
\int_compare:nNnTF {##1} > {##2}
\sort_return_swapped:
\sort_return_same:
}
}
\cs_new_protected:Nn \egreg_randomlist_use:nn
{
\cs_set:Nn \__egreg_randomlist:n { #2 }
\seq_if_empty:cTF { l_egreg_randomlist_#1_seq }
{
\msg_warning:nnn { randomlist } { exhausted } { #1 }
}
{
\seq_pop_left:cN { l_egreg_randomlist_#1_seq } \l__egreg_randomlist_item_tl
\__egreg_randomlist:V \l__egreg_randomlist_item_tl
}
}
\tl_new:N \l__egreg_randomlist_item_tl
\cs_new_protected:Nn \__egreg_randomlist:n { } % initialize
\cs_generate_variant:Nn \__egreg_randomlist:n { V }
\msg_new:nnn { randomlist } { exhausted } {List ~ #1 ~ exhausted}
\ExplSyntaxOff
\newcommand{\randomincludefile}[3]{%
% #1 = list name
% #2 = name before numbering system
\usefromrandomlist{#1}{\input{\string#2##1.tex}}
}
但是,由于您的随机序列构建器正在将范围内的所有数字构建到该序列中,只是以一种混洗的方式,这与首先构建从 0 到 10 的序列本质上相同......因此,\egreg_randomlist_declare:nnn {foo} {0} {10}
您总是会得到相同的结果,即包含元素 0、1、2、...、10 的序列。