我有 58 张 PDF 格式的图片。目前,每张都在单独的 PDF 中,例如等1.pdf
。2.pdf
我想以随机顺序包含这 58 张图片,且不重复。
我找到了‘随机顺序,不重复’部分:生成无重复的随机数
坦率地说,这就是我所做的(最少的代码):
\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}
当然,这是行不通的,原因是我认为'\includegraphics 应该是一个文件名(宏扩展之后),而不是计算它的指令集',如如何包含随机图像?。我尝试按照这个问题建议的路线,通过做一个
\newcommand{\myrandomnumber}{\pgfmathrandomitem\z{total}\z}
\includegraphics{\myrandomnumber.pdf}
但这也不起作用。
请注意,如果需要,我可以将我的 58 张图像保存在同一个 pdf 中(它们是由 pdflatex 生成的)并使用\includepdf[pages={\pgfmathrandomitem\z{total}\z}]{images.pdf}
,但这也不起作用,我想是出于同样的原因。
有人知道发生了什么事吗(我想了解一下)以及如何解决我的问题?任何帮助都将不胜感激。
答案1
你应该把\makeatletter
和\makeatother
放在定义周围\declarenumlist
,并移到\pgfmathrandomitem\z{total}
参数之外,就像\includegraphics
在
\pgfmathrandomitem\z{total}\includegraphics{\z.pdf}
然而,我建议一个不同的定义,\declarenumlist
它不依赖于 TikZ/PGF 内部构建随机选择列表的实际方式。
在下面的代码中,生成从最低界限到最高界限的数字列表并将其作为参数输入\pgfmathdeclarerandomlist
。循环非常相似,但如前所述,即使 TikZ/PGF 更改了这些列表的内部结构,宏仍将继续工作。
接下来我定义一个接口\includegraphics
:该\randomincludegraphics
命令接受要传递的选项\includegraphics
,其强制参数是列表名称。
由于我没有可以使用的文件,我只是隐藏了要调用的真正宏并使用模拟文本来显示效果。
\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}
这是一个避免重复的版本;如果调用的次数超过可用项数,则使用随机重复(带有警告)。如果您想在其他时间使用它,则必须重新声明列表。
\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}
2022 年更新
现在有更好的方法来完成同样的任务。
\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}
除了名称之外,主要的区别在于
\usefromrandomlist
以列表名称和模板作为参数,其中#1
代表随机列表中的当前项,因此可以执行
\usefromrandomlist{total}{\includegraphics{#1.pdf}}
或者抽象命令(如上面的代码),记住必须#1
变成##1
。