6

I am in the midst of my second attempt to create basic high school factoring worksheets.

Next up is figuring out how to reject unwanted zeroes and ones.

The following code generates simple trinomial equations but displays two unwanted behaviors.

  1. It creates unwanted difference of squares when, randomly, \b=-\d. I want these outcomes to be rejected and the code to simply rerun again.
  2. It prints coefficients of -1 and 1.

What is the easiest way to fix this?

Thanks!

\documentclass{article}

\usepackage[margin=1.2cm]{geometry}
\usepackage{ifthen}
\usepackage{multicol}
    \setlength{\columnsep}{1.5cm}
\usepackage{pgf}
\usepackage{pgffor}

\setlength{\parindent}{0pt}

\pagestyle{empty}

\pgfmathsetseed{\number\pdfrandomseed}


% How to make sure that we don't get a difference of squares, i.e. if \b=-\c, then the middle term will be zero and it's a DOS.
\newcommand{\InitVariables}
{%
 \pgfmathsetmacro{\b}{int(random(1,10)-11)}
 \pgfmathsetmacro{\d}{int(random(1,10))}
 \pgfmathsetmacro{\B}{int(\d+\b)}
 \pgfmathsetmacro{\C}{int(\b*\d)}
}

\newcommand{\trinomial}
{%
\InitVariables
\((x\b)(x+\d)=\ifnum \B <0 x^2{\B}x\C \else x^2+{\B}x\C\fi\)
}

\newcommand{\ManyTrinomials}[1]
{%
\foreach \x in {1,2,3,...,#1}
{\trinomial \\}
}

\begin{document}

\ManyTrinomials{50}

\end{document}

enter image description here

  • I thought this would be a reasonable way to prevent difference of squares, which it does, but it produces blank lines for the times when \B=0 twice in a row. Is there some way I could keep telling it to \InitVariables until \B0? \newcommand{\trinomial} {% \InitVariables% \ifnum\B=0\InitVariables% \else{\((x\b)(x+\d)=\ifnum\B<0x^2{\B}x\C \else x^2+{\B}x\C\fi\)}\fi% } – WeCanLearnAnything May 15 '17 at 18:36
  • Are you saying that none of the solutions below work? In my code \B is never 0 so I don't understand what the problem is. Is this a new question? – Andrew May 16 '17 at 0:27
  • The solutions below clearly do work. In fact, I just used your nested \ifnum code today. :) The only problem is that I am having a very hard time understanding much of the other code mentioned in the answers below. I will persist in trying to make sense of it, but I'm still a relative newb. – WeCanLearnAnything May 16 '17 at 2:51
6

To pick random distinct numbers \b and \d with \d in the range 1..10 and \d in the range -10,...,-1 I would use

 \pgfmathsetmacro{\d}{int(random(1,10)}
 \pgfmathsetmacro{\b}{int(random(1,9))}

now replace \b with -\b if \b<\d and with -1-\b otherwise.

With your printing issue with the coefficients you just need another \ifnum branch. In fact, you need two because rather than printing -1x you want to print -x.

Here's your MWE with these modifications:

\documentclass{article}

\usepackage[margin=1.2cm]{geometry}
\usepackage{ifthen}
\usepackage{multicol}
    \setlength{\columnsep}{1.5cm}
\usepackage{pgf}
\usepackage{pgffor}

\setlength{\parindent}{0pt}

\pagestyle{empty}

\pgfmathsetseed{\number\pdfrandomseed}


% How to make sure that we don't get a difference of squares, i.e. if \b=-\c, then the middle term will be zero and it's a DOS.
\newcommand{\InitVariables}
{%
 \pgfmathsetmacro{\d}{int(random(1,10)}
 \pgfmathsetmacro{\b}{int(random(1,9))}
 \ifnum\b<\d\pgfmathsetmacro{\b}{int(-\b)}%
 \else\pgfmathsetmacro{\b}{int(-1-\b)}%
 \fi
 \pgfmathsetmacro{\B}{int(\d+\b)}
 \pgfmathsetmacro{\C}{int(\b*\d)}
}

\newcommand{\trinomial}
{%
\InitVariables
\((x\b)(x+\d)=x^2 \ifnum\B<0\ifnum\B=-1-\else\B\fi\else+\ifnum\B>1\B\fi\fi x\C\)
}

\newcommand{\ManyTrinomials}[1]
{%
\foreach \x in {1,2,3,...,#1}
{\trinomial \\}
}

\begin{document}

\ManyTrinomials{50}

\end{document}

together with a random snapshot of the output:

enter image description here

Depending on what else you are doing it might be better to write a general macro for pretty printing polynomials.

EDIT

Here is a version with a \printpolynomial macro for "pretty printing polynomials". The macro takes comma separated list of coefficients, so

\printpolynomial{3,-2,1}% --> 3x^2-2x+1

Note that the macro assumes that it is in mathematics mode, so the above really ought to be $\printpolynomial{3,-2,1}$.

To do this the code first has a dry run through the coefficients to degree the maximum degree of the polynomial, called \polydeg -- actually, this is the degree plus one. It then goes through the coefficients again and prints them. A macro \plusseperator is sued to cope withe the annoying case when many of the leading coefficients are 0:

\printpolynomial{0,0,0,0,0,2,-2}%. -> 2x-2

In the extreme case, \printpolynomial{0,0,0,0,0} prints 0. IN addition to worrying about the coefficients being 1 and --1 there are additional complications with the exponents and in dealing with polynomials like \printpolynomial{1,1}, where the coefficient of the constant term does need to printed.

As a final bonus, the macro accepts an optional argument, which is the name of the determinant. So

\printpolynomial[y]{3,-4,2,1}%. --> 3y^3-4y^2+2y+1

The macro can be shortened a little if you print the polynomials with increasing exponents; that is, in the form a_0+a_1x+a_2x^2+...+a_nx^n.

Here is the revised code:

\documentclass{article}

\usepackage[margin=1.2cm]{geometry}
\usepackage{ifthen}
\usepackage{multicol}
    \setlength{\columnsep}{1.5cm}
\usepackage{pgf}
\usepackage{pgffor}

\setlength{\parindent}{0pt}

\pagestyle{empty}

\pgfmathsetseed{\number\pdfrandomseed}

% usage: \printpolnomial[name of indeterminate]{a_n, ... , a_1, a_0}
\newcommand\printpolynomial[2][x]{%
  % need to loop over #2 once to find the degree (actually, polydeg=degree+1)
  \foreach \coeff [count=\polydeg, remember=\polydeg] in {#2} {}
  \let\plusseperator\relax%  as the leading coefficients may be zero use this instead of +
  \foreach \coeff [count=\term, evaluate=\term as \pdeg using int(\polydeg-\term)] in {#2} {%
     \ifnum\coeff=0\relax% do nothing
     \else
       \ifnum\coeff>0\relax%
          \plusseperator% print a + if some terms have already appeared
          \ifnum\coeff=1\relax%
            \ifnum\pdeg=0\relax1\fi% print 1 if constant term
          \else\coeff% print coefficient if it is greater than 1
          \fi%
       \else\ifnum\coeff<0%
          \ifnum\coeff=-1\relax%
             \ifnum\pdeg=0\relax-1\else-\fi% print -1 if constant term
           \else\coeff% print \coeff
           \fi%
         \fi%
       \fi%
       \ifnum\pdeg=0\relax%
       \else%
         \ifnum\pdeg=1\relax #1\else {#1}^{\pdeg}\fi% print the power of #1 = x
       \fi%
       \gdef\plusseperator{+}% change \plusseperator to + as polynomial is non-zero
     \fi%
   }%
   \ifx\plusseperator\relax 0\fi% zero polynomial
}


% How to make sure that we don't get a difference of squares, i.e.
% if \b=-\d, then the middle term will be zero and it's a DOS.
\newcommand{\InitVariables}
{%
 \pgfmathsetmacro{\d}{int(random(1,10)}
 \pgfmathsetmacro{\b}{int(random(1,9))}
 \ifnum\b<\d\pgfmathsetmacro{\b}{int(-\b)}%
 \else\pgfmathsetmacro{\b}{int(-1-\b)}%
 \fi
 \pgfmathsetmacro{\B}{int(\d+\b)}
 \pgfmathsetmacro{\C}{int(\b*\d)}
}

\newcommand{\trinomial}
{%
\InitVariables
$(\printpolynomial{1,\b})(\printpolynomial{1,\d})
    = \printpolynomial{1,\B,\C}$
}

\newcommand{\ManyTrinomials}[1]
{%
\foreach \x in {1,2,3,...,#1}
    {\trinomial\\}
}

\begin{document}

$\printpolynomial{2,1,3,-2,0}$

$\printpolynomial{0,1,3,-2,0}$

$\printpolynomial{0,0,0,0,0}$

\ManyTrinomials{50}

\end{document}
  • Thank you for your awesome suggestions! How much more work is it to make this "general macro for pretty printing polynomials"? – WeCanLearnAnything May 15 '17 at 4:33
  • @WeCanLearnAnything I have added a macro for pretty printing polynomials – Andrew May 15 '17 at 6:02
  • Wow, thank you! I'm going to study that tonight. – WeCanLearnAnything May 16 '17 at 2:49
4

You can make coding a lot easier on yourself if you're willing to use the sagetex package documented here. In addition to the computing power of a computer algebra system you get the benefit of easy to program Python commands and even built in commands to make the output pretty through the latex() command. The line of code below containing latex(factor(poly)) tells Sage to factor the polynomial that was created and express the result in LaTeX code.

\documentclass{article}%
\usepackage{sagetex}
\begin{document}
\begin{sagesilent}
x = var('x')
Problems = r"Factor each quadratic:\\ \begin{enumerate}"
for i in range(0,20):
    r1 = Integer(randint(-10,10))
    r2 = Integer(randint(-10,10))
    while r1 == -r2:
        r2 = Integer(randint(-10,10))
    poly = expand((x-r1)*(x-r2))
    Problems += r"\item $%s = %s$"%(latex(poly),latex(factor(poly)))
Problems += r"\end{enumerate}"
\end{sagesilent}
\sagestr{Problems}
\end{document}

The output is shown running in SagemathCloud: enter image description here

And getting used to Sage and the sagemath package pays off in making all sorts of math worksheets easier--Sage handles all levels of math. You do need Sage installed locally on your computer OR a free SagemathCloud account.

  • This sounds absolutely incredible. My questions: Given that I know ~0 about Python, programming more generally, and Sage, do you still think it's worth learning? How hard is Sage to learn? – WeCanLearnAnything May 15 '17 at 4:16
  • +1 for using sagetex. It's worth adding that you need --shell-escape when using this from the command line. (At least, I think you do!) – Andrew May 15 '17 at 6:09
  • Yes, you will need shell escape if you rely on a local installation of Sage. However, if you use Sagemath Cloud then everything is done automatically as part of the compile process. – DJP May 15 '17 at 14:11
  • @WeCanLearnAnything I think Python is easier to learn/remember (with nicer syntax) than LaTeX programming. The important benefit for people with math uses are Sage commands that allow to factor numbers, test whether they are prime, integrate etc. You don't need to reinvent the wheel and Sage can check your math thereby eliminating mistakes. See, for example, my answer on matrices here where Sage calculates the transpose matrix and multiplies matrices. – DJP May 15 '17 at 14:25
  • Ok. Other people have told me Python is a lot easier than LaTeX as well. How hard is it to learn the basic functions (i.e. high school math) of the Sage package? – WeCanLearnAnything May 16 '17 at 2:52
4

Just for good measure, here's a pure LuaLaTeX-based solution. Other than the luacode package, no further packages are required to generate the required output.

enter image description here

A full MWE:

% !TeX program = lualatex
\documentclass{article}
\usepackage{amsmath} % for 'align*' env.

%% Lua-side code
\usepackage{luacode}
\begin{luacode}
function trinom ( n ) -- n: # of iterations (integer)
local f1,f2,g1,g2,s1
for i=1,n do -- Repeat the following code 'n' times

  -- f1 & f2: integers between -10 and 10, excl. 0
  repeat f1=math.random(-10,10) until f1~=0
  repeat f2=math.random(-10,10) until f2~=0 and f2~=-f1

  -- Compute additive and multiplicative factors 
  g1 = f1+f2 
  g2 = string.format("%+i", f1*f2) -- store as string

  -- Retrieve sign ("s1") of g1
  if g1<0 then s1 = "-" else s1 = "+" end
  g1 = tostring(math.abs(g1)) -- convert to abs. value
  -- Special treatment if abs(g1)=="1"
  if g1=="1" then g1 = "" end

  -- Generate output
  tex.sprint( "(x" .. string.format("%+i",f1) .. ")" ..
              "(x" .. string.format("%+i",f2) .. ")" ..
              "&= x^2" .. s1 .. g1 .. "x" .. g2 ) 
  -- Insert line-break directive if not (yet) at last line
  if n>1 and i<n then tex.sprint ( "\\\\" ) end 

end -- end of 'for' loop
end -- end of 'trinom' function
\end{luacode}

%% TeX-side code
\newcommand\ManyTrinomials[1]{\directlua{trinom(#1)}}

\begin{document}
\allowdisplaybreaks
\begin{align*}
\ManyTrinomials{30} % 30 rows
\end{align*}
\end{document}

Remark: If you don't want to embed the equations in an align* environment and, instead, simply want to print them out line by line, just change the chunk

     tex.sprint( "(x" .. string.format("%+i",f1) .. ")" ..
                 "(x" .. string.format("%+i",f2) .. ")" ..
                 "&= x^2" .. s1 .. g1 .. "x" .. g2 ) 

to

     tex.sprint( "(x" .. string.format("%+i",f1) .. ")" ..
                 "(x" .. string.format("%+i",f2) .. ")" ..
                 "= x^2" .. s1 .. g1 .. "x" .. g2 ) 

i.e., omit the & symbol.

  • 1
    Nice. (You could also move the loop to inside the "Lua-side code" (define a manyTrinomials Lua function that takes a number) and then the TeX-side \ManyTrinomials (if you need one at all) would just call the Lua function.) – ShreevatsaR May 15 '17 at 8:04
  • @ShreevatsaR - Thanks!. I've updated the code and incorporated your suggestion by moving the for loop into the body of the main Lua function. – Mico May 15 '17 at 11:14
3

Here's an expl3 version, though I haven't translated the \foreach loop here. That might make sense, I suppose, but it didn't occur to me earlier ....

So, on second thoughts, here's a non-quite-entirely-expl3 version:

\documentclass{article}
\usepackage[margin=1.2cm]{geometry}
\usepackage{enumitem,xparse}
\usepackage{multicol}
\setlength{\columnsep}{1.5cm}
\usepackage{pgffor}

\setlength{\parindent}{0pt}
\pagestyle{empty}

\ExplSyntaxOn
\int_new:N \l_wcla_b_int
\tl_new:N \l_wcla_b_tl
\int_new:N \l_wcla_d_int
\tl_new:N \l_wcla_d_tl
\int_new:N \l_wcla_B_int
\tl_new:N \l_wcla_B_tl
\int_new:N \l_wcla_C_int
\tl_new:N \l_wcla_C_tl
\cs_new_protected_nopar:Nn \wcla_init_variables:
{
  \int_zero:N \l_wcla_b_int
  \int_zero:N \l_wcla_d_int
  \int_do_while:nNnn { \int_abs:n { \l_wcla_b_int } } = { \int_abs:n { \l_wcla_d_int } }
  {
    \int_set:Nn \l_wcla_b_int { \fp_eval:n { randint ( -10 , -1 ) } }
    \int_set:Nn \l_wcla_d_int { \fp_eval:n { randint ( 1 , 10 ) } }
  }
  \int_set:Nn \l_wcla_B_int { \l_wcla_b_int + \l_wcla_d_int }
  \int_compare:nNnTF  { \l_wcla_B_int } < { 0 }
  {
    \tl_set:Nn \l_wcla_B_tl { \wcla_de_one:n {\l_wcla_B_int} }
  }{
    \tl_set:Nn \l_wcla_B_tl { + \wcla_de_one:n {\l_wcla_B_int} }
  }
  \int_set:Nn \l_wcla_C_int { \l_wcla_b_int * \l_wcla_d_int }
  \tl_set:Nn \l_wcla_C_tl { \int_to_arabic:n {\l_wcla_C_int} }
  \tl_set:Nn \l_wcla_b_tl { \int_to_arabic:n {\l_wcla_b_int} }
  \tl_set:Nn \l_wcla_d_tl { \int_to_arabic:n {\l_wcla_d_int} }
}
\cs_new_protected_nopar:Nn \wcla_trinomial:
{
  \group_begin:
    \wcla_init_variables:
    \(
      ( x \l_wcla_b_tl ) ( x + \l_wcla_d_tl ) = x^2 \l_wcla_B_tl x \l_wcla_C_tl
    \)
  \group_end:
}
\cs_new_protected_nopar:Nn \wcla_de_one:n
{
  \int_case:nnF { #1 }
  {
    { -1 } { - }
    { 1 } { }
  }{
    \int_to_arabic:n { #1 }
  }
}
\NewDocumentCommand \trinomial { }
{
  \wcla_trinomial:
}
\ExplSyntaxOff

\newcommand{\ManyTrinomials}[1]
{%
  \begin{itemize}[label={},wide,noitemsep]
    \foreach \x in {1,2,3,...,#1} {\item \trinomial}
  \end{itemize}%
}

\begin{document}

\ManyTrinomials{50}

\end{document}

not-quite-entirely-<code>expl3</code> version

  • I just tried to read part of the expl3 package pdf and I have absolutely no idea what it's saying. It's way over my head. Do you think it's worth it for me to learn it? – WeCanLearnAnything May 15 '17 at 4:31
  • I keep thinking that I should learn exp3...is it always so verbose? – Andrew May 15 '17 at 6:11
  • @WeCanLearnAnything No idea whether it is worth it for you. It was definitely worth it for me. Makes things much easier. Read interface3.pdf. Not all of it., obviously. I tried reading expl3.pdf first, I think, and it didn't help at all. – cfr May 16 '17 at 0:00
  • @Andrew I doubt it is always so verbose and it is probably sometimes more so. It is supposed to be intelligible and intelligibility is often more verbose, I guess. Also, it tries to reduce the chance of name clashes. But it is predictable. You can often guess the names of macros you've never used before, just by following the patterning. Above all, it is logical! – cfr May 16 '17 at 0:03
  • @Andrew The code would probably be shorter if somebody wrote it who knew what they're doing. You have to bear in mind that I don't. – cfr May 16 '17 at 0:04

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.