* 逆ポーランド記法の問題を大量生産する

とある友人に頼まれたのでやっつけで作った。

逆ポーランド記法に関してはこちらを参照。

Wikipedia: 逆ポーランド記法

スタックの概念を理解するために逆ポーランド記法の問題を処理するプログラムを書いたりする(らしい)。

こいつの問題を大量生産したいってなったとき、考えなきゃいけないことは思いついたやつでこんな感じ。

  • 問題の先頭2つは必ず数字でなければならない
  • 記号、数字の数をx, yとすると、先頭からそれぞれ数えた時に常にx <= y - 1でなければいけない

… アキュムレータに2つ以上数字が無いと記号が積まれた時に処理が行えないため - 無理数などが生まれてはいけない

… 今回はランダム生成する数に0を入れないことで対処 - いい感じにランダムにできたらオサレ

こんな感じでしょうか。

これに加えて負の数、浮動小数点を加えるかどうかを選択するようにしました。

コードが以下の感じ。

              #!/usr/bin/python
              # -*- coding: utf-8 -*-
              import random
               
              DECIMEL, MINUS = 10, 4 # 小数点/負の数の出現頻度 1/variable name
               
              def randnum(d, m):
                  num = random.randint(1, 9)
                  if d: num = randdecimel(num)
                  if m: num = randminus(num)
                  return num
               
              def randdecimel(num):
                  return num if random.randint(0, DECIMEL) != 0 else float(format(random.random(), '.1f')) + float(num)
               
              def randminus(num):
                  return num if random.randint(0, MINUS) != 0 else num * -1
               
              ans = []
               
              m = eval(input('生成する問題数を入力してください (1 <= M <= 10000)'))
              n = eval(input('問題の最大桁数を入力してください (3 <= N <= 100)'))
              decimel = True if input("小数点は入れます? Y/n") == "Y" else False
              minus = True if input("負の数は入れます? Y/n") == "Y" else False
               
              for i in range(m):
                  f = random.randint(3, n) # 問題桁数の決定
                  while f % 2 == 0:
                      f = random.randint(3, n)
                  q = [] # 問題
                  num = [randnum(decimel, minus) for j in range(int(f / 2 + 1))]
                  obj = [['+', '-', '*', '/'][random.randint(0,3)] for j in range(int(f / 2))]
                  n1, n2 = 0, 0 # 数字/符号の数
                  while len(q) < f:
                      if len(num) == 0:
                          q += obj # 数字が尽きた場合
                          break
                      if n1 - n2 < 2:
                          q.append(num.pop())
                          n1 += 1
                      else:
                          tmp = random.randint(0, 1)
                          if tmp == 0:
                              q.append(num.pop())
                              n1 += 1
                          else:
                              q.append(obj.pop())
                              n2 += 1
                  print(" ".join(map(str, q)))
              

ついでにソルバーも載せておく。

              #!/usr/bin/python
               
              def rpn(stack, obj):
                  if type(obj) == int:
                      stack.append(obj)
                  else:
                      p = stack.pop()
                      if obj == '+':
                          stack.append(stack.pop() + p)
                      elif obj == '-':
                          stack.append(stack.pop() - p)
                      elif obj == '*':
                          stack.append(stack.pop() * p)
                      elif obj == '/':
                          stack.append(stack.pop() / p)
                  return stack
               
              while True:
                  stack = []
                  print("半角スペース区切りで問題を入力してください")
                  formula = input("-> ").split()
                  try:
                      for f in formula:
                          stack = rpn(stack, int(f) if f.isdigit() else f)
                      print(stack[0])
                  except:
                      print('Error: invalid input')
                      break
              

実行すると以下のようにやりとりできる

              
              % python3 rpn_gene.py
              生成する問題数を入力してください (1 <= N <= 10000) 10000
              問題の最大桁数を入力してください (3 <= N <= 100) 100 
              小数点は入れます? Y/n Y
              負の数は入れます? Y/n Y
              ~ ここ以下で問題生成 ~
              

以上!

【追記】

関数化して少しだけ改良した。

https://gist.github.com/sota1235/906d56098b9da9815913


※この記事は WordPress に投稿した記事を変換したものです。一部不自然な表示があるかも知れません。ご了承ください。また、記事タイトル先頭の * は WordPress から移行した記事である印です。

comments powered by Disqus