LuaTeXでルートの簡略化の計算問題の解答の作成(虚数のケースに対応)

以前の記事でルートの簡略化の問題を作るスクリプトを書いた。

もともと、これは二次方程式を解く問題の解答を自動生成するために作ったものである。よって、虚数にも拡張しておきたいという自然な欲求がある。そこで、虚数も混み(ルートの中身をマイナス)で作成したものをここに示す。

以下のような計算問題を考える。

f:id:baruku07:20180218222634p:plain

Luaの部分は次の通り。無駄に行数が多い・・・

-- 素因数分解。i番目の要素にiで何回割れるかが入る。結果として素因数のところ以外には全て0が入る。
function prime_division1(num) 
x = math.abs(num)
 ary={}
 if num>=0 then
   ary[1] =1
 else
   ary[1] = -1 -- 虚数の場合1を-1個かけられているという扱いをする。後の処理をきれいにするため。
 end
 for i=2,math.abs(num) do
   ary[i] = 0
    while x%i ==0 do
      ary[i] = ary[i] + 1
      x = math.floor(x/i)
    end
 end
 if num ==0 then --0の場合は1を0個のみ持つことにする。以下の処理がこれで不都合ないかは深く検討していない。(今後の課題)
  ary[1] =0
 end
  return ary --i番目にiが何回かけられている回数が格納されている。
end

-- 与えた数のルートの簡略化a\sqrt{b}を{a,b}の配列の形で返す
function kanyakuka(soinsuu_ary) ----i番目にiが何回かけられているかが格納されている配列を想定。
 a = 1
 b = soinsuu_ary[1] --実数なら1, 虚数なら-1
  for i=2, #soinsuu_ary do
   k = ary[i] -- 素因数iが何回掛けられているかを表している。
     if k>0 then
       a = a*i^(math.floor(k/2))
       b = b*i^(k%2) --割り切れる場合は0乗になって1がかけられて値不変。
     end     
  end
  return {a,b}
end

-- {a,b}の配列をa\sqrt{b}として出力。aやbには0以外の整数がくることが想定。
function root_exp(kanyakuka_ary)
  a = kanyakuka_ary[1]
  b = kanyakuka_ary[2]
  str = ""
  -- sqrt{b}の部分の生成
  function root_part(y) 
    str =""
    if math.abs(y) ~= 1 then -- +-1以外・ルート+-1は何も出力しない。
      str = "\\sqrt{" .. math.abs(y)  .. "}" -- 虚数の場合にマイナスを取り去る。実数ならそのまま。
    end
    if y<0 then --虚数であればをiくっつける
      str = str .. "i"
    end    
    return str
  end
  --aの部分の生成
  function int_part(x,y) 
    if (x==1 and y==1) or x~=1  then --1と整数部が1以外
      str =x
    else
      str =""
    end   
  return str   
  end
  str = int_part(a,b) .. root_part(b)
  return(str)
end

TeXの部分

\documentclass{ltjsarticle}
\usepackage{luacode}
\usepackage{amsmath}
\usepackage{tcolorbox}
\begin{luacode*}
-- ここに上のluaのコードが入る
\end{luacode*}

\newcommand{\gentoi}[1]{
 $\sqrt{#1 } $
 \begin{tcolorbox}[colframe=white,colback=white,width=2truecm,height=3pc,top=-2mm,visible] %
 \[
 \luaexec{tex.print(root_exp(kanyakuka(prime_division1(#1))))} 
 \]
\begin{gather*}
\end{gather*}   
 \end{tcolorbox}
}

\begin{document}

\begin{enumerate}
 \item \gentoi{-3}
 \item \gentoi{-16}
 \item \gentoi{-8}
 \item \gentoi{8}
 \item \gentoi{-1}
 \end{enumerate}
\end{document}

地味に書いていっているのでやたらに行数はかさんでいるがやっていることは単純で、ほぼ実数のみに対応するものと同じである。

  1. prime_division1で素因数分解
  2. kanyakukaでルートの外に出すべきものを外に出す
  3. root_expでa\sqrt{b}の形にする(aの文字列の生成部分とbの文字列の生成部分を内部で関数化して別々に考察している)

3.の部分に手を加えて虚数に対応した。いくつか簡単に説明しておく。

1. prime_division1

実数のみ考えるときに書いたものと同じく、配列のi要素に素因数iが何回かけられているかが入っている。ただし、マイナスの素因数に対応するため、マイナスの場合は1の個数が-1回かけられているとみなすことにした。

3. root_exp

a\sqrt{b}は色々と例外ルールがあり、それに対応する場合分けを書く必要がある。aがこうでbがこうで、とやっているとごちゃごちしていたので、aとbにわけて考えて後からくっつけることにした。

aについて

aについては、b=1のときのみに限り省略がaそのものを省略できる。

bについて

bの絶対値が平方数のときは、基本的にはルートそのものは一切書かなくて良い。

ただし、b=-1のときは、この部分にiを出力する必要がある。

キーボードの使用許可は手書きが厳しい人への配慮として機能しない場合がかなりありそう

高校の次期指導要領のパブコメ募集が出た。私の本業は情報であるが、国語の指導要領に特に注目していた。情報は、色々と理系風に高度化したがってるから、現状の何かを追い出さないといけない。その追い出し先が国語になりそうだったからである。期待としては、国語にメディアリテラシー風の話やプレゼンは丸投げ、あわよくば情報モラルは公共に丸投げ(これは失敗したが)というのがあった。期待通り、レポートとプレゼンは国語に丸投げに成功した。

私は社会科学出身の文系だが人文系ではないので、メディアリテラシーや文章・会話表現は教えたく無いのである。正確に言うと、教えられないことはないけど、国語教師の考える正しい国語に合わせて教えないとならないのがストレスなので嫌なのである。あとは、書くことの単発の指導ぐらいなら情報の人でもできるけど、基礎から積み重ねていって技能を習得させる指導については、やはり国語の方がプロなので効率が良い。よって、情報から消えてくれたほうが楽しい。

国語でPCが許されたとしても文字をキーボードで入力できるのか?

次期要領の国語の書くことは、期待以上であった。最もうれしかったのは、書くことの時間数をきちんと縛ってくれたことである。それも、「現代の国語」だけでなく「論理国語」までそうなった(追記注 : 現行でも国総は縛ってるけどスルーされている。古典ともセットなのでどうとでも逃げやすい。)。これで、学習指導要領を盾に「レポートとプレゼンは国語の仕事なので私は知りません(国語の免許はもってないし)」と言えるのである。嬉しい限りである。

しかし、押し付けられた国語は色々と大変であろう。例えば、この時間数で国語で書くことに取り組ませるとなると、手書きではきつい生徒も出てくると思われる。PCの使用を許可せざるを得ないケースもありそうである。だいたい、例示されている活動に「画像」というキーワードがちらっと見えた。画像を含む文章を手書きで扱うとも考えにくい。PCでやると考えるのが自然である。また、いくつもの例示されてる活動も、これは普通は手でやらないよな、というものが多数ある。

さて、今でも先進的な方々は、障害等に対する配慮としてPCによる文字入力の許可を要求している。しかし、それが仮に許されたとして、キーボードはそんなに簡単ではないぞと心の中で思っている私がいる。

以下、詳細に踏み込むことを避けるためにわかりにくい書き方になるかもしれないが、書ける範囲で思うことを書く。

仮に配慮を要する生徒に関してキーボード使用を認めたとしてですよ。本当にその人達タイピングもできるのか?私が見てきた傾向として、手書きが苦手な生徒は、タイピングの習熟も遅い傾向がある。あるいは、何らかの指導の工夫がないと伸びていかないという傾向もある。もちろん、例外も相当数あるので、やらせてみる価値は十二分にある。

タイピングの習熟が難しいケースに関しては、業務で様々なメニューを試してきた。それによって効果が上がったケースもそれなりにある。しかし、どうしようもないものはどうしようもないケースも存在することを痛感している。

超重症患者は手がまっすぐに置けない・指の曲げ伸ばしが難しい

習熟が遅いを通り越してしまったケースに共通する問題としては、キーに対して平行に指を置くことが難しい、というのがある。また、指を適切に曲げ伸ばしできないという問題も併発していることがほとんどである。この問題以外は時間をかけて、症状に応じた適切なメニューを実行させれば、クリアできることは実感している。

キーに平行に対して指が置けないケースに対して、安物のエルゴノミクスキーボードを試したケースもある。この方法で改善したケースもある。使ったキーボードが安物だったため、傾斜が適切でなく、それで合わないでだめとなったケースもあったが。

できないケースの様子を観察している感じだと、左右分離型のキーボードで自分に合った角度をつけてあげれば、なんとかなりそうという実感が得られている。しかし、お値段が2万円弱である。今の私の経済状況では試せない。

うまく行きにくい情報がもう少し表に出てきて欲しい

最近、私が思うことに、タイピングの練習の効果が上がりにくいケースが、もっと積極的に表に出てきて欲しいというのがある。今、インターネット上に落ちている情報の大半は「タッチタイピングは、適切な方法で適切な期間練習をすれば誰でも簡単にできる。最大のハードルは、練習方法の情報が行き渡ってないことと練習時間の確保である。」といったものである。私も数年前まではそれを信じていた。しかし、それがあてはまらないケースをそれなりに経験した今は、そんな無邪気なことは言えない。

こういった、タイピングは「いかなる人にも」簡単、という情報のみが氾濫する状況で困ることは、タイピングができないのは指導法が悪いとか本人の努力が悪いとかいうことになってしまうことである。困難な人には困難なんだから温かく見守ってあげないとね・・・

今は、タイピングをさらっと指導できるケースしか表に出てきていないと思われる。さらっとできないケースを無理して指導するインセンティブがゼロだからである。やらせていなければ、何の問題も表に出るわけもない。しかし、書くことの時間数が増えてきたら、さらっとタイピングが指導できないケースでもせざるを得ないシーンもでてきそうである。そうすると、もう少しタイピングが困難なケースが表に出てくるのではないかと期待している。

どうせ骨抜きか手書きかになりそうだけど

もっとも、国語の件は、ここまで過激に書かれたら「どうせやるの無理だから全部骨抜き」というのが、今のところの私の予想の本命である。

ただ、それでも「建前としては国語の責任」と言い切れる状況ができたことだけで私は十分(だけど社会全体としてはどうかと思うが)。

追記(2/18)

現在でも国総は書くことの時間数の明示はあった。冒頭のかき回しだと、現在は時間数縛りがない感じになるので良くない。確かに見た記憶もある。

情報の授業をするために、国総の教科書もそれなりに参考にしたことがあった。国総では、現行でも確かにそれなりに書くこと・話すことは存在するのは把握していた。ただし、指導要領本文側の縛り方が薄かったようなイメージがあったので、それに比べたらきつく縛ったなというイメージである。

そして、現代文Bでは書くことはスルーしても問題ないようになっている。さらに、現行では指導要領側(解説はともかく)に活動の例示もこんなにない。さすがにこの書き方では国語以外の人に「レポートとプレゼンは国語の仕事だ」とやるのは難しいのである。国語の先生に対してはは通用するものの、そういうのが通用する国語の先生は非常に優秀な方なので、そもそも最初から敵対しない。

現在の書き回しでは、書くことや話すことをスルーした国語をあまり責められないようなイメージがある。特に国表を開講していない場合はそうである。ぱっとみ、新の書き回しなら、これならレポート・プレゼンは国語の責任に完全にできるという印象を持った。よって、冒頭の書き回しである。

正直、国語がやるかどうかという実態はどうでも良いのである。情報が今後わけわからないことを教えたときに(絶対にそうなる。あんなの生徒が理解できるわけない。これはそのうち詳しく論じる。)、「もっとわかりやすく楽しいことを教えろ」とか「実社会で役立つレポート・プレゼンを教えろ」という声が絶対にでるに決まってる。そのときに、対生徒・対その他いろいろを突っぱねたいのである。(いつまでこの仕事するかわからないけど)私が、本来教えるべきことでないことについて、責任逃れできればそれで良いのである。

(生徒が理解しにくい)統計やモデリングを教えれば数学だと言われ、(生徒がやった感がでる)プレゼンを教えるのが情報だというの屁理屈はさっさと却下したいのである。

情報Iがどのようなものになっても「プレゼンは情報で面倒みろ」というわけわかめな理屈は出てくると思われる。それをはねのけようと思ったら、情報科の学習指導要領を盾に抵抗するのは弱い。誰でもわかる、主要教科の国語の指導要領を盾に取るしかない。その盾が大幅強化されたというのが今回の私の印象である。この盾であれば誰とも戦えると。

LuaTeXでルートの簡略化の計算問題と解答を生成

昨日の2次元方程式のファイルを、ルートの簡略化が発生するケースに拡張したいと考えている。その一歩として、ルートの簡略化をするためのコードを書いた。

結果として、次のような計算問題と解答までは作れるようになった。途中式はまだ。

f:id:baruku07:20180215224623p:plain

Luaのコード

-- 素因数分解。i番目の要素にiで何回割れるかが入る。結果として素因数のところ以外には全て0が入る。
function prime_division1(num) 
x = num
ary={}
ary[1] = 1 --1つ目の要素に1を詰めておかないと、2,3のときに正しい値を返さない
 for i=2,num do
   ary[i] = 0
    while x%i ==0 do
      ary[i] = ary[i] + 1
      x = math.floor(x/i)
    end
 end
  return ary
end

-- 与えた数のルートの簡略化a\sqrt{b}を{a,b}の配列の形で返す
function kanyakuka(soinsuu_ary) 
 a = 1
 b = 1
  for i=2, #soinsuu_ary do
   k = ary[i] -- 素因数の個数
     if k>0 then
       a = a*i^(math.floor(k/2))
       b = b*i^(k%2) --割り切れる場合は0乗になって1がかけられて値不変。
     end     
  end
  return {a,b}
end

-- {a,b}の配列をa\sqrt{b}として出力。
function root_exp(kanyakuka_ary)
  a = kanyakuka_ary[1]
  b = kanyakuka_ary[2]
  if    b == 1 then
    tex.print{a}
  elseif a == 1 then --これを最初のif節にすると\sqrt{1}が出力になるのでダメ。
    tex.print("\\sqrt{" .. b .. "}")
  else
    tex.print(a .. "\\sqrt{" .. b .."}")
  end
end

TeXのコード

\documentclass{ltjsarticle}
\usepackage{luacode}
\usepackage{amsmath}
\usepackage{tcolorbox}
\begin{luacode*}
--ここに上のluaのコード
\end{luacode*}

\newcommand{\kanyakuka}[1]{
 \luaexec{root_exp(kanyakuka(prime_division1(#1)))}  
}

\newcommand{\gentoi}[1]{
$\sqrt{#1}$
 \begin{tcolorbox}[colframe=white,colback=white,width=3truecm,height=4pc,visible] %
 \[
 \kanyakuka{#1}
 \]
 \end{tcolorbox}
}

\begin{document}

\gentoi{4}
\gentoi{12}
\gentoi{121}
\gentoi{128}

\end{document}

やっていることは

  1. prime_division1で素因数分解(配列で返す)
  2. kanyakukaで各素因数ごとにx\sqrt{y}の形を作り、xの部分のみををかけたものとyの部分をかけたものを作る。(と結果としてa\sqrt{b}の形が得られる)
  3. 2.をroot_expでtexでa\sqrt{b}の形で表現する。

である。以下1.と3.について簡単にコメント。

1. prime_division1で素因数分解

素因数をキーに、個数を値にしたテーブルを作るのが自然である。しかし、テーブルのキーは文字列として扱われるかもしれない(調べていない)。そのあたりを調べたくなかったのでこのように書いた。

大きい数を扱うつもりはない・速度は気にしない、ということなので、nまでの全ての整数を並べた配列を作っておき、そこに素因数の個数を格納する方針を取った。

2で割って割れるだけ割る -> 2割った回数を記録 -> 3で割って割れるだけ割る -> 3で割った回数を記録なので、単純に書ける。

2/16修正

prime_division1のループの終端をmath.floor(num1/2)をnumに修正。それに合わせて上の文も\sqrt{n}までをnまでに修正。最後の最後で、たぶんroot{n}までで大丈夫だろうと良く考えずにやったのが大うっかり。

素数の場合で考えると、明らかに自分自身の数に素因数の個数1が入らないとだめなので、自分自身の数までループは回すしかない。

3. root_exp

整数になるケースも含めて対応をする。1\sqrt{b}やa\sqrt{1}という表現はありえないので、それを回避するために簡単な場合分けを書いている。

感想(余談)

この問題も、emathにあるような気がしないでもない。うまくみつけられないが。

なお、素因数分解の問題もこれを少し工夫すればいけそうである。ただし、素因数分解はemathに既製品はある。

この問題も、一応は自作する価値が私にとってある。途中式を、素因数分解の筆算を横に並べるという形では書きたく無いということがあるからである。おおざっぱに、九九で分解して、それをさらに細かく分解していくというスタイルでの途中式を書きたい。

LuaTeXで解の公式の問題(実数解・ルートが残る・ルートの簡略化なし)の問題と解答を作る

昨日の話の続きのようなものである。

今度は2次方程式の問題のうち、解の公式を使って解くものの問題と解答を生成できるものを作った。今日も、LuaTeXで強引にやっている。そんなに無理してTeXを使わず既製品を使えという話ですが・・・ネタだから問題ないということで。

次のようなものを作りたい。 f:id:baruku07:20180214204248p:plain

以下がソースである。まずはLua

--- 文字式の係数の処理
function add_plus_dimzero(num) --定数項
  if num > 0 then
     return "+" ..  num
  else 
    return num
  end
end

function add_plus_dimhighest(num) --最高次
 if num == 1 then
   return ""
 elseif num == -1 then
   return "-"
 else
   return num
 end
end

function add_plus(num) -- 定数項と最高次以外
 if  num == 1 then
   return "+"
 elseif num ==-1 then
   return "-"
 elseif num > 0 then
   return "+" .. num
 else
   return num
 end
end

function gen_polyform(ary) -- 係数の配列(n次から並んでいる)から多項式を生成
  str =""
  -- n次(最高次)の処理
     n = #ary -1 
     str = str ..add_plus_dimhighest(ary[1]) .. "x^ " .. n
  -- n次, 1, 定数項以外
  for i=2, #ary -2 do
     n=n -1
     str = str ..add_plus(ary[i]) .. "x^" .. n
  end 
 --1次式の処理(x^1はだめだから別枠)
    str = str .. add_plus(ary[#ary - 1]) ..  "x"
  --定数項
    str = str .. add_plus_dimzero(ary[#ary])  
  tex.print(str)
end

---2次方程式の解の公式の形(bが偶数や虚数のケースなども視野に入れて、解答の形を整形するための関数は別に用意することにする。)
function kai_form(ary)
  tex.print("\\frac{" .. ary[1] .. " \\pm \\sqrt{" .. ary[2] .. "}}{" .. ary[3]  .. "}")
end

-- 文字式にマイナスの数値を入れるときの括弧の追加
function add_minus_kakko(num)
   if num<0 then
     return "(" .. num .. ")"
   else
     return num 
   end
end

-- 解の公式に単純に数値を代入しただけの途中式を作る
function dainyushiki(ary)
  arystr ={}
  for i=1, #ary do
    arystr[i] = add_minus_kakko(ary[i])
  end
  bunshi = "\\frac{-" .. arystr[2] .. " \\pm \\sqrt{" .. arystr[2] .. "^2-4\\cdot" .. arystr[1] .. "\\cdot" .. arystr[3] .. "}}"
  bunbo = "{2 \\cdot " .. arystr[2]  .. "}"
  tex.print(bunshi .. bunbo)
end

-- 計算を実行して解を出力する(ただし、判別式が平方数・ルートの簡略化が発生・虚数が発生するケースには対応していない)
function h(ary)
  bunsi1 =  -ary[2]
  disc      = ary[2]*ary[2] - 4*ary[1]*ary[3]
  bunbo  = 2*ary[1]
  kai_form({bunsi1,disc,bunbo})
end

TeXの部分。

\documentclass{ltjsarticle}
\usepackage{luacode}
\usepackage{amsmath}
\usepackage{tcolorbox}
\begin{luacode*}
%ここに上のluaのコードが入る
\end{luacode*}

\newcommand{\gentoi}[3]{
 $\luaexec{gen_polyform({#1,#2,#3})} = 0$
 \begin{tcolorbox}[colframe=white,colback=white,width=6truecm,height=8pc,visible] %
 %widthの設定の際には、(-)が幅を取ることを想定して設定する。
\begin{gather*}
  x = \luaexec{dainyushiki({#1,#2,#3})} \\
  x = \luaexec{h({#1,#2,#3})}
\end{gather*}   
 \end{tcolorbox}
}

\begin{document}

\begin{enumerate}
 \item \gentoi{1}{3}{-5}
 \item \gentoi{2}{7}{-7}
 \item \gentoi{1}{-7}{3}
 \item \gentoi{3}{5}{-5}
\end{enumerate}
\end{document}

特に説明するべき点もない。

Luaのコードのほとんどは多項式の生成のためのものである。

多項式の生成部分はemathのprPolynomialを使えば3秒である。しかし、このためだけにemathを読み込ませるのはあまりに重すぎる。よって、簡単に自作した。最終的には、素のoverleafで使えるようにしたい、という思いがあるので、多項式を作る関数を自作した。

途中式色々

途中式を触りたいので、このように自作をしている。これぐらい書いておけば少々さわれば思い通りの式が作れる。

例えば、私はb2の部分の代入は、符号を書かずに(どうせ2乗だから)いかなる場合も+と思い込んで計算せよと指示している。よって、途中式のマイナスは取りさってしまいたい。-(-b)も、それぐらいひっくり返すだけなんだから暗算しろという。

場合によっては、いきなりb2を計算した値を書けとか、4acぐらいも暗算して書けという指示をすることもある。その場合は、1式目に-(-b)・b2・4acの計算した結果が入る。

学力層が高く無い集団を教えるのであれば、b2の計算結果・4acの計算結果・2aの計算結果を書いた式をもう一行作ることもありえる。

道はまだ遠い

まだ、対応すべきケースが様々ある。

  • 虚数解が発生するケース
  • ルートの中が平方数(この場合は因数分解で解答を作るようにする)
  • ルートを簡略化するケース
  • 偶数のケース

いろいろと面倒そうなので、暇なときにやりたい。

係数の設定

問題を作るときには、適度に手計算できるように係数を設定する。この「適度な」を適切に定義し、その「適度な」係数パターンの全パターンを何らかの形で列挙したものを持っておきたい。そうすれば、そこから代入して問題を作るだけである。これも今後の課題である。

LuaTeXで簡単な2次方程式の問題と解答を作る

中学生的な数学を教えていると、ワンパターンの2次方程式を解く練習を大量に作りたいということがある。

LuaTeXを使って作ってみた。2次の係数は1・解は0か2解を足して0になる以外の整数・異なる2解を持つケースに対応している。前の2次関数のときと違い、方程式の1次の係数が-1と1になるケースについても対応した。

f:id:baruku07:20180212205953p:plain

以下がluaのコード。(色分けのためブロックをわけた)

function add_plus(num) -- +記号をつけて出力。定数項で使用。
 if num > 0 then
  return "+" .. num
 else
  return num
 end
end

function add_plus2(num) -- +記号をつけて出力・係数1を無視する。定数項以外で使用。
 if  num ==1 then
 return "+"
 elseif num ==-1 then
 return "-"
 elseif num > 0 then
  return "+" .. num
 else
  return num
 end
end

function h1(ary) --問題文の式
 x1_plus_x2 = ary[1] + ary[2]
 x1_times_x2 = ary[1]*ary[2]
 tex.print("x^2" ..add_plus2(-x1_plus_x2) .. "x" ..add_plus(x1_times_x2) .."=0")
end

function h2(ary) --因数分解の式の生成
 a = add_plus(-ary[1])
  b = add_plus(-ary[2])
 tex.print("(x" ..  a .. ")(x" ..  b ..")=0" )
end

以下がtexのソース。

\documentclass{ltjsarticle}
\usepackage{luacode}
\usepackage{amsmath}
\usepackage{tcolorbox}
\begin{luacode*}
%%ここに上のluaのコードが入る
\end{luacode*}

\newcommand{\gentoi}[2]{%
$\luaexec{h1({#1,#2})}$
\begin{tcolorbox}[colframe=white,colback=white,width=4truecm,height=5pc,left=-5truemm,top=-3truemm,visible]
\begin{gather*}
   \luaexec{h2({#1,#2})}\\
   x =  #1, #2
\end{gather*}
\end{tcolorbox}
}

\begin{document}
\begin{enumerate}
 \item \gentoi{3}{5}
 \item \gentoi{6}{-5}
 \item \gentoi{5}{-6}
 \item \gentoi{-7}{-5}
\end{enumerate}
\end{document}

この問題については、既製品は色々とある。既製品に対する利点は、細かい部分を自分で思うようにできるというだけである。定型パターンで対応できない集団を教えているときには、この点が大きい。

自分が定型的なパターンで対応できない集団を教えているときには、自分で自動生成をやろうと試行錯誤していた。

この手の2次方程式の生成は、Rubyで文字列を生成してから貼り付けをしていたこともあった。Rubyの機能を生かして、2解の候補の配列候補を生成しておいて、それからランダムに選んでプリントに貼り付けたこともある。しかし、コピーアンドペーストもだるいし、スクリプトファイルをどこに置いたか行方不明にもなることがある。TeXで済むならそれが良い。

LuaTeXで2次関数の頂点を計算する問題の解答を作る(2次の係数は1, 頂点整数のケース)

数学の授業を持っていると、自分の思う通りの途中式で解答を作りたくなることがある。特に、持っている学力層によっては問題集そのままの解答は使えないので、必然的にそうせざるをえない。解答は数値だけを見て、問題を解く方法は授業で教えた方法に読み替える、という行為は、相当に高度な行為なのである。

問題・解答の生成で面倒なのは、単純反復系・同系統の問題の大量作成である。学力層によってはこの作業が頻繁に発生する。この作業はそれなりに骨が折れる。解答をミスすることも良くある。

同系統の問題を大量生成するとき、TeXのマクロで途中式に数字をはめ込むぐらいのことは今までもしていた。しかし、計算もやってくれると助かる、と思うことはよくあった。そこで、Luaの力を借りて計算をしてみる方法を考えることとした。

手始めに2次関数の頂点を求める問題とその解答の作成を自動化してみた。現状は、2次の係数が1で頂点が整数になるケースのみしか扱えない。しかし、とりあえずは動くものができたので紹介。

f:id:baruku07:20180212160040p:plain

以下はソース。luaの部分。(色分けをさせるためにあえてブロックをわけている)

function add_plus(num) -- +記号をつけて出力
 if num > 0 then
  return "+" .. num
 else
  return num
 end
end

function calc_choten(ary) --頂点の計算
  ary1 ={}
  ary1[1] = -ary[1]/2
  ary1[2] = -(ary[1])^2/4 + ary[2]
  return ary1
end

function h1(ary) --問題文の式
 tex.print("x^2" ..add_plus(ary[1]) .. "x" ..add_plus(ary[2]) )
end

function h2(ary) --途中式1行目
 tex.print("(x" .. add_plus(ary[1]/2) .. ") ^2-" .. math.abs(ary[1])/2 .. "^2" .. add_plus(ary[2])   ) 
end

function h3(ary) --平方完成済の式
 choten_x = calc_choten(ary)[1]
 choten_y = calc_choten(ary)[2] 
 tex.print("(x" .. add_plus(-choten_x) .. ") ^2" .. add_plus(choten_y)  ) 
end

function h4(ary) --頂点の座標の出力
 choten_x = calc_choten(ary)[1]
 choten_y = calc_choten(ary)[2] 
 tex.print(choten_x .."," .. choten_y)
end

texの部分。

\documentclass{ltjsarticle}
\usepackage{luacode}
\usepackage{amsmath}
\usepackage{tcolorbox}
\begin{luacode*}
%%この部分に上のluaのコードを挿入
\end{luacode*}

\newcommand{\gentoi}[2]{%
$y=\luaexec{h1({#1,#2})}$
\begin{tcolorbox}[colback=white,colframe=white, width=7truecm,height=9pc,visible]
  \begin{align*}
   y &= \luaexec{h2({#1,#2})} \\
      &= \luaexec{h3({#1,#2})} 
  \end{align*}

よって、頂点は点$(\luaexec{h4({#1,#2})})$となる。
\end{tcolorbox}
}

\begin{document}
以下の2次関数のグラフの頂点の座標を求めよ。
\begin{enumerate}
 \item \gentoi{-4}{12}
 \item \gentoi{2}{-5}
 \item \gentoi{8}{6}
 \item \gentoi{-6}{-6}
\end{enumerate}


\end{document}

本体については、特に説明を要する部分はない。

なお、tcolorboxを使っているのは次の意図。

  • widthとheightの指定
  • visibleとinvisibleで問題と解答を切り替える

レイアウトは手抜き。問題の生成の例をみせるためだから。実際のプリントはもう少し丁寧にレイアウトして作る(たぶん問題を横に2つ並べると思う。)。

途中式は私の好みである。学力層によって、ここは書き分けるべきである。それをしたいからこそ、自分でマクロを書いているとも言える。いちおう、意図。

  • 2で割る式は省略。それはさすがに何をやっているか類推できる。
  • 1行目の2乗を引く式はあえて計算していない(()内の第2項と同じものの絶対値の2乗を引いている、ということを見せる。)
  • 2乗の計算結果と定数項との足し算は省略(学力次第ではこの行も必要となる。)

もう少し拡張したい

配列から多項式の式を組み立てる関数を書けば、2次の係数が-1のケースは扱えそう。2次の係数が2以上のケースは、途中式が変わってくるので手間が少しかかりそう。

頂点分数ぐらいまでは対応させたいけどな・・・

でも、このプリントを使うこともないのでいつになるかわからない。。。

LuaTeXで単回帰の係数を計算して本文に埋め込んだ

最近、業務で単回帰に関連した問題を作った。データと推定式を与えて、

  1. 残差二乗和の式
  2. 式の簡単な解釈を書かせる問題

の2つを書かせる問題である。この問題を作ろうとすると、データと回帰式の両方を問題文に書く必要がある。

業務では、与えたデータをRのlm関数に計算させて得た回帰係数を問題文に書いた。しかし、このようなコピペ作業は間違いが起こりやすい。単回帰の計算程度であれば、全てTeXの中で完結させたいと考えるのは自然な欲求である。

以下が問題のイメージ(設問は関係ないので略。書き方にツッコミは入れないでほしい。)。

f:id:baruku07:20180212023536p:plain

ソースは次の通り。

まずluaの部分から。これを以下のtexの\begin{luacode}以下に挟む。上品でない関数名や変数名は見なかったことにしてほしい。 (luaシンタックスハイライトを使うためにブロックをわけた)

--http://www.geocities.jp/m_hiroi/light/lua02.html sumを引用
function sum(ary)
  local total = 0
  for i = 1, #ary do
    total = total + ary[i]
  end
  return total
end

function mean(ary)
  return sum(ary) / #ary
end

function xysum(ary1,ary2)
 xy = {}
 meanx = mean(ary1)
 meany = mean(ary2)
 for i=1, #ary1 do
   xy[i] = (ary1[i] - meanx )* (ary2[i] - meany)
 end
 return sum(xy)
end

function f(ary1,ary2) --回帰式の出力
 meanx = mean(ary1)
 meany = mean(ary2)
 bunbo = xysum(ary1,ary1)
 bunshi = xysum(ary1,ary2)
 --係数の計算
 hatb = bunshi/bunbo
 hata = meany - (bunshi/bunbo)*meanx
 --出力
 tex.print(hatb ..  "x+" .. hata)
end

function output_tblrow(ary) --表の行出力
 for i=1, #ary -1 do
 tex.print(ary[i])
 tex.print("&")
 end
 tex.print(ary[#ary])
end

TeXの部分のコード。

\documentclass{ltjsarticle}
\usepackage{luacode}
\begin{luacode*}
%%上のluaのコードが入る
\end{luacode*}

\begin{document}
ある店の付近の温度と自動販売機の売り上げの関係について考える。以下のようなデータが得られたものとする。
%%データの配列を与える。
\def\datx{25,27,29,31,33}
\def\daty{70,73,77,78,80}
%%
\begin{center}
\begin{tabular}{lccccc}
 \hline
温度        & \luaexec{output_tblrow({\datx})} \\
\hline
売り上げ & \luaexec{output_tblrow({\daty})} \\
\hline
 \end{tabular}
\end{center}
最小二乗法によって$y=ax+b$の関係式を推定したところ($y$は売り上げ・$x$は温度)次のような関係が推定できた。
\[
 y = \luaexec{f({\datx },{\daty })}
\]

tabularの設定のlcccccも自動で出したかったが、なぜかうまくいかなかった。luaexecでlcccccの文字列を返す関数を書いたが、エラーを履いた。今後の課題である。

lua有理数を簡単に扱えるのであれば、もう少し色々な問題でこんな遊びができそうな気がするのだが。。。