TeX Alchemist Online

TeX を使って化学のお仕事をしています。

TeX で乱数を使う (2) ~ ランダムなシャッフル

前回の記事では,TeX で乱数を生成するいろいろな方法を調べ,その使用例としてモンテカルロ・シミュレーションを見ました。本記事では,実際の組版の場面においてより実用的と思われる乱数の活用例を見てみましょう。

教材作成の仕事をしていると,周囲でしばしば「TeX で乱数って作れないんですか?」という質問を耳にします。前回の記事で見たように,TeX で乱数を生成する方法は多々あります。しかし,そのような質問をする教材作成関係者が思い描いている「乱数」というのは,乱数そのものではなく,「ランダムなシャッフル」である場面が多く見られます。要するに,「問題がランダムな順序で出題される試験問題を作りたい」という要望であるわけです。乱数の場合,1, 3, 3, 1, 3, 2,... のように,同じ値が何度も登場しますが,それをそのまま試験の問題番号としてしまうと,同じ問題が何度も出てきてしまって,試験としてふさわしくありません。あくまで,n個の問題を並べ替えた n! 通りの順列の中からランダムに1つの順列を選び出して試験として出題したい,というわけですね。

シャッフリングアルゴリズム

さて,n個のものをランダムな順序にシャッフルするには,どうすればよいでしょうか。これもまた Knuth 先生の The Art of Computer Programming を見てみましょう。The Art of Computer Programming, Vol.2, 3rd ed., §3.4 に,Algorithm P として,次のようなシャッフリングアルゴリズムが掲載されています。

f:id:doraTeX:20190116000732p:plain
出典:Donald E. Kunth, The Art of Computer Programming, Vol.2, 3rd ed., §3.4

このアルゴリズムを TeX 言語で実装してパッケージ化してみました。

仕様

  • TeXエンジンにビルトインされている疑似乱数生成プリミティブ \pdfuniformdeviate / \uniformdeviate を使用します。どちらかが実装されているエンジンならば使えます。
  • \randomshuffle[options]{first}{last} の形式で使うと,first 以上 last 以下の数がシャッフルされた数列が生成されます。
  • デフォルトでは,\shuffled という命令を介してシャッフル後の数列にアクセスできます。すなわち,\shuffled{1}\shuffled{2},……とすることでシャッフルされた数列の第1項,第2項,……が得られます(完全展開可能)。
  • デフォルトでは,\shuffledinverse という命令を介して逆置換が得られます(後述)。
  • options は keyval 形式で次のオプションが指定できます。
    • seed\pdfsetrandomseed / \setrandomseed に与える seed の値。出力を固定したい場合に指定する。
    • name : シャッフル後の数列にアクセスするための命令名(デフォルト:shuffled
    • inversesuffix:逆置換にアクセスするための命令名につける接尾辞(デフォルト:inverse

詳細な仕様

  • $ f(1)= \texttt{first}, f(2)= \texttt{first}+1, \ldots, f(n)= \texttt{last} $ とします。
  • \randomshuffle[options]{first}{last} とすることで,$\{1,2,\ldots,n\}$上の$n$次対称群 $\mathfrak{S}_n$ の元 $\sigma$ がランダムにチョイスされます。
  • \shuffled{1}, \shuffled{2}, ……, \shuffled{n} によって $f(\sigma(1)),f(\sigma(2)),\ldots, f(\sigma(n))$ の値が得られます。
  • \shuffledinverse{1}, \shuffledinverse{2}, ……, \shuffledinverse{n} によって $f(\sigma^{-1}(1)),f(\sigma^{-1}(2)),\ldots, f(\sigma^{-1}(n))$ の値が得られます。

特によく使うのは first が 1 のケースでしょう。この場合,$n=$last として

  • \shuffled{i}$=\sigma(i)\quad(i=1,\dots,n)$
  • \shuffledinverse{i}$=\sigma^{-1}(i)\quad(i=1,\dots,n)$

となります。

使用例

「問題ストックをインプットして,そこからランダムに数題をチョイスして問題と解答を出力する」という機能を持つ文書のコンパクトな一例を作ってみましょう。(サンプルの正誤問題は,2018年センター試験「化学」の選択肢から持ってきました。)

【コード例】

\documentclass[autodetect-engine]{jsarticle}
\usepackage{randomshuffle}
\makeatletter
\newcount\problemnum

\def\addProblem#1#2{%
  \advance\problemnum\@ne
  \@namedef{problem\the\problemnum}{#1}%
  \@namedef{answer\the\problemnum}{#2}%
}

\def\printProblem#1{\@nameuse{problem\shuffled{#1}}}
\def\printAnswer#1{\@nameuse{answer\shuffled{#1}}}
\makeatother

\addProblem{アルカリ土類金属は,すべて遷移元素である。}{×}
\addProblem{典型元素には,両性元素が含まれる。}{}
\addProblem{遷移元素は,すべて金属元素である。}{}
\addProblem{典型元素では,周期表の左下に位置する元素ほど陽性が強い。}{}
\addProblem{遷移元素には,複数の酸化数をとるものがある。}{}
\addProblem{密閉容器に入れてある物質が気液平衡の状態にあるとき,単位時間当たりに液体から蒸発する分子の数と,気体から凝縮する分子の数は等しい。}{}
\addProblem{無極性分子の気体が凝縮して液体になる現象には,分子間にはたらくファンデルワールス力が関わっている。}{}
\addProblem{純溶媒の沸点は,その純溶媒に不揮発性の溶質が溶けた溶液の沸点よりも低い。}{}
\addProblem{純物質は,三重点で気体・液体・固体が共存する平衡状態をとる。}{}
\addProblem{純物質は,液体の状態で凝固点より低い温度になることはない。}{×}
\addProblem{フッ素は,ハロゲンの単体の中で,水素との反応性が最も高い。}{}
\addProblem{フッ化水素酸は,ガラスを腐食する。}{}
\addProblem{塩化銀は,アンモニア水に溶ける。}{}
\addProblem{次亜塩素酸は,塩素がとりうる最大の酸化数をもつオキソ酸である。}{×}
\addProblem{ヨウ化カリウム水溶液にヨウ素を溶かすと,その溶液は褐色を呈する。}{}

% 1〜\problemnum をシャッフルして \shuffled でアクセス
\randomshuffle[name=shuffled]{1}{\problemnum}

\begin{document}

\section{問題}

次の各記述が正しければ○,誤っていれば×と答えよ。
\begin{enumerate}
\item \printProblem{1}
\item \printProblem{2}
\item \printProblem{3}
\item \printProblem{4}
\item \printProblem{5}
\end{enumerate}

\section{解答}

\begin{enumerate}
\item \printAnswer{1}
\item \printAnswer{2}
\item \printAnswer{3}
\item \printAnswer{4}
\item \printAnswer{5}
\end{enumerate}

\end{document}

【出力例】

インプットした問題群の中からランダムに5題チョイスして問題・解答を出力します。コンパイルごとに出力結果が変わります。 f:id:doraTeX:20190116011339p:plain

このアイデアは,いろいろな教材作成に役立つことでしょう。

次回は,逆置換を使った実用的応用例を考えてみましょう。