2

My goal is to define \avar and \bvar such that they are integers between 1 and 11 inclusively but share no common factors and they should never be equal.

I think that the code I've written should do that, but sometimes it does not and I don't know why.

Is there a fundamentally easier way to do this? Or is there an easy way to fix the code I've got?

\documentclass{article}


\usepackage{pgf}
\usepackage{pgffor}
\usepackage{multicol}


\pgfmathsetseed{\number\pdfrandomseed}


\newcommand{\InitVariablesDOS}
{%
 % This randomly sets \avar to be a whole number between 1 and 10.
 % I am using the list format so that when I'm not doing a MWE,
 % I can easily weight, say, 7s to come more often.
 % Let's say in our example \avar is randomly set to 6....
 \pgfmathdeclarerandomlist{avar}{{1}{2}{3}{4}{5}{6}{7}{8}{9}{10}}
 \pgfmathrandomitem{\avar}{avar}

 % If \avar=6 I want \bvar to share no common factors (other than 1) with 6.
 % So, \bvar is selected from {DOSIfSix},
 % which are whole numbers which do not have the factors 2,3, or 6.
 \pgfmathdeclarerandomlist{DOSIfOne}{{1}{2}{3}{4}{5}{6}{7}{8}{9}{10}}
 \pgfmathdeclarerandomlist{DOSIfTwo}{{1}{3}{5}{7}{9}}
 \pgfmathdeclarerandomlist{DOSIfThree}{{1}{2}{4}{5}{7}{8}{10}}
 \pgfmathdeclarerandomlist{DOSIfFour}{{1}{3}{5}{7}{9}}
 \pgfmathdeclarerandomlist{DOSIfFive}{{1}{2}{3}{4}{6}{7}{8}{9}}
 \pgfmathdeclarerandomlist{DOSIfSix}{{1}{5}{7}{11}}
 \pgfmathdeclarerandomlist{DOSIfSeven}{{1}{2}{3}{4}{5}{6}{8}{9}{10}}
 \pgfmathdeclarerandomlist{DOSIfEight}{{1}{3}{5}{7}{9}}
 \pgfmathdeclarerandomlist{DOSIfNine}{{1}{2}{4}{5}{7}{8}{10}}
 \pgfmathdeclarerandomlist{DOSIfTen}{{1}{3}{7}{9}{11}}

 \ifcase\avar\relax% This should define \bvar so that it shares no common factors with \avar. 
  \or \pgfmathrandomitem{\bvar}{DOSIfOne}
  \or \pgfmathrandomitem{\bvar}{DOSIfTwo}
  \or \pgfmathrandomitem{\bvar}{DOSIfThree}
  \or \pgfmathrandomitem{\bvar}{DOSIfFour}
  \or \pgfmathrandomitem{\bvar}{DOSIfFive}
  \or \pgfmathrandomitem{\bvar}{DOSIfSix}  
  \or \pgfmathrandomitem{\bvar}{DOSIfSeven}
  \or \pgfmathrandomitem{\bvar}{DOSIfEight}
  \or \pgfmathrandomitem{\bvar}{DOSIfNine}
  \or \pgfmathrandomitem{\bvar}{DOSIfTen}
 \fi
 \xdef\bvar{\bvar}
}


\newcommand{\avarandbvar}
{%
\InitVariablesDOS
\(avar=\avar\)

\(bvar=\bvar\)

\vspace{0.5cm}
}

\newcommand{\manyavarandbvars}[1]{\foreach \x in {1,...,#1} {\avarandbvar}}


\pagestyle{empty}


\begin{document}
\begin{multicols}{5}
\manyavarandbvars{70}
\end{multicols}
\end{document}

enter image description here

6
  • It is sitting inside a loop, did you try making \avar global too ?
    – percusse
    Jul 22 '17 at 14:49
  • I don't really know what your comment means. I need \avar and \bvar to be generated a whole bunch of times so I can make series of math exercises. Jul 22 '17 at 14:51
  • 3
    you are declaring \bvar to be global via \xdef. Do the same thing to \avar
    – percusse
    Jul 22 '17 at 14:54
  • Wow, I think that fixed it! But why did that work? ... and what does 'global' really mean? [I copy/pasted \xdef into my code weeks ago due to advice, but I never really knew what it meant/did.] Jul 22 '17 at 14:56
  • 1
    Euclid's algorithm (en.wikipedia.org/wiki/Euclidean_algorithm) is the usual test for common roots. Jul 22 '17 at 16:12
3

For comparison, here is a luatex based solution. (I use ConTeXt, but it should be straightforward to translate this to LaTeX as well). To make it more challenging, I generate the list of bvars algorithmically.

I wasn't sure why some lists went until 10, while others went until 11. In the code below, all lists go until 10.

\startluacode
  -- Euclid's algorithm for gcd
  local gcd = function(m, n)
    while m ~= 0 do
      m, n = math.mod(n,m), m
    end
    return n
  end

  local avars = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
  local bvars = {}

  -- Generate bvars
  for i = 1, #avars do
    local a = avars[i]
    if bvars[a] == nil then
      local b = {1}
      for j = 2, 10 do
         if gcd(a, j) == 1 then
            b[#b + 1] = j
          end
      end
      bvars[a] = b
    end
  end

  local random, format = math.random, string.format

  local sampleAandB = function()
    local i = random(#avars)
    local a = avars[i]
    local j = random(#bvars[a])
    local b = bvars[a][j]

    return a, b
  end

  local avarandombvar = function()
    local a, b = sampleAandB()
    context.dontleavehmode()
    context.framed({"frame=off, align=middle"}, format("avar = %s \\crlf bvar = %s", a, b))
    context.blank{"big"}
  end

  interfaces.definecommand ("avarandombvar", {macro = avarandombvar})

\stopluacode

\starttext

\startcolumns[n=5]
\dorecurse{70}{\avarandombvar}
\stopcolumns

\stoptext

which gives

enter image description here

1

First the solution for your pronlem:

You need to preserve \avar to keep it from changing. This can be done adding \edef\avar{\avar} after \pgfmathrandomitem{\avar}{avar}.

As a recommendation: you can move the declarations of the random lists into the preamble. This way, they are defined only once and not every time \InitVariablesDOS is used. And you should comment out the line ends of several lines in \InitVariablesDOS. Otherwise they can produce spurios spaces, depending on where the macro is called.

\documentclass{article}


\usepackage{pgf}
\usepackage{pgffor}
\usepackage{multicol}


\pgfmathsetseed{\number\pdfrandomseed}
\pgfmathdeclarerandomlist{avar}{{1}{2}{3}{4}{5}{6}{7}{8}{9}{10}}
\pgfmathdeclarerandomlist{DOSIfOne}{{1}{2}{3}{4}{5}{6}{7}{8}{9}{10}}
\pgfmathdeclarerandomlist{DOSIfTwo}{{1}{3}{5}{7}{9}}
\pgfmathdeclarerandomlist{DOSIfThree}{{1}{2}{4}{5}{7}{8}{10}}
\pgfmathdeclarerandomlist{DOSIfFour}{{1}{3}{5}{7}{9}}
\pgfmathdeclarerandomlist{DOSIfFive}{{1}{2}{3}{4}{6}{7}{8}{9}}
\pgfmathdeclarerandomlist{DOSIfSix}{{1}{5}{7}{11}}
\pgfmathdeclarerandomlist{DOSIfSeven}{{1}{2}{3}{4}{5}{6}{8}{9}{10}}
\pgfmathdeclarerandomlist{DOSIfEight}{{1}{3}{5}{7}{9}}
\pgfmathdeclarerandomlist{DOSIfNine}{{1}{2}{4}{5}{7}{8}{10}}
\pgfmathdeclarerandomlist{DOSIfTen}{{1}{3}{7}{9}{11}}

\newcommand{\InitVariablesDOS}
{%
 \pgfmathrandomitem{\avar}{avar}%
 % added as workaround
 \edef\avar{\avar}%
 % to show the problem, uncomment the next line and comment out the previous one
% \(avar=\avar\)

 \ifcase\avar\relax% This should define \bvar so that it shares no common factors with \avar. 
  \or \pgfmathrandomitem{\bvar}{DOSIfOne}%
  \or \pgfmathrandomitem{\bvar}{DOSIfTwo}%
  \or \pgfmathrandomitem{\bvar}{DOSIfThree}%
  \or \pgfmathrandomitem{\bvar}{DOSIfFour}%
  \or \pgfmathrandomitem{\bvar}{DOSIfFive}%
  \or \pgfmathrandomitem{\bvar}{DOSIfSix}%
  \or \pgfmathrandomitem{\bvar}{DOSIfSeven}%
  \or \pgfmathrandomitem{\bvar}{DOSIfEight}%
  \or \pgfmathrandomitem{\bvar}{DOSIfNine}%
  \or \pgfmathrandomitem{\bvar}{DOSIfTen}%
 \fi
 \edef\bvar{\bvar}%
}


\newcommand{\avarandbvar}
{%
\InitVariablesDOS

\(avar=\avar\)

\(bvar=\bvar\)

\vspace{0.5cm}
}

\newcommand{\manyavarandbvars}[1]{\foreach \x in {1,...,#1} {\avarandbvar}}


\pagestyle{empty}


\begin{document}
\begin{multicols}{5}
\manyavarandbvars{70}
\end{multicols}
\end{document}

Now the reason for the problem:

In this case, it has nothing to do with groups and/or making \avar global. The problem comes from pgf and how it defines a macro with \pgfmathrandomitem. If the latter is called for different macros, e.g.

\pgfmathrandomitem{\avar}{avar}
\pgfmathrandomitem{\bvar}{bvar}

the second call will also change the result of the first. Therefore, the first result has to be preserved with \edef\avar{\avar}.

The details (you may stop reading here):

The macro is simply defined with \def (pgfmathfunctions.random.code.tex, lines 241 and 242):

\pgfmathrandominteger{\pgfmath@randomtemp}{1}{\pgfmath@randomlistlength}%
\def#1{\csname pgfmath@randomlist@#2@\pgfmath@randomtemp\endcsname}}}

As can be seen, the contents of the macro depends on \pgfmath@randomtemp. If it changes, the macro changes too. And this will happen every time \pgfmathrandomitem is called, even if this is done for a different macro from a different list.

Since I can't think of a reason for using \def instead of \edef here, which would prevent this problem, I would consider this a bug.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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