7

Inspired from this answer I would like to generate a multi-digit addend using some random numbers.

I can generate the random numbers, but I'm unable to use them properly (Tex capacity exceeded). Here is my 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}
4
  • 1
    The big problem here is that \random is not expandable. The other problem is that, even if it were expandable, you would have to expand it for \showsum by way of \expandafter\showsum\expandafter{\expanded{\random}} Feb 8, 2020 at 18:45
  • hmmm, so a better approach would be using another random generator package (pgf for example)?
    – lAtExFaN
    Feb 8, 2020 at 18:54
  • I don't work with random numbers in LaTeX, but you would need to be able to do something like \edef\tmp{\random} and then \expandafter\showsum\expandafter{\tmp}. Feb 8, 2020 at 18:57
  • Another approach would be to write the random numbers to a file and then read them back in. Feb 8, 2020 at 18:58

2 Answers 2

8

As said by @StevenB.Segletes, \rand isn't expandable, therefore one can't record the produced random numbers in a macro simply using this function. For the same reason, even if \showsum were trying to recursively expand your \random, it wouldn't be able to get anything useful from it—and this wouldn't be better if you passed it directly the argument \rand\arabic{rand} \rand\arabic{rand} \rand\arabic{rand}, which is only one expansion step away from your \random.

In order to solve this, I propose to use the \int_rand:nn function from expl3, which does produce random integers in an expandable manner (the bounds provided as arguments are both included in the possible results). Moreover, I'll wrap this up so that for instance, calling \myrandsums{5}{1000}{9999} prints a sum of five random integers n for which 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}

Sample output:

enter image description here

The following is a slightly more elaborate variant. It provides a \myrandsums function that behaves the same as above, and additionally \myRandsums that ignores the current contents of \showsums:

  • \myRandsums*{<num>}{<min>}{<max>} always prints the sum of the randomly chosen integers;

  • \myRandsums{<num>}{<min>}{<max>} never prints the result, only the operands.

\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}

Sample output:

enter image description here

2
  • thanks a lot @frougon - it works nice. And some day I'll hopefully also understand why ... ;-)
    – lAtExFaN
    Feb 8, 2020 at 21:21
  • You're welcome. I added a slightly more elaborate version that provides 1) an equivalent \myrandsums function and 2) a new \myRandsums that prints the result if, and only if you use its starred form. If you want to understand all this, you can 1) Read the TeXbook until chap. 20 (macros), possibly skipping math typesetting; 2) Practice macro programming (you can treat problems like this question in plain TeX). 3) Read expl3.pdf and chosen parts of interface3.pdf from l3kernel. Happy TeXing! :-)
    – frougon
    Feb 9, 2020 at 0:30
4

An implementation with expl3 a bit different from frougon's, not needing extra packages.

\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}

enter image description here

3
  • thanks @egreg, less dependencies are of course preferable. One small note: Compared to frougon's answer the alignment is not so nice (I use two for-loops to generate a sheet and the lined tables seem to have different width) Code: \large \foreach \i in {1,...,12}{% \noindent \foreach \j in {1,...,6}{% \randomsum*{3}{1}{999} } }
    – lAtExFaN
    Feb 9, 2020 at 9:35
  • workaround: placed the tabular into a parbox with fixed width
    – lAtExFaN
    Feb 9, 2020 at 10:54
  • @lAtExFaN The widths are different to cope with different width of the summands and the total.
    – egreg
    Feb 9, 2020 at 14:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.