[Tex/LaTex] Selecting random elements from a comma separated list

comma-separated listmacrosrandom numbers

I would like to have a command (either user-defined, or from a package, or whatever) that has the basic form:

\selectNrandom{N}{list, of, comma, separated, elements}{code to execute}

which will select N distinct, random elements from the csv list that follows, and then executes the code on those elements.

For example, this would select two elements and then typeset them with a large space between them (think math quiz):

\selectNrandom{2}{N, W, Z, Q, R, C}{%
    \mathbb{firstElement} \qquad  \mathbb{secondElement}
    }

I don't know what kind of MWE to post other than the above since I don't really have any idea of how to even begin writing a macro to do this.

Justification for Asking (i.e., I did do my homework!): I have been reading through sources such as various package documentations (etextools, probsoln, datatool, etc) as well as a few books (Joy of TeX, The Advanced TeXbook) and various websites. But I am still very new to the whole programming aspects of LaTex and Tex.

Best Answer

Here's a version with xparse and LaTeX3 code, with the help of the random.tex file by D. Arsenau

\documentclass{article}
\usepackage{xparse}
\input{random}

\ExplSyntaxOn
\NewDocumentCommand{\htguse}{ m }
 {
  \use:c { htg_arg_#1: }
 }
\NewDocumentCommand{\selectNrandom}{ m m m }
 {
  \htg_select_n_random:nnn { #1 } { #2 } { #3 }
 }

\cs_new_protected:Npn \htg_select_n_random:nnn #1 #2 #3
 {
  \seq_clear:N \l_htg_used_seq
  \int_set:Nn \l_htg_length_int { \clist_count:n { #2 } }
  \int_compare:nTF { #1 > \l_htg_length_int }
   {
    \msg_error:nnxx { randomchoice } { too-many } { #1 } { \int_to_arabic:n { \l_htg_length_int } }
   }
   {
    \int_step_inline:nnnn { 1 } { 1 } { #1 }
     {
      \htg_get_random:
      \cs_set:cpx { htg_arg_##1: }
       { \clist_item:nn { #2 } { \l_htg_random_int } }
     }
    #3
   }
 }
\cs_new_protected:Npn \htg_get_random:
 {
  \setrannum { \l_htg_random_int } { 1 } { \l_htg_length_int }
  \seq_if_in:NxTF \l_htg_used_seq { \int_to_arabic:n { \l_htg_random_int } }
   { \htg_get_random: }
   { \seq_put_right:Nx \l_htg_used_seq { \int_to_arabic:n { \l_htg_random_int } } }
 }
\seq_new:N \l_htg_used_seq
\int_new:N \l_htg_length_int
\int_new:N \l_htg_random_int
\msg_new:nnnn { randomchoice } { too-many }
 { Too~ many~choices }
 { You~want~to~select~#1~elements,~but~you~have~only~#2 }
\ExplSyntaxOff

\begin{document}

\selectNrandom{2}
  {N, W, Z, Q, R, C}
  {$\mathbf{\htguse{1}}$ and $\mathbf{\htguse{2}}$}

\selectNrandom{3}
  {A, B, C}
  {$\mathbf{\htguse{1}}$, $\mathbf{\htguse{2}}$ and $\mathbf{\htguse{3}}$}

\selectNrandom{3}
  {N, W}
  {$\mathbf{\htguse{1}}$, $\mathbf{\htguse{2}}$ and $\mathbf{\htguse{3}}$}

\end{document}

The macros take care to check that distinct elements are chosen by maintaining the list of already extracted elements and doing a new choice if a number is extracted again.

You refer to the first, second, and so on, element by \htguse{1}, \htguse{2} and so on.

The third call will raise an error:

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
! randomchoice error: "too-many"
! 
! Too many choices
! 
! See the randomchoice documentation for further information.
! 
! For immediate help type H <return>.
!...............................................  

l.56 ...bf{\htguse{2}}$ and $\mathbf{\htguse{3}}$}

? h
|'''''''''''''''''''''''''''''''''''''''''''''''
| You want to select 3 elements, but you have only 2
|...............................................

enter image description here

With a recent expl3 kernel, \input{random} is not needed any longer with pdflatex or LuaLaTeX (it still is necessary for XeLaTeX). The line

\setrannum { \l_htg_random_int } { 1 } { \l_htg_length_int }

can be substituted with

\int_set:Nn \l_htg_random_int { \fp_eval:n { randint( \l_htg_length_int ) } }