10

I have 58 images in PDF format. At the moment, each of them is in a separate PDF, such as 1.pdf, 2.pdf etc. I would like to include those 58 images in a random order without repetition.

I found the 'random order without repetitition' part: Generating random numbers without repetitions

So very candidly, this is what I did (minimal code):

\documentclass[a4paper,12pt]{article} 
\usepackage{tikz}

\begin{document}

%here is the bit from the link above to randomly draw a number from a list of integers without repetition
\def\declarenumlist#1#2#3{%
    \expandafter\edef\csname pgfmath@randomlist@#1\endcsname{#3}%
    \count@\@ne
    \loop
    \expandafter\edef
    \csname pgfmath@randomlist@#1@\the\count@\endcsname
    {\the\count@}
    \ifnum\count@<#3\relax
    \advance\count@\@ne
    \repeat}

%I want 58 numbers because I have 58 images
\declarenumlist{total}{1}{58}

%I try to include my pdf
\includegraphics{\pgfmathrandomitem\z{total}\z.pdf}

\end{document}

Of course, this does not work, the reason being I suppose '\includegraphics should be a file name (after macro expansion), not the set of instructions to compute it', as explained in How to include random images?. I tried to follow the route suggested in this question, by doing a

\newcommand{\myrandomnumber}{\pgfmathrandomitem\z{total}\z}
\includegraphics{\myrandomnumber.pdf}

But this does not work either.

Note that, if needed, I can keep my 58 images in the same pdf (they are pdflatex generated) and use a \includepdf[pages={\pgfmathrandomitem\z{total}\z}]{images.pdf}, but this does not work either, I think for the same reasons.

Does anyone know what's going on (I would like to understand) and then how to fix my problem? Any help would be very much appreciated.

darpich
  • 319
  • 1
    Welcome to TeX.SX! You can't (and shouldn't) redefine \number, which is a very important TeX primitive. – egreg Nov 14 '13 at 17:01
  • Thanks! This is what I thought and I've used several variable names in my file, I've just used \number here because this was the first variable name that came to my mind... I will update my question with a less contentious variable name. – darpich Nov 14 '13 at 17:03

1 Answers1

9

You should put \makeatletter and \makeatother around the definition of \declarenumlist and move \pgfmathrandomitem\z{total} outside the argument to \includegraphics like in

\pgfmathrandomitem\z{total}\includegraphics{\z.pdf}

However I would recommend a different definition of \declarenumlist that doesn't depend on the actual way a list for random choice is internally built by TikZ/PGF.

In the code below the list of numbers from the lowest bound to the highest bound is generated and fed as argument to \pgfmathdeclarerandomlist. The loop is very similar, but, as said, the macro will continue to work even if TikZ/PGF changes the internals of these lists.

Next I define an interface for \includegraphics: the \randomincludegraphics command accepts options to pass to \includegraphics and its mandatory argument is the list name.

Since I don't have the files to play with, I just hid the real macro to call and used a mock text for showing the effect.

\documentclass[a4paper,12pt]{article} 
\usepackage{tikz}

\begin{document}

\makeatletter \def\declarenumlist#1#2#3{% \begingroup \count@=#2\relax \def\x{}% \loop \edef\x{\x{\the\count@}}% \ifnum\count@<#3\relax \advance\count@@ne \repeat \edef\x{\endgroup \noexpand\pgfmathdeclarerandomlist{#1}{\x}% }\x } \makeatother

\newcommand{\randomincludegraphics}[2][]{% \begingroup \pgfmathrandomitem\z{#2}% % Uncomment the following line for the production version
% \includegraphics[#1]{\z}% % and remove the following line I want to include \texttt{\z.pdf} with options ``\texttt{#1}''% \endgroup }

%I want 58 numbers because I have 58 images \declarenumlist{total}{1}{58}

\randomincludegraphics{total}

\randomincludegraphics{total}

\randomincludegraphics[height=3cm,width=1cm]{total}

\end{document}

enter image description here


Here's a version that avoids repetitions; if it's called more times than the available items, random repetitions are used (with a warning). You have to redeclare the list, if you want to use it another time.

\documentclass[a4paper,12pt]{article} 
\usepackage{tikz}

\begin{document}

\makeatletter \def\declarenumlist#1#2#3{% @ifundefined{c@#1@listcount}{\newcounter{#1@listcount}}{}% \setcounter{#1@listcount}{1}% \begingroup \count@=#2\relax \def\x{}% \loop \edef\x{\x{\the\count@}}% \global\expandafter\let\csname#1@\number\count@ @used\endcsname\relax \ifnum\count@<#3\relax \advance\count@@ne \stepcounter{#1@listcount}% \repeat \edef\x{\endgroup \noexpand\pgfmathdeclarerandomlist{#1}{\x}% }\x \expandafter\mathchardef\csname #1@number\endcsname\value{#1@listcount}% \setcounter{#1@listcount}{0}% }

\newcommand{\pgfmathuniquerandomitem}[2]{% \pgfmathrandomitem#1{#2}% \ifnum\value{#2@listcount}=@nameuse{#2@number}% @latex@warning{List #2 exhausted}% \else @ifundefined{#2@#1@used}% {\stepcounter{#2@listcount}\global@namedef{#2@#1@used}{used}}% {\pgfmathuniquerandomitem#1{#2}}% \fi }

\makeatother

\newcommand{\randomincludegraphics}[2][]{% \begingroup \pgfmathuniquerandomitem\z{#2}% % Uncomment the following line for the production version
% \includegraphics[#1]{\z} % and remove the following line I want to include \texttt{\z.pdf} with options ``\texttt{#1}'' \endgroup }

%I want 58 numbers because I have 58 images \declarenumlist{total}{1}{58}

\randomincludegraphics{total}

\randomincludegraphics{total}

\randomincludegraphics[height=3cm,width=1cm]{total}

\foreach\i in {1,...,58} { \randomincludegraphics{total}\endgraf }

\declarenumlist{total}{1}{58} % redeclare the list \foreach\i in {1,...,58} { \randomincludegraphics{total}\endgraf }

\end{document}

Update 2022

There are much better ways now to do the same task.

\documentclass[a4paper]{article}

\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{\randomincludegraphics}[2][]{% % Uncomment the following line for the production version
% \usefromrandomlist{#2}{\includegraphics[#1]{##1} % and remove the following line \usefromrandomlist{#2}{I want to include \texttt{##1.pdf} with options ``\texttt{#1}''} }

\begin{document}

\declarerandomlist{total}{5}

\randomincludegraphics{total}

\randomincludegraphics{total}

\randomincludegraphics[height=3cm,width=1cm]{total}

\myforeach{5}{\randomincludegraphics{total}\par}

\end{document}

The main difference, apart from the names, is that

\usefromrandomlist

takes as arguments the list name and a template where #1 stands for the current item in the random list, so one can do

\usefromrandomlist{total}{\includegraphics{#1.pdf}}

or abstract the command (like in the code above), remembering that #1 has to become ##1.

enter image description here

egreg
  • 1,121,712
  • Wow! This is absolutely perfect! It is exactly what I wanted to achieve! Thank you so much! May I ask a simple question to understand correctly what is going on: what is the \z for? I could not find documentation on this, probably because looking for 'latex z' in Google is not likely to yield good results... – darpich Nov 15 '13 at 11:35
  • @darpich The \z is just a scratch macro; in order to avoid possible problems with it having a meaning, I put its definition (made by the call \pgfmathrandomitem\z{total}) inside a group, so this won't clobber any value the macro can have. You could use \sOmEsTrAnGeCs or anything else, just ensure that the name is not shared by macros used in the processing of those specific instructions \pgfmathrandomitem and \includegraphics. – egreg Nov 15 '13 at 11:39
  • Oh I see, so if I understand correctly, writing \pgfmathrandomitem\z{total} stores the result of \pgfmathrandomitem{total} into the variable \z, which you can then call later. Is that right? – darpich Nov 15 '13 at 12:52
  • @darpich Yes; other \pgfmath... macros do the same. – egreg Nov 15 '13 at 13:09
  • But that's brilliant! Thanks again, I've learned two great things today! – darpich Nov 15 '13 at 13:34
  • Huh, actually, it seems that it is not without repetition... I've checked carefully and some images are inserted twice. Could it be because I try to include the images with a \foreach \n in {1,2,...,58}{\randomincludegraphics{total}}? – darpich Nov 15 '13 at 14:58
  • @darpich I don't think that \pgfmathrandomitem avoids repetitions; neither the other version of \declarenumlist ensures it. I'll add a way to get uniqueness. – egreg Nov 15 '13 at 15:04
  • Yes, with the other version of \declarenumlist in http://tex.stackexchange.com/questions/109619/generating-random-numbers-without-repetitions there's still repetition, but I've just realised that @DavidCarlisle also defines a \prunelist which I will try to use in my case. – darpich Nov 15 '13 at 15:11
  • So, I tried to use the \prunelist but I still have repetitions, so there's probably something I am not doing right. – darpich Nov 15 '13 at 15:21
  • How I can save the random value in macro please – defdarry Jan 28 '23 at 21:23