仕事算やその関連問題は分数の計算が必要になるので、答えが分数(や小数)になりやすい。採点の効率化のため答えが整数になるような問題を作成したいが、どうすればそのような値を探し出せるだろうか。
例題
作りたい問題はこんな感じ。小学生向けの文章題以外にも様々なバリエーションができる。
答えは本記事の最後に。
仕事算
ある廊下の掃除をひとりで行うと、Aさんは12分、Bさんは15分、Cさんは20分かかる。この3人が一緒に掃除した場合は何分で完了するか。
電気回路
値が 100Ω, 120Ω, 150Ω の3つの抵抗器を並列に繋いだときの合成抵抗はいくらか。
(コンデンサの場合は直列にすることで同様の問題を作れる)
追いつき算
兄弟が公園を同じルートで何周かジョギングする。1周に要する時間は、兄は10分、弟は14分である。兄弟が出会うのは何分毎になるか。
(惑星の会合周期などへ応用できる)
幾何学
仕事算という括りではないが、計算方法が同じようになる問題が幾何学にも色々ある。
レンズの公式
ある凸レンズの左側 24cm の距離に光源を置いたところ、レンズの右側 40cm の位置に倒立の実像が現れた。このレンズの焦点距離はいくらか。
(符号をうまく取り扱えば、虚像や凹レンズにも対応できる)
直角三角錐の高さ
角 O が全て 90° の直角三角錐 OABC がある。辺の長さは OA = 90mm, OB = 150mm, OC = 200mm である。頂点 O から平面 ABC に垂線をおろすとき、その長さはいくつか。
(デカルト・グアの定理1と相補的であり、他の次元への適用も見通しが良い)
その他
調和平均を参照。
基本的な解法
手計算
これらの問題では仕事の能率の和を考えればいい。能率というのは時間の逆数で考えられる2ため、各仕事時間を R1, R2, … 、協力したときを R で表せば、次の式が成り立つ。
\frac{1}{R} = \frac{1}{R_1} + \frac{1}{R_2} + \cdots
全 Ri が整数である場合の計算テクニックとして、右辺で分数の足し算を避けるために、全 Ri の公倍数(仮に V とする)を両辺に掛ける方法がある。とはいえ、そうすれば右辺は整数になるものの、その総和が先ほどの公倍数 V を割り切れなければ、答えは分数となってしまう。
R = \frac{V}{\frac{V}{R_1} + \frac{V}{R_2} + \cdots}
プログラム
Qiitaなので一応プログラムでも計算する。Rubyでサクッと解いてみる。
$ ruby -v
ruby 2.6.0rc1 (2018-12-06 trunk 66253) [x86_64-linux]
$ ruby -e 'puts (1 / ARGV.sum { |s| 1 / Complex(s) }).real' -- 12 15 20
5
わざわざ複素数クラス Complex
で計算しているのは、内部で実数クラス( Integer
, Rational
, Float
)を適切に選んで処理してくれるから。上記の例では引数に整数のみを与えているが、分数や小数を表す文字列を与えても計算できる。詳細はRubyの込み入った話になってしまうので、別記事で説明する。
二項演算と結合法則
3つ以上の仕事の能率の和を考える場合、実は二項演算さえ定義してあれば、逐次演算していくことで求められる。
R_i \star R_j := \frac{R_i R_j}{R_i + R_j} \\
\downarrow \\
R = R_1 \star R_2 \star \cdots
結合法則を満たすのでどこから計算を実行してもいいし、さらに交換法則も満たすので順序を入れ替えてもいい。
\begin{align}
100 \star 120 \star 150
&= (100 \star 150) \star 120 \\
&= 60 \star 120 \\
&= 40
\end{align}
例題で色々出した通り結構便利な演算なので、電卓を自作した際に入れておいたことがある。足し算や掛け算と同じ要領で、何個の演算でも連続して使える。
本題:問題の作成
考え方
値を直接探すのではなく、先に能率を決めてしまうことで必ず整数を見つけられる。
- 例えば個々の能率を 2 と 3 とする。
- すると合計の能率は 2 + 3 = 5 である。
- これらの値の最小公倍数を計算する。合計の能率も含めるのがポイント。 lcm(2,3,5) = 30
- 最小公倍数をそれぞれの能率で割れば、仕事算の問題と答えが整数で得られる。この例では問題が 30/2 = 15 と 30/3 = 10 、答えが 30/5 = 6 である。
最小公倍数を使ったため最も小さな値が求まっているが、これら全ての値を等倍しても構わない。例えば4倍すれば、 60, 40 → 24 という問題を作れる。
個々の能率は3個以上でもいいし、マイナスでもいい。ただし最小公倍数が0になる組み合わせはダメ。
良い値の探索
能率に小さな値を選んだからといって問題の値が小さくきれいになるわけではない。例えば 2 と 5 を選ぶと、和は 7 のため最小公倍数は 70 、よって問題は 35, 14 → 10 となる。7の倍数であることが気持ち悪い(※個人的な意見)。3つ以上の数となると意図通りの問題を作るのは難しい。
なので可能な値を列挙してしまい、そこから良さそうなものを目視で探すことにする。
M = 3 # 協力の数
N = 50 # 探索する能率の最大値
list = [*1..N].combination(M) # 同じ値があってもよければ #repeated_combination を使う
.select { |ary| ary.inject(0, :gcd) == 1 } # 公約数がある組合せはスキップ
.map do |ary|
s = ary.sum
m = ary.inject(s, :lcm)
ary.map { |e| m / e } << (m / s) # 能率から仕事へ変換(配列に追加した値は答え)
end
# 最大仕事時間が小さく、能率の開き(比)が小さく、協力時間が長い順に列挙
list.sort_by { |ary| [ary[0], ary[0].fdiv(ary[-2]), -ary[-1]] }
.take(30).each(&method(:p))
[6, 3, 2, 1]
[15, 12, 10, 4]
[15, 10, 6, 3]
[15, 10, 3, 2]
[18, 12, 9, 4]
[18, 9, 3, 2]
[20, 15, 12, 5]
[20, 12, 5, 3]
[20, 5, 4, 2]
[24, 8, 6, 3]
[24, 8, 3, 2]
[28, 24, 21, 8]
[28, 21, 12, 6]
[28, 14, 7, 4]
[28, 21, 6, 4]
[28, 21, 4, 3]
[30, 18, 9, 5]
[30, 24, 8, 5]
[30, 10, 5, 3]
[30, 20, 4, 3]
[33, 22, 11, 6]
[35, 21, 15, 7]
[35, 15, 14, 6]
[35, 14, 10, 5]
[36, 28, 21, 9]
[36, 30, 20, 9]
[36, 18, 4, 3]
[40, 30, 15, 8]
[40, 35, 14, 8]
[40, 20, 8, 5]
能率の最大値 N
を100に増やしてもoutputに変化は無かったので、仕事算に使える小さな候補は上記くらいだろう。あとは作りたい問題に合わせて、値の比率を考慮して選んだり3、それを適当な倍率で大きくすればいい。
例題の答え
- 仕事算:5分
- 電気回路:40Ω
- 追いつき算:35分(※弟の仕事をマイナスと考える)
- 幾何学
- レンズの公式:15cm
- 直角三角錐の高さ:72mm(※全ての数を2乗して考える)