Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

I am looking for a LaTeX package that will allow me to generate an exam with questions drawn from a particular question bank. Each question within the bank would be a self-contained block of LaTeX code.

For example, in the spirit of the exam package, I might have the questions:

\question
$2+2=$
\begin{choices}
\choice 3
\choice 0
\choice 4
\choice $\sqrt{2}$
\choice $-\pi$
\end{choices}

\question
$\int_0^1 x^2\,dx=$
\begin{choices}
\choice $-1$
\choice $1/3$
\choice $\infty$
\choice $1/2$
\choice None of the above.
\end{choices}

This would be a bank containing two questions. Each question is a block of LaTeX code that, if it were to be "drawn" from the bank and inserted into a "parent", compilable LaTeX file, would thereby generate an exam (presumably what such a package would do).

Being greedy, I'd really like if I could specify the number of questions $q_1$ to be drawn from question bank $B_1$, $q_2$ from $B_2$, etc. where each bank $B_i$ would be over a specific topic.

If this already exists, I have not been able to find it. Preserving the functionality of the exam documentclass (or something like it) would make assigning points and/or generating answer keys simultaneous with (random) exam creation.

share|improve this question
1  
I assume, since the question is somewhat general, that you're open to an input adjustment. For example, instead of \question, you could potentially be okay with wrapping each question inside a bank in an environment, like \begin{question}...\end{question}. –  Werner May 2 at 17:09
    
Sure thing! I am open to anything that works. Also, say I had 20 questions in the bank. It would be important for me to be able to specify that I want, say, 5 questions (pseudo)randomly chosen from the bank. –  JohnD May 2 at 17:10
1  
I have no doubt that a system for generating exams like this could be implemented in LaTeX, but wouldn't a short Perl script accomplish the same thing more simply? –  Senex May 2 at 17:11
    
Perhaps, but I am not familiar with Perl. My motivation was to take the existing functionality of the exam documentclass and just automate things via a question bank. –  JohnD May 2 at 17:14
1  
exsheets all the way –  Jubobs May 2 at 17:45

1 Answer 1

Let's assume you have a bank of questions in bankA.tex that has the following format:

\begin{questionblock}
Question 1
\end{questionblock}
\begin{questionblock}
Question 2
\end{questionblock}
\begin{questionblock}
Question 3
\end{questionblock}
\begin{questionblock}
Question 4
\end{questionblock}
\begin{questionblock}
Question 5
\end{questionblock}
\begin{questionblock}
Question 6
\end{questionblock}
\begin{questionblock}
Question 7
\end{questionblock}
\begin{questionblock}
Question 8
\end{questionblock}
\begin{questionblock}
Question 9
\end{questionblock}
\begin{questionblock}
Question 10
\end{questionblock}

It's important to denote each question by some form of block/environment (questionblock in this case). Ultimately you would keep each bank inside a separate file, say bankA.tex, bankB.tex, ... There should be no restriction on what can be included inside the questionblock environment.

Our algorithm for generating a set of random questions from this bank proceeds as follows:

  1. Read file and store in a macro (thanks to catchfile;

  2. Count how many questions are inside the bank (using environ to process the entire questionblock environment as a \stepcounter mechanism). Call this number totalquestions;

  3. Create a random list from 1 to totalquestions (as reference, see Generating random numbers without repetitions));

  4. Repeat the following steps:

    • Pick a number from the random list;

    • Process the entire bank until you reach the question that matches the picked number and print it;

    • Prune the random list by removing the selected number.

The above procedure may do a large amount of extra processing, but I'm confident it won't be prohibitively intensive. Here's a complete minimal example for one bank:

enter image description here

\documentclass{article}
\usepackage{multicol}% Just for this example
\usepackage{filecontents}
\begin{filecontents*}{bankA.tex}
\begin{questionblock}
Question 1
\end{questionblock}
\begin{questionblock}
Question 2
\end{questionblock}
\begin{questionblock}
Question 3
\end{questionblock}
\begin{questionblock}
Question 4
\end{questionblock}
\begin{questionblock}
Question 5
\end{questionblock}
\begin{questionblock}
Question 6
\end{questionblock}
\begin{questionblock}
Question 7
\end{questionblock}
\begin{questionblock}
Question 8
\end{questionblock}
\begin{questionblock}
Question 9
\end{questionblock}
\begin{questionblock}
Question 10
\end{questionblock}
\end{filecontents*}

\usepackage{catchfile,environ,tikz}

\makeatletter% Taken from http://tex.stackexchange.com/q/109619/5764
\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}
\def\prunelist#1{%
  \expandafter\xdef\csname pgfmath@randomlist@#1\endcsname
          {\the\numexpr\csname pgfmath@randomlist@#1\endcsname-1\relax}
  \count@\pgfmath@randomtemp 
  \loop
    \expandafter\global\expandafter\let
    \csname pgfmath@randomlist@#1@\the\count@\expandafter\endcsname
    \csname pgfmath@randomlist@#1@\the\numexpr\count@+1\relax\endcsname
    \ifnum\count@<\csname pgfmath@randomlist@#1\endcsname\relax
      \advance\count@\@ne
  \repeat}
\makeatother

% Define how each questionblock should be handled
\newcounter{questionblock}
\newcounter{totalquestions}
\NewEnviron{questionblock}{}%

\newcommand{\randomquestionsfrombank}[2]{%
  \CatchFileDef{\bank}{#1}{}% Read the entire bank of questions into \bank
  \setcounter{totalquestions}{0}% Reset total questions counters  ***
  \RenewEnviron{questionblock}{\stepcounter{totalquestions}}% Count every question  ***
  \bank% Process file  ***
  \declarenumlist{uniquequestionlist}{1}{\thetotalquestions}% list from 1 to totalquestions inclusive.
  \setcounter{totalquestions}{#2}% Start the count-down
  \RenewEnviron{questionblock}{%
    \stepcounter{questionblock}% Next question
    \ifnum\value{questionblock}=\randomquestion 
      \par% Start new paragraph
      \BODY% Print question
    \fi
  }%
  \foreach \uNiQueQ in {1,...,#2} {% Extract #2 random questions
    \setcounter{questionblock}{0}% Start fresh with question block counter
    \pgfmathrandomitem\randomquestion{uniquequestionlist}% Grab random question from list
    \xdef\randomquestion{\randomquestion}% Make random question available globally
    \prunelist{uniquequestionlist}% Remove picked item from list
    \bank% Process file
  }}

\begin{document}

\begin{multicols}{3}
  \foreach \x in {1,...,6} {
    \bigskip
    \randomquestionsfrombank{bankA.tex}{6}
  }
\end{multicols}

\end{document}

A call to \randomquestionsfrombank{<file>}{<num>} picks <num> questions at random from <file>. If you don't want to process the <file> the first time around to get the number of questions, you can remove the lines highlighted with ***.

share|improve this answer
    
I'd do something like this with a scripting language, like Perl or Python –  vonbrand May 3 at 10:44
    
@werner: Could you please tell me why I need to wait about 30 seconds to generate another set of questions? –  Vahid Damanafshan Dec 14 at 11:36

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.