0

I found a code here from @John Kormylo that chooses \Nchoose numbers from 1,2,...,\Nmax numbers and give it out as comma-separatet \mylist.

Question: How could I make a command from this, like

\createlist{<name of list>}{<Nmax>}{<Nchoose>} 

For example \createliste{pokerlist}{52}{6}, so that I can say:
Show list: \pokerlist
or Third element is: \pgfmathparse{\pokerarray[2]} \pgfmathresult (see 'Hint')
and so on.

Hint 1: I need it as an array too, but I think I can say \def\myarray{\mylist} then.

Hint 2: In these lists are no duplicates (For example "1,2,2,2,5,6,7,8,9,10,11,12" is NOT allowed.)

enter image description here

\documentclass{article}
\usepackage{tikz}

% Choose \Ncoose from \Nmax
\pgfmathtruncatemacro\Nmax{52}% 
\pgfmathtruncatemacro\Nchoose{12}% 


\begin{document}
% List of \Ncoose from \Nmax:
\foreach \i in {1,...,\Nmax} {\expandafter\xdef\csname index\i\endcsname{\i}}% create indexes
\foreach \i in {1,...,\Nmax} {% randomise (last step redundant)
  \pgfmathparse{random(\i,\Nmax)}%
  \let\j=\pgfmathresult
  \expandafter\let\expandafter\tempa\csname index\i\endcsname
  \expandafter\let\expandafter\tempb\csname index\j\endcsname
  \global\expandafter\let\csname index\i\endcsname=\tempb
  \global\expandafter\let\csname index\j\endcsname=\tempa
}%
\xdef\mylist{\csname index1\endcsname}% create comma delimited list
\foreach \i in {2,...,\Nchoose} {\xdef\mylist{\mylist, \csname index\i\endcsname}}%


\section{{\Nchoose} random numbers from {1,2,\dots,\Nmax}}
\noindent\mylist

\section{{33} random numbers from {1,2,\dots,60}}
...

\section{{52} random numbers from {1,2,\dots,52}}
...
\end{document}
0

2 Answers 2

3

I don't think you need any package code for this, LaTeX has all the functionality pre-defined.


No duplicate answer based on a shuffled list

This makes a list 1..n then shuffles it and takes the first m entries

\documentclass{article}

\ExplSyntaxOn
\cs_generate_variant:Nn  \cs_set:Npn {Npe} % old latex
\cs_generate_variant:Nn  \clist_set:Nn {Ne} % old latex

\cs_new:Npn \cis_comma_tmp:n #1 {#1,}
\cs_new:Npn \cis_comma_tmpb:n #1 {\seq_item:Nn \l_tmpa_seq {#1} , }


\NewDocumentCommand\createlist{mmm}{
  \clist_set:Ne \l_tmpa_clist {
    \int_step_function:nN {#2} \cis_comma_tmp:n
  }
  
  \seq_set_from_clist:NN \l_tmpa_seq \l_tmpa_clist
  \seq_shuffle:N \l_tmpa_seq
  \cs_set:Npe#1 {
    \int_step_function:nN {#3-1} \cis_comma_tmpb:n
    \seq_item:Nn \l_tmpa_seq {#3}
  }
  }
\ExplSyntaxOff
\begin{document}
\createlist\aaa{52}{12}
\typeout{\aaa}


\createlist\bbb{60}{33}
\typeout{\bbb}


\createlist\ccc{52}{52}
\typeout{\ccc}


\end{document}

producing

12,21,36,23,2,24,3,49,25,52,8,10


9,15,36,27,51,48,10,32,55,31,49,57,47,5,42,12,29,43,17,56,44,34,35,30,2,16,41,23,28,33,37,11,59

36,34,15,49,7,45,33,31,2,29,51,50,5,10,44,17,6,43,3,14,1,19,9,23,48,24,7,32,42
,28,11,46,16,35,40,27,18,20,4,30,26,12,25,52,21,41,38,47,8,39,22,13


original answer

This produces the following terminal output (I left trailing , although they could be removed

40,12,45,51,43,15,6,21,8,37,16,39,


38,6,24,59,19,50,51,37,28,10,46,17,49,23,33,47,60,45,36,43,1,56,9,33,52,6,45,2,
30,2,30,49,46,


52,9,27,42,37,7,8,34,31,21,6,36,18,22,7,46,7,50,39,9,29,44,10,2,41,21,44,18,11,
20,37,16,16,36,16,26,49,7,21,18,21,40,32,28,43,31,27,25,5,18,16,6,
\documentclass{article}

\ExplSyntaxOn
\NewDocumentCommand\createlist{mmm}{
  \cs_set:Npe#1{\prg_replicate:nn{#3}{\int_rand:n{#2},}}}
\ExplSyntaxOff
\begin{document}

\createlist\aaa{52}{12}
\typeout{\aaa}


\createlist\bbb{60}{33}
\typeout{\bbb}


\createlist\aaa{52}{52}
\typeout{\aaa}


\end{document}

This is assuming that duplicates are allowed.

If not then instead use \seq_suffle on a sequence 1...N and then take the first m items.


A version removing one , and declaring e versions for old latex

\documentclass{article}


\ExplSyntaxOn
\cs_generate_variant:Nn  \cs_set:Npn {Npe} % old latex
\NewExpandableDocumentCommand\createlist{mmm}{
  \cs_set:Npe#1{\use:e{\exp_not:N\use_none:n\use:e{\prg_replicate:nn{#3}{,\int_rand:n{#2}}}}}}
\ExplSyntaxOff
\begin{document}

\createlist\aaa{52}{12}
\typeout{\aaa}


\createlist\bbb{60}{33}
\typeout{\bbb}


\createlist\aaa{52}{52}
\typeout{\aaa}


\end{document}
11
  • It's impressive to have that in one line; but for further work (TikZ, pgfplots etc.) it can be advantageous to have it in the usual form. The list of random numbers is of course just a detail of the whole thing and not the main goal.
    – cis
    Apr 15 at 13:51
  • @cis possibly although the result is in the same format used by a pgf foreach loop, just a comma separated list. So even if you use pgf elsewhere this could still be considered. Apr 15 at 13:52
  • I get Undefined control sequence. \createlist code #1#2#3->\cs_set:Npe ... at mine and at 'overleaf'. And I think the last comma can become problematic.
    – cis
    Apr 15 at 14:13
  • 1
    @cis see update Apr 15 at 21:51
  • 1
    @cis oh same issue as before you have an old latex, add \cs_generate_variant:Nn \clist_set:Nn {Ne} % old latex to define the e form. Apr 16 at 10:55
3

If you don't want duplicates, you have two strategies, but the one suggested by David is slower by a very large factor. Here I show two strategies: in the first the sequence with the integers from 1 to Nmax is generated, then shuffled and the first Nmax items are stored in a new sequence; in the second implementation, a random integer from 1 to Nmax is generated and added to the sequence if not already present, the process stopping when Nmax items have been added.

\documentclass{article}

\usepackage{l3benchmark}

\ExplSyntaxOn

\NewDocumentCommand{\createlistA}{mmm}
 {% #1 = list name, #2 = Nmax, #3 = Nchoose
  \seq_clear:N \l_tmpa_seq
  \int_step_inline:nn { #2 } { \seq_put_right:Nn \l_tmpa_seq { ##1 } }
  \seq_shuffle:N \l_tmpa_seq
  \seq_clear_new:c { l_cis_list_#1_seq }
  \int_step_inline:nn { #3 }
   {
    \seq_put_right:ce { l_cis_list_#1_seq } { \seq_item:Nn \l_tmpa_seq { ##1 } }
   }
 }

\NewDocumentCommand{\createlistB}{mmm}
 {% #1 = list name, #2 = Nmax, #3 = Nchoose
  \seq_clear:N \l_tmpa_seq
  \bool_until_do:nn { \int_compare_p:n { \seq_count:N \l_tmpa_seq = #3 } }
   {
    \tl_set:Ne \l_tmpa_tl { \int_rand:n { #2 } }
    \seq_if_in:NVF \l_tmpa_seq \l_tmpa_tl { \seq_put_right:NV \l_tmpa_seq \l_tmpa_tl }
   }
  \seq_clear_new:c { l_cis_list_#1_seq }
  \seq_set_eq:cN { l_cis_list_#1_seq } \l_tmpa_seq
 }

\benchmark:n { \createlistA{testA}{200}{6} }

\benchmark:n { \createlistB{testB}{200}{6} }

\stop

The output of the benchmark is

0.00248 seconds (1.66e4 ops)
7.69e-5 seconds (511 ops)

For Nmax=10 and Nchoose=6, we get

1.33e-4 seconds (869 ops)
8.36e-5 seconds (556 ops)

so the second strategy also wins when Nmax is small.

Hence I'll go with the second strategy.

\documentclass{article}

\ExplSyntaxOn

\NewDocumentCommand{\createlist}{mmm}
 {% #1 = list name, #2 = Nmax, #3 = Nchoose
  \cis_createlist:nnn { #1 } { #2 } { #3 }
 }

\NewExpandableDocumentCommand{\listitem}{mm}
 {% #1 = list name, #2 = item number
  \seq_item:cn { l_cis_list_#1_seq } { #2 }
 }

\NewExpandableDocumentCommand{\printlist}{m}
 {
  \seq_use:cn { l_cis_list_#1_seq } { ,~ }
 }

\seq_new:N \l__cis_createlist_tmp_seq
\tl_new:N \l__cis_createlist_tmp_tl

\cs_new_protected:Nn \cis_createlist:nnn
 {
  \seq_clear:N \l__cis_createlist_tmp_seq
  \bool_until_do:nn
   {
    \int_compare_p:nNn { \seq_count:N \l__cis_createlist_tmp_seq } = { #3 }
   }
   {
    \tl_set:Ne \l__cis_createlist_tmp_tl { \int_rand:n { #2 } }
    \seq_if_in:NVF \l__cis_createlist_tmp_seq \l__cis_createlist_tmp_tl
     {
      \seq_put_right:NV \l__cis_createlist_tmp_seq \l__cis_createlist_tmp_tl
     }
   }
  \seq_clear_new:c { l_cis_list_#1_seq }
  \seq_set_eq:cN { l_cis_list_#1_seq } \l__cis_createlist_tmp_seq
 }

\ExplSyntaxOff


\newcommand{\Nmax}{52}
\newcommand{\Nchoose}{12}

\begin{document}

\section{{\Nchoose} random numbers from \{1,2,\dots,\Nmax\}}
\createlist{first}{\Nmax}{\Nchoose}

\printlist{first}

\section{33 random numbers from \{1,2,\dots,60\}}
\createlist{second}{60}{33}

\printlist{second}

\section{52 random numbers from \{1,2,\dots,52\}}
\createlist{third}{52}{52}

\printlist{third}

\end{document}

If you need this to work also with TeX Live 2020, 2021 or 2022, add

\cs_generate_variant:Nn \tl_set:Nn { Ne }

after \ExplSyntaxOn.

If you want to support also TeX Live 2018 or 2019, you must

\usepackage{xparse}

enter image description here

3
  • 1
    the second strategy has no guaranteed termination though Apr 15 at 21:54
  • @DavidCarlisle Possibly. I could add a threshold for the attempts, say 1000 times Nmax.
    – egreg
    Apr 15 at 22:11
  • TYVM too! .............
    – cis
    Apr 20 at 18:09

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .