如何将多个随机数(使用 lcg 包生成)传递给宏?

如何将多个随机数(使用 lcg 包生成)传递给宏?

灵感来自这个答案我想使用一些随机数生成一个多位加数。

我可以生成随机数,但无法正确使用它们(超出 Tex 容量)。这是我的 MWE:

\documentclass{article}
\usepackage{stringstrings,stackengine}

\newcounter{mysum}
\newcommand\showsum[1]{%
    \convertchar[q]{#1}{ }{+}%
    \setcounter{mysum}{\numexpr\thestring\relax}%
    \def\stackalignment{r}%
    \if T\showsums\edef\tmp{\themysum}\else\edef\tmp{~}\fi%
    \raisebox{-\dp\strutbox}{+\,}{\stackunder{\underline{\ \Longstack{#1}}}{%
            \tmp}}%
}

\usepackage{lcg}

\begin{document}

\reinitrand[first=0, last=1000]
%get three random values:
\newcommand{\random}{\rand\arabic{rand} \rand\arabic{rand} \rand\arabic{rand}}
These are three random values: \random

\def\showsums{T}
\showsum{411 319 217}  $\qquad$ %<-- works :-)
\showsum{\random}               %<-- doesn't work :-(

\end{document}

答案1

正如@StevenB.Segletes 所说,\rand是不可扩展的,因此不能仅使用此函数在宏中记录生成的随机数。出于同样的原因,即使我们\showsum试图递归扩展\random,也无法从中获得任何有用的东西——如果您直接将参数 传递给它,情况也不会更好\rand\arabic{rand} \rand\arabic{rand} \rand\arabic{rand},因为 距离 只有一个扩展步骤\random

为了解决这个问题,我建议使用\int_rand:nn中的函数expl3,它确实以可扩展的方式生成随机整数(作为参数提供的边界都包含在可能的结果中)。此外,我将把它包装起来,例如,调用 会\myrandsums{5}{1000}{9999}打印五个随机整数的总和n其中 1000 ≤ n ≤ 9999.

\documentclass{article}
% 'geometry' is only used so that the examples nicely fit on a single line.
\usepackage[hmargin=2cm]{geometry}
\usepackage{stringstrings}
\usepackage{stackengine}
\usepackage{xparse}

\ExplSyntaxOn
\cs_new_protected:Npn \latexfan_showsum:n #1
  {
    \showsum {#1}
  }

\cs_generate_variant:Nn \latexfan_showsum:n { x }
\cs_generate_variant:Nn \seq_use:Nn { NV }

\NewDocumentCommand \myrandsums { m m m }
  {
    \seq_clear:N \l_tmpa_seq
    \int_step_inline:nn {#1}
      { \seq_put_right:Nx \l_tmpa_seq { \int_rand:nn {#2} {#3} } }
    \latexfan_showsum:x { \seq_use:NV \l_tmpa_seq \c_space_tl }
  }
\ExplSyntaxOff

% Equivalent to the desired \random macro from your example
\newcommand{\hardcodedSumOfThree}{\myrandsums{3}{0}{1000}}

\newcounter{mysum}
\newcommand\showsum[1]{%
    \convertchar[q]{#1}{ }{+}%
    \setcounter{mysum}{\numexpr\thestring\relax}%
    \def\stackalignment{r}%
    \if T\showsums\edef\tmp{\themysum}\else\edef\tmp{~}\fi%
    \raisebox{-\dp\strutbox}{+\,}{\stackunder{\underline{\ \Longstack{#1}}}{%
            \tmp}}%
}

\begin{document}

\def\showsums{T}% Print the result
\showsum{411 319 217}%
%
\qquad
\myrandsums{3}{0}{1000}%
%
\qquad
\hardcodedSumOfThree % ditto
%
\qquad
\myrandsums{5}{1000}{9999}%
%
\qquad
\def\showsums{F}% Don't print the result
\myrandsums{8}{1000}{9999}%

\end{document}

示例输出:

在此处输入图片描述

以下是一个稍微复杂一些的变体。它提供了一个\myrandsums与上面行为相同的函数,此外还\myRandsums忽略了的当前内容\showsums

  • \myRandsums*{<num>}{<min>}{<max>}总是打印随机选择的整数的总和;

  • \myRandsums{<num>}{<min>}{<max>}从不打印结果,仅打印操作数。

\documentclass{article}
% 'geometry' is only used so that the first series of examples nicely fits on a
% single line.
\usepackage[hmargin=2cm]{geometry}
\usepackage{stringstrings}
\usepackage{stackengine}
\usepackage{xparse}

\ExplSyntaxOn
\cs_new_protected:Npn \latexfan_showsum:n #1
  {
    \showsum {#1}
  }

\cs_generate_variant:Nn \latexfan_showsum:n { x }
\cs_generate_variant:Nn \seq_use:Nn { NV }

\cs_new_protected:Npn \latexfan_randsums:nnn #1#2#3
  {
    \seq_clear:N \l_tmpa_seq
    \int_step_inline:nn {#1}
      { \seq_put_right:Nx \l_tmpa_seq { \int_rand:nn {#2} {#3} } }
    \latexfan_showsum:x { \seq_use:NV \l_tmpa_seq \c_space_tl }
  }

\NewDocumentCommand \myrandsums { m m m }
  {
    \latexfan_randsums:nnn {#1} {#2} {#3}
  }

\NewDocumentCommand \myRandsums { s m m m }
  {
    \group_begin:
    \cs_set:Npx \showsums { \IfBooleanTF {#1} {T} {F} }
    \latexfan_randsums:nnn {#2} {#3} {#4}
    \group_end:
  }
\ExplSyntaxOff

% Equivalent to the desired \random macro from your example
\newcommand{\hardcodedSumOfThree}{\myrandsums{3}{0}{1000}}

\newcounter{mysum}
\newcommand\showsum[1]{%
    \convertchar[q]{#1}{ }{+}%
    \setcounter{mysum}{\numexpr\thestring\relax}%
    \def\stackalignment{r}%
    \if T\showsums\edef\tmp{\themysum}\else\edef\tmp{~}\fi%
    \raisebox{-\dp\strutbox}{+\,}{\stackunder{\underline{\ \Longstack{#1}}}{%
            \tmp}}%
}

\begin{document}

\def\showsums{T}%
\showsum{411 319 217}% Print the result
%
\qquad
\myrandsums{3}{0}{1000}%
%
\qquad
\hardcodedSumOfThree % ditto
%
\qquad
\myrandsums{5}{1000}{9999}%
%
\qquad
\def\showsums{F}% Don't print the result
\myrandsums{8}{1000}{9999}%

\bigskip
\qquad
\myRandsums*{3}{100}{999}%
\qquad
\myRandsums{2}{10}{99}%
\qquad
\myRandsums*{2}{10}{99}%
\qquad
\myRandsums{3}{100}{999}%

\end{document}

示例输出:

在此处输入图片描述

答案2

expl3与 frougon 略有不同的实现,不需要额外的包。

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn

\NewDocumentCommand{\randomsum}{smmm}
 {% #1 = * to show, #2 = summands, #3 = lower bound, #4 = upper bound
  \IfBooleanTF{#1}
   {
    \bool_set_false:N \l__latexfan_randomsum_solution_bool
   }
   {
    \bool_set_true:N \l__latexfan_randomsum_solution_bool
   }
  \latexfan_randomsum:nnn { #2 } { #3 } { #4 }
 }

\bool_new:N \l__latexfan_randomsum_solution_bool
\seq_new:N \l__latexfan_randomsum_summands_seq

\cs_new_protected:Nn \latexfan_randomsum:nnn
 {
  \seq_clear:N \l__latexfan_randomsum_summands_seq
  % make a sequence with random numbers
  \int_step_inline:nn { #1 }
   {
    \seq_put_right:Nx \l__latexfan_randomsum_summands_seq { \int_rand:nn { #2 } { #3 } }
   }
  % print the summands, first a raised +
  \raisebox{0.51\normalbaselineskip}{$+$}\,
  % the summands in column, with a rule in the middle
  \begin{tabular}[b]{@{\,}r@{}}
  \seq_use:Nn \l__latexfan_randomsum_summands_seq { \\ } \\
  \hline
  \bool_if:NTF \l__latexfan_randomsum_solution_bool
   {% a phantom of the sum to hint at the number of digits
    \phantom { \int_eval:n { \seq_use:Nn \l__latexfan_randomsum_summands_seq { + } } }
   }
   {% the sum
    \int_eval:n { \seq_use:Nn \l__latexfan_randomsum_summands_seq { + } }
   }
  \end{tabular}
 }

\ExplSyntaxOff

\begin{document}

\randomsum*{2}{10}{90}\qquad
\randomsum{4}{100}{999}\qquad
\randomsum{7}{1000}{9999}\qquad
\randomsum*{7}{1000}{9999}

\end{document}

在此处输入图片描述

相关内容