mecobalamin’s diary

人間万事塞翁が馬

pythonで計算問題をつくる

小学校が休校になって一ヶ月以上になる
自宅学習をさせているけど
計算問題をこなす数が足りない気がしたので
pythonの勉強も兼ねてiPadのpythonisata3を使って
ランダムに計算問題を作るスクリプトを書いた

出力したいのは

  1. 2-3桁の掛け算
  2. あまりのある割り算
  3. 少数の足し算・引き算
  4. 分数の足し算・引き算

方針は2つの数について計算して
表示する数式を出力する関数を作る

実際の計算はノートにやってもらって
答え合わせのときに答えを出力させる

イメージはこんな感じ

計算の種類を選んで数字を入力してね
1: 少数のたし算・ひき算
2: 大きな数のかけ算
3: あまりのあるわり算
4: 分数のたし算・ひき算
数値を入力 (1 - 4): 1

1) 94.8 + 84.1 = ?
2) 37.0 + 19.1 = ?
3) 96.1 + 21.9 = ?

終わったらendを入力してね>>end

1) 94.8 + 84.1 = 178.9
2) 37.0 + 19.1 = 56.1
3) 96.1 + 21.9 = 118.0

操作と表示の流れは

  1. どの計算をするか選んで数字を入力
  2. 数式の表示
  3. 手書きで計算が終わったらendを入力
  4. 答えの表示

となる

問題は分数の表示
アスキーアートみたいに表示はできなくはないけど
できればTeXを使いたい
こんな感じ
f:id:mecobalamin:20200430163041p:plain

調べてみたら
matplotlibを使えば文字をLaTeX表示できるらしい
http://civilyarou.web.fc2.com/Tech_etc/sub_j_latex_test.html

グラフの軸を消してタイル表示させれば
等間隔に数式を並べられる
matplotlibでAxesを真っ白にする(x軸とかy軸なんかを消して非表示にする) - 静かなる名辞
matplotlibのめっちゃまとめ - Qiita

初めてclassを自作した
この本を参考にした
ゼロから作るDeep Learning ―Pythonで学ぶディープラーニングの理論と実装
(Amazonに飛びます)

classには数式を作るメソッドを定義した
数式を作るときに計算の答えも計算してあって
問題の式、答えを含む式の2つを作る

PythonistaではTeXのコマンドが使える程度っぽい
表示はあまりきれいでない

デスクトップでTeX表示する場合は

rc('text', usetex=True)

を使うと表示がきれいになる

実際のコードはこちら
libraryのconsoleはPythonisata3のライブラリなので
デスクトップで実行する場合は削除する
math_exam.py

import console

import matplotlib.pyplot as plt
from matplotlib.ticker import *
from matplotlib import rc
import random
import numpy as np

from math_class import math_equations

#rc('text', usetex=True)
console.clear()
console.set_font('Menlo',20)

math = math_equations()

x = 3
y = 2

n = x * y

print('計算の種類を選んで数字を入力してね')
print('1: 少数のたし算・ひき算')
print('2: 大きな数のかけ算')
print('3: あまりのあるわり算')
print('4: 分数のたし算・ひき算')
while True:
	eq_type = input('数値を入力 (1 - 4): ')
	if eq_type > '0' and eq_type < '5':
		break

ans = []
if eq_type == '1':
	for j in range(0, int(n/2)):
		r = np.array(math.generate_decimal(n, 2, 2, 1))
		r[0][j], r[1][j] = math.replace_num(r[0][j], r[1][j])
		tmp = math.eq_summation(r[0][j], r[1][j])
		ans.append(tmp[1])
		print(str(j + 1) + ') ' + tmp[0])
		if (j + 1) % 5 == 0:
			print()

	for j in range(int(n/2), n):
		r = np.array(math.generate_decimal(n, 2, 2, 1))
		r[0][j], r[1][j] = math.replace_num(r[0][j], r[1][j])
		tmp = math.eq_subtraction(r[0][j], r[1][j])
		ans.append(tmp[1])
		print(str(j + 1) + ') ' + tmp[0])
		if (j + 1) % 5 == 0:
			print()

elif eq_type == '2':
	for j in range(n):
		r = np.array(math.generate_int(n, 2, 2, 1))
		r[0][j], r[1][j] = math.replace_num(r[0][j], r[1][j])
		tmp = math.eq_multiplication(r[0][j], r[1][j])
		ans.append(tmp[1])
		print(str(j) + ') ' + tmp[0])
		if (j + 1) % 5 == 0:
			print()

elif eq_type == '3':
	for j in range(n):
		r = np.array(math.generate_int(n, 1, 2, 1))
		r[0][j], r[1][j] = math.replace_num(r[0][j], r[1][j])
		r[0][j] = random.randrange(r[1][j],10 * r[1][j])
		tmp = math.eq_division(r[0][j], r[1][j])
		ans.append(tmp[1])
		print(str(j) + ') ' + tmp[0])
		if (j + 1) % 5 == 0:
			print()

elif eq_type == '4':
	fig, axes = plt.subplots(x, y)

	m = 0
	for i in range(x):
		for j in range(0, int(y/2)):
			r = np.array(math.generate_int(n, 2, 2, 1))
			r[0][j], r[1][j] = math.replace_num(r[0][j], r[1][j])
			tmp = math.eq_fraction_sum(r[0][j], r[1][j], r[2][j])
			ans.append(tmp[1])
			axes[i, j].axis('off')
			axes[i, j].text(0.0, 0.5, str(m + 1) + ') ' + tmp[0], fontsize = 20)
			m += 1
		for j in range(int(y/2), y):
			r = np.array(math.generate_int(n, 2, 2, 1))
			r[0][j], r[1][j] = math.replace_num(r[0][j], r[1][j])
			tmp = math.eq_fraction_sub(r[0][j], r[1][j], r[2][j])
			ans.append(tmp[1])
			axes[i, j].axis('off')
			axes[i, j].text(0.0, 0.5, str(m + 1) + ') ' + tmp[0], fontsize = 20)
			m += 1
	plt.show()
	print()

while True:
	inp=input('終わったらendを入力してね>>')
	print()
	if inp == 'end':
		break

if eq_type != '4':
	for i in range(n):
		print(str(i + 1) + ') ' + ans[i])
		if (i + 1) % 5 == 0:
			print()
else:
	fig, axes = plt.subplots(x, y)
	m = 0
	for i in range(x):
		for j in range(y):
			axes[i, j].axis('off')
			axes[i, j].text(0.0, 0.5, str(m + 1) + ') ' +ans[m], fontsize = 20)
			m += 1

	plt.show()

math_class.py

import random

class math_equations:
	def __init__(self):
		pass

	def eq_summation(self, a, b):
		re = a + b
		eq = str(a) + ' + ' + str(b) + ' = '
		return eq + '?', eq + str(re)

	def eq_subtraction(self, a, b):
		re = a - b
		eq = str(a) + ' - ' + str(b) + ' = '
		return eq + '?', eq + str(re)

	def eq_multiplication(self, a, b):
		re = a * b
		eq = str(a) + ' × ' + str(b) + ' = '
		return eq + '?', eq + str(re)

	def eq_division(self, a, b):
		re = a // b
		su = a % b
		eq = str(a) + ' ÷ ' + str(b) + ' = '
		return eq + '?', eq + str(re) + '・・・' + str(su)

	def eq_fraction_sum(self, a, b, c):
		re = a + b
		if a != c and b != c:
			eq = r'$\frac{' + str(a) + '}{' + str(c) + '} + \\frac{' + str(b) + '}{' + str(c) + '} = '
		elif a == c:
			eq = r'$1 + \frac{' + str(b) + '}{' + str(c) + '} = '
		elif b == c:
			eq = r'$\frac{' + str(a) + '}{' + str(c) + '} + 1 = '
		return eq + '?$', eq + '\\frac{' + str(re) + '}{' + str(c) + '}$'

	def eq_fraction_sub(self, a, b, c):
		re = a - b
		if a != c and b != c:
			eq = r'$\frac{' + str(a) + '}{' + str(c) + '} - \\frac{' + str(b) + '}{' + str(c) + '} = '
		elif a == c:
			eq = r'$1 - \frac{' + str(b) + '}{' + str(c) + '} = '
		elif b == c:
			eq = r'$\frac{' + str(a) + '}{' + str(c) + '} - 1 = '
		return eq + '?$', eq + '\\frac{' + str(re) + '}{' + str(c) + '}$'

	def replace_num(self, a, b):
		if a < b:
			tmp = a
			a = b
			b = tmp
		return a, b

	def generate_int(self, n, dim_x, dim_y, dim_z):
		a = []
		b = []
		c = []
		for i in range(n):
			a.append(random.randrange(10 ** (dim_x - 1), 10 ** dim_x))
			b.append(random.randrange(10 ** (dim_y - 1), 10 ** dim_y))
			c.append(random.randrange(2, 99))

		return a, b, c

	def generate_decimal(self, n, dim_x, dim_y, dim_z):
		a = []
		b = []
		c = []
		for i in range(n):
			a.append(round(random.uniform(10 ** (dim_x - 1), 10 ** dim_x), 1))
			b.append(round(random.uniform(10 ** (dim_y - 1), 10 ** dim_y), 1))
			c.append(round(random.uniform(10 ** (dim_z - 1), 10 ** dim_z), 1))

		return a, b, c

実際に触らせてみたら
数字を選ぶところで
画面を触ってた。。。。

さすがデジタルネイティブ

Pythonistaはアプリも作れる
Pythonistaの使い方まとめ - みやびのどっとぴーわい

そのうちタッチ操作できるようにできたらいいね

21 May 2020 追記
この記事のコードを少し書き直した
問題を選ばせるところ
新しく記事を書いたのでリンクを貼る
mecobalamin.hatenablog.com

追記ここまで

30 November 2020 追記
書き直してみた
共通する変数があるときに同じモジュール内に記述する

class MathEquations:
    def __init__(self, a, b, c, d, equation_type):
        self.a = a
        self.b = b
        self.c = c
        self.d = d
        self.equation_type = equation_type

    def exam_equation(self):

        kotae = ''
        amari = ''
        shiki = ''

        if self.equation_type == '1':
            kotae = self.a + self.b
            shiki = r'$\displaystyle' + str(self.a) + " + " + str(self.b) + ' = $'
            kotae = r'$' + str(kotae) + '$'

        elif self.equation_type == '2':
            kotae = round(self.a * self.b, 1)
            shiki = r'$\displaystyle' + str(self.a) + '\\times' + str(self.b) + ' = $'
            kotae = r'$' + str(kotae) + '$'

        elif self.equation_type == '3':
            kotae = round(self.a // self.b, 1)
            amari = self.a % self.b
            shiki = r'$\displaystyle' + str(self.a) + '\\div' + str(self.b) + ' = $'
            kotae = r'$' + str(kotae) + '\\cdots' + str(amari) + '$'

        elif self.equation_type == '4':
            kotae = self.a + self.b
            shiki = r'$\displaystyle\frac{' + str(self.a) + '}{' + str(self.c) + '} + \\frac{' + str(self.b) + '}{' + str(self.c) + '} = $'
            kotae = r'$\displaystyle\frac{' + str(kotae) + '}{' + str(self.c) + '}$'

        return([shiki, kotae, self.d])
|
class GenerateNumbers:
    def __init__(self, n, x, y, z):
        self.n = n
        self.x = x; self.y = y; self.z = z
        self.a = []; self.b = []; self.c = []

    def generate_int(self):
        for i in range(self.n):
            self.a.append(random.randrange(10 ** (self.x - 1), 10 ** self.x))
            self.b.append(random.randrange(10 ** (self.y - 1), 10 ** self.y))
            self.c.append(random.randrange(2, 99))
        return(self.a, self.b, self.c)

    def generate_decimal(self):
        for i in range(self.n):
            self.a.append(random.uniform(10 ** (self.x - 1), 10 ** self.x))
            self.b.append(random.uniform(10 ** (self.y - 1), 10 ** self.y))
            self.c.append(random.uniform(10 ** (self.z - 1), 10 ** self.z))
        return(self.a, self.b, self.c)