答えを与えると、ランダムに式を生成するプログラム

そろばん甲子園:今年限りで廃止…競技人口の減少止まらず - 毎日jp(毎日新聞) にもあるように、そろばん人口が減ってきているそうだというニュースが NHK で流れていましたが、その中でそろばんの問題の式を出すプログラムを 100 万円出して購入したという話がありました。 10 年前くらいであれば、こうした勉強素材のソフトは高価で専門のプログラムが必要だったのかもしれないが、今では簡単に LL で代用することができます。

元ネタは 答えを与えると、ランダムに式を生成するプログラム - みずぴー日記ですが、上述のそろばんプログラムにも使えるのではないでしょうか。

#! /usr/local/bin/nawk -f
# expr.awk
# ランダムに式を生成
# usage: nawk -f expr.awk num [depth]


BEGIN {
    num     = ARGV[1];
    depth   = ARGV[2] ? ARGV[2] : 3;

    srand();
    print expand_expr(num, depth);
}

# expand_expr():    数式を展開して数値と演算子に分ける
#   in:     数式 (str)
#           展開する深さ (depth)
#   out:    数値 -> 数式
function expand_expr(str, depth,    num_str, new_str, arr_str) {
    num_str = split(str, arr_str);
    new_str = "";
    for (i = 1; i <= num_str; i++) {
        if (arr_str[i] ~ /[0-9]+/) {
            new_str = new_str " " calc_expr(arr_str[i]);
        } else {
            new_str = new_str " " arr_str[i];
        }
    }
    count++;
    if (count >= depth) {
        return new_str;
    } else {
        return expand_expr(new_str, depth);
    }
}

# calc_expr():  逆計算
#   in:     数値 (num)
#   out:    数式
function calc_expr(num,     num1, num2, expr) {

    # 足し算
    num1 = int_rand(num);
    num2 = num - num1;
    expr[1] = "( " num1 " + " num2 " )";

    # 引き算
    num2 = int_rand(num);
    num1 = num + num2;
    expr[2] = "( " num1 " - " num2 " )";

    # 掛け算
    do {
        num1 = int_rand(num);
        if (num1 == 0) {
            num1 = 1;
        }
    } while (num % num1 != 0)
    num2 = num / num1;
    expr[3] = "( " num1 " * " num2 " )";

    # 割り算
    num2 = int_rand(num);
    num1 = num * num2;
    expr[4] = "( " num1 " / " num2 " )";

    return expr[int_rand(4)];
}

# int_rand():   1 から数値 num までの整数の乱数を返す
#   in:     数値 (num)
#   out:    1 から数値 num までの整数の乱数
function int_rand(num) {
    return int(rand() * num) + 1;
}

こちらは四則演算をランダムで出力しているのですが、足し算だけといったようにすることも簡単にできます。

$ nawk -f expr.awk 123
 ( ( ( 1 * 5 ) - ( 4 - 2 ) ) * ( ( 7 + 20 ) + ( 14 * 1 ) ) )

深さも変えられます。

$ nawk -f expr.awk 123 6
 ( ( ( ( ( ( 1 + 0 ) * ( 2 - 1 ) ) + ( ( 1 / 1 ) - ( 1 * 1 ) ) ) / ( ( \
( 1 / 1 ) + ( 1 - 1 ) ) + ( ( 1 * 1 ) - ( 2 - 1 ) ) ) ) * ( ( ( ( 1 + 0\
 ) * ( 4 * 2 ) ) * ( ( 135 * 1 ) / ( 5 + 0 ) ) ) - ( ( ( 42 / 6 ) * ( 1\
 + 0 ) ) + ( ( 1 + -1 ) / ( 1 / 1 ) ) ) ) ) - ( ( ( ( ( 30 + 42 ) - ( 20\
 1 ) ) - ( ( 1 * 1 ) * ( 19 - 6 ) ) ) * ( ( ( 24 / 2 ) / ( 1 + 1 ) ) -\
 ( ( 2 - 1 ) * ( 3 * 1 ) ) ) ) - ( ( ( ( 1 + 0 ) + ( 3 * 1 ) ) + ( ( 1\
 + 16 ) - ( 1 * 5 ) ) ) + ( ( ( 1 / 1 ) * ( 44 - 21 ) ) - ( ( 96 / 6 )\
 - ( 32 / 4 ) ) ) ) ) )

ところで、合っているのでしょうか? 検算するのも一苦労ですが、こういう場合には bc コマンドに渡してやると検算ができます。

$ nawk -f expr.awk 123 6 | bc
123

というわけで、合っているようです。

tag_nawk.pngtag_nawk.pngtag_nawk.pngtag_nawk.png