質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.63%

計算問題を表示させるツール作成における÷0に対する対処がうまくいきません。

解決済

回答 3

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 363

mami99

score 2

前提・実現したいこと

計算問題が生成されるプログラムを作成しています。
条件は以下の通りです。

・計算式は全てで5問表示される。
・1問、4つの数字をランダム、3つの演算子をランダムに表示させる。
・5問の中で4つすべての算術演算子が使われるようにする。
・1つの計算式に使われる算術演算子は最低2つ。

しかし、÷0の問題が出力されると問題が発生します。

初心者の書くコードなんて読み辛いことこの上ないでしょうが、
以下のことを教えていただけると嬉しいです。

・問題点に対する具体的な対処方法。
・(もしよろしければ)その他コードの添削。

発生している問題

÷0の問題が出力された際、その問題が表示され、回答が入力できてしまう。

該当のソースコード

public class Main {
    public static void main(String[] args) {

        Calculation.setOperator();

    }
}
import java.util.ArrayList;
import java.util.InputMismatchException;
import java.util.List;
import java.util.Random;
import java.util.Scanner;

public class Calculation {

    //演算子を決めるメソッド
    public static void setOperator() {

        Random rm = new Random();
        String[] operators = { "+", "-", "×", "÷" };

        //フラグ判定
        boolean[] used = new boolean[4];

        //3つの演算子を5セット格納
        String operator[][] = new String[5][3];

        while (true) {

            for (int i = 0; i < 4; i++) {
                used[i] = false;
            }

            for (int i = 0; i < 5; i++) {

                //1つの式に最低2つ以上の演算子が使われているかを確認
                while (true) {
                    int o = rm.nextInt(4);
                    operator[i][0] = operators[o];
                    used[o] = true;

                    o = rm.nextInt(4);
                    operator[i][1] = operators[o];
                    used[o] = true;

                    o = rm.nextInt(4);
                    operator[i][2] = operators[o];
                    used[o] = true;
                    if (!operator[i][0].equals(operator[i][1]) && !operator[i][0].equals(operator[i][2])) {
                        break;
                    }
                }
            }

            //5つの式で4つの演算子が使われているかを確認
            boolean ok = true;
            for (int i = 0; i < 4; i++) {
                if (used[i] == false)
                    ok = false;
            }
            if (ok) {
                break;

            }
        }

        for (int i = 0; i < 5; i++) {
            calculate(operator[i][0], operator[i][1], operator[i][2], i);
        }

    }

    //四則演算メソッド
    public static void calculate(String operator1, String operator2, String operator3, int count) {

        Random rm = new Random();

        List<String> list = new ArrayList<>();

        boolean aaa = true;

        //計算式を表示するためのリスト
        List<String> formulaList = new ArrayList<>();

        String operator = null;

        int value = rm.nextInt(5);
        int beforeValue = value;
        formulaList.add(String.valueOf(beforeValue));

        for (int i = 0; i < 3; i++) {
            switch (i) {
            case 0:
                operator = operator1;
                formulaList.add(operator1);
                break;

            case 1:
                operator = operator2;
                formulaList.add(operator2);
                break;

            case 2:
                operator = operator3;
                formulaList.add(operator3);
                break;

            }

            value = rm.nextInt(5);
            formulaList.add(String.valueOf(value));

            //×,÷を先に計算、+,-はリストに格納
            switch (operator) {
            case "+":
                list.add(beforeValue + "");
                list.add("+");
                beforeValue = value;
                break;

            case "-":
                list.add(beforeValue + "");
                list.add("-");
                beforeValue = value;
                break;

            case "×":
                beforeValue *= value;
                break;

            case "÷":
                try {
                    beforeValue /= value;
                } catch (ArithmeticException e) {

                    System.out.println("※÷0はだめです。");
                    System.out.println("=========================");

                }
                break;
            }
        }

        list.add(beforeValue + "");
        int correctAnswer = Integer.parseInt(list.get(0));

        //リストに+,-があれば計算
        for (int i = 0; i < list.size(); i++) {
            String s = list.get(i);
            if (s.equals("+")) {
                correctAnswer += Integer.parseInt(list.get(i + 1));
            } else if (s.equals("-")) {
                correctAnswer -= Integer.parseInt(list.get(i + 1));
            }
        }

        question(formulaList, count, correctAnswer);

    }

    //問題表示メソッド
    static void question(List<String> formulaList, int count, int correctAnswer) {

        int correctInput;

        // 「1」を返されると同じ問題を繰り返す
        do {
            System.out.print("問題" + (count + 1) + ")");

            for (int i = 0; i < formulaList.size(); i++) {
                System.out.print(formulaList.get(i));
            }

            System.out.println("=");
            correctInput = decide(correctAnswer);

        } while (correctInput == 1);

    }

    //正誤判定メソッド
    static int decide(int correctAnswer) {

        int yourAnswer = 0;
        Scanner scanner = new Scanner(System.in);

        //数字以外を入力すると「1」を返す
        try {
            yourAnswer = scanner.nextInt();
        } catch (InputMismatchException e) {
            System.out.println("※整数を入力してください。");
            return 1;
        }

        if (yourAnswer == correctAnswer) {
            System.out.println("【正解】");

        } else {
            System.out.println("【不正解】");

        }
        System.out.println("=========================");
        return 0;

    }

}

試したこと

÷0をキャッチした回数分、問題を再生成させるようにするために以下のコードを書いた。
ただ、問題の表示がおかしくなる。

試したソースコード

public class Main {
    public static void main(String[] args) {

        Calculation.setOperator(0);

    }
}
import java.util.ArrayList;
import java.util.InputMismatchException;
import java.util.List;
import java.util.Random;
import java.util.Scanner;

public class Calculation {

    //演算子を決めるメソッド
    public static void setOperator(int count) {

        Random rm = new Random();
        String[] operators = { "+", "-", "×", "÷" };

        //フラグ判定
        boolean[] used = new boolean[4];

        //3つの演算子を5セット格納
        String operator[][] = new String[5][3];

        while (true) {

            for (int i = 0; i < 4; i++) {
                used[i] = false;
            }

            for (int i = 0; i < 5; i++) {

                //1つの式に最低2つ以上の演算子が使われているかを確認
                while (true) {
                    int o = rm.nextInt(4);
                    operator[i][0] = operators[o];
                    used[o] = true;

                    o = rm.nextInt(4);
                    operator[i][1] = operators[o];
                    used[o] = true;

                    o = rm.nextInt(4);
                    operator[i][2] = operators[o];
                    used[o] = true;
                    if (!operator[i][0].equals(operator[i][1]) && !operator[i][0].equals(operator[i][2])) {
                        break;
                    }
                }
            }

            //5つの式で4つの演算子が使われているかを確認
            boolean ok = true;
            for (int i = 0; i < 4; i++) {
                if (used[i] == false)
                    ok = false;
            }
            if (ok) {
                break;

            }
        }

        if (count == 0) {
            for (int i = 0; i < 5; i++) {
                calculate(operator[i][0], operator[i][1], operator[i][2], i);
            }
        } else {

            calculate(operator[count][0], operator[count][1], operator[count][2], count);

        }

    }

    //四則演算メソッド
    public static void calculate(String operator1, String operator2, String operator3, int count) {

        Random rm = new Random();

        List<String> list = new ArrayList<>();

        boolean aaa = true;

        //計算式を表示するためのリスト
        List<String> formulaList = new ArrayList<>();

        String operator = null;

        int value = rm.nextInt(5);
        int beforeValue = value;
        formulaList.add(String.valueOf(beforeValue));

        for (int i = 0; i < 3; i++) {
            switch (i) {
            case 0:
                operator = operator1;
                formulaList.add(operator1);
                break;

            case 1:
                operator = operator2;
                formulaList.add(operator2);
                break;

            case 2:
                operator = operator3;
                formulaList.add(operator3);
                break;

            }

            value = rm.nextInt(5);
            formulaList.add(String.valueOf(value));

            //×,÷を先に計算、+,-はリストに格納
            switch (operator) {
            case "+":
                list.add(beforeValue + "");
                list.add("+");
                beforeValue = value;
                break;

            case "-":
                list.add(beforeValue + "");
                list.add("-");
                beforeValue = value;
                break;

            case "×":
                beforeValue *= value;
                break;

            case "÷":
                try {
                    beforeValue /= value;
                } catch (ArithmeticException e) {

                    System.out.println("※÷0はだめです。");
                    System.out.println("=========================");
                    setOperator(count);

                }
                break;
            }
        }

        list.add(beforeValue + "");
        int correctAnswer = Integer.parseInt(list.get(0));

        //リストに+,-があれば計算
        for (int i = 0; i < list.size(); i++) {
            String s = list.get(i);
            if (s.equals("+")) {
                correctAnswer += Integer.parseInt(list.get(i + 1));
            } else if (s.equals("-")) {
                correctAnswer -= Integer.parseInt(list.get(i + 1));
            }
        }

        question(formulaList, count, correctAnswer);

    }

    //問題表示メソッド
    static void question(List<String> formulaList, int count, int correctAnswer) {

        int correctInput;

        // 「1」を返されると同じ問題を繰り返す
        do {
            System.out.print("問題" + (count + 1) + ")");

            for (int i = 0; i < formulaList.size(); i++) {
                System.out.print(formulaList.get(i));
            }

            System.out.println("=");
            correctInput = decide(correctAnswer);

        } while (correctInput == 1);

    }

    //正誤判定メソッド
    static int decide(int correctAnswer) {

        int yourAnswer = 0;
        Scanner scanner = new Scanner(System.in);

        //数字以外を入力すると「1」を返す
        try {
            yourAnswer = scanner.nextInt();
        } catch (InputMismatchException e) {
            System.out.println("※整数を入力してください。");
            return 1;
        }

        if (yourAnswer == correctAnswer) {
            System.out.println("【正解】");

        } else {
            System.out.println("【不正解】");

        }
        System.out.println("=========================");
        return 0;

    }

}
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

checkベストアンサー

+3

質問への直接の回答としては、「『÷0』が入るかどうかの判定のタイミングと再設定のタイミングがおかしい」です。その原因として、演算子の選択と数字の選択が別メソッドに分かれていることが挙げられます。演算子を全部選んでから数字を選ぶという行動をしているため、巻き戻して演算子から選ぼうとすれば、再度演算子が5回選ばれます。その前に選んだ演算子や数字もそのまま残っている(再帰で呼び出しているだけだからるため、当然表示数が増えることになります。
なので、1つの式を完成させ、それに「÷0」がないかチェックするまでを1つの動作とする必要があります。そこまでするなら、演算子に÷が選ばれた場合は乱数から0をはじく、という動作のほうが簡単だと思います。かっこが使われない限り、÷より先に別の演算がなされ、結果として÷0になる、という可能性はありませんから。

それ以外にもツッコミどころを1つ指摘すると…

  • 次の文は「演算子を2つ以上使っている」の判定として不適切
if (!operator[i][0].equals(operator[i][1]) && !operator[i][0].equals(operator[i][2]))


0番と1番、0番と2番の両方が等しくないときにのみこのifを通るので、
0番と1番が+、2番が-
のようなケースがこのifを通過できません。
「演算子を2つ以上使っている」の否定は「演算子を1つしか使っていない」、つまり「すべて同じ演算子」となるため、正しい判定文の例は次のようになります。

if (!(operator[i][0].equals(operator[i][1]) && operator[i][0].equals(operator[i][2])))

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/08/26 20:12

    ご回答ありがとうございます。
    考え方から改めてみます。

    また、条件に関しても教えてくださりありがとうございます。
    こういったミスをなくしていきたいですね……。

    キャンセル

+2

加算、減算、乗算においても、0を対象とすると簡単すぎるので、乱数で生成する数字から0の場合を除外するのが良いと思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/08/25 18:02

    ご回答ありがとうございます。
    コード以前の問題でしたか。

    ちなみに、0が入力されることも想定した場合はどうすればいいですか?

    キャンセル

+1

・(もしよろしければ)その他コードの添削。

添削になっていませんが, 勢いで書いてみました.
ゼロ割りは例外を生成してキャッチしています.

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.InputMismatchException;
import java.util.Random;
import java.util.Scanner;

public class Main {
  static final int Q_COUNT = 5; //問題数
  static final int E_COUNT = 7; //式の要素数
  static final int V_MAX = 5; //数値要素の最大値+1

  public static void main(String[] args) {
    int[][] formulas = createFormulas();

    Scanner scanner = new Scanner(System.in);
    for(int i=0; i<Q_COUNT; i++) {
      while(true) {
        System.out.print("問題 " + (i+1) + ") ");
        System.out.println(toFormulaString(formulas[i])+"=");

        try {
          int yourAnswer = scanner.nextInt();
          int correctAnswer = ReversePolishNotation.calculate(formulas[i]);
          if (yourAnswer == correctAnswer) {
            System.out.println("【正解】");
          } else {
            System.out.println("【不正解】 正解は"+correctAnswer);
          }
          System.out.println("=========================");
          break;
        } catch (InputMismatchException e) {
          System.out.println("※整数を入力してください。");
          scanner.next(); //捨てる
        }
      }
    }
    System.out.println("お疲れ様でした。");
  }

  static int[][] createFormulas() {
    int formulas[][] = new int[Q_COUNT][E_COUNT]; //式数, 偶数番目=数値,奇数番目=演算子
    Random rm = new Random();

    //演算子を4種類全て使用したら終了
    for(int usedBits=0; usedBits!=(1<<Operator.size())-1; ) {
      usedBits = 0;
      for(int i=0; i<Q_COUNT; i++) {
        while(true) {
          for(int j=0; j<E_COUNT; j++) {
            formulas[i][j] = rm.nextInt(j%2==0?V_MAX:Operator.size());
          }
          //演算子が全て同じならもう一度生成
          boolean ok = false;
          for(int j=3; j<formulas[i].length; j+=2) ok |= formulas[i][1] != formulas[i][j];
          if(!ok) continue;

          //計算してみる
          try {
            ReversePolishNotation.calculate(formulas[i]);
          } catch(DivisionByZeroException e) { //ゼロ割り
            //System.out.println(toFormulaString(formulas[i])+" ... ゼロ割り");
            continue;
          }

          break;
        }
        for(int j=1; j<formulas[i].length; j+=2) usedBits |= 1<<formulas[i][j];
      }
    }
    return formulas;
  }

  static String toFormulaString(int formula[]) {
    StringBuilder sb = new StringBuilder();
    for(int i=0; i<formula.length; i++) {
      if(i%2 == 0) sb.append(formula[i]);
      else sb.append(Operator.valueOf(formula[i]).toString());
    }
    return sb.toString();
  }

  //ゼロ割り例外
  static class DivisionByZeroException extends ArithmeticException {
    DivisionByZeroException() { super(); };
  }

  static enum Operator {
    ADD(0,"+",(v1,v2)->{ return v1+v2; }),
    SUB(0,"-",(v1,v2)->{ return v1-v2; }),
    MUL(1,"*",(v1,v2)->{ return v1*v2; }),
    DIV(1,"/",(v1,v2)->{ if(v2 == 0) throw new DivisionByZeroException(); return v1/v2; });

    interface Calculator {
      int calculate(int value1, int value2);
    }

    private int priority; //加減乗除の優先順位
    private String str; //表示用文字列
    private Calculator calculator; //計算機
    private Operator(int priority, String str, Calculator calculator) {
      this.priority = priority;
      this.str = str;
      this.calculator = calculator;
    }
    public int getPriority() { return priority; }
    public int calculate(int v1, int v2) { return calculator.calculate(v1,v2); }
    @Override
    public String toString() { return str; }

    static Operator valueOf(int operator) { return values()[operator]; }
    static int size() { return 4; } //(valueOf(int) で指定できる)演算子の数
  }

  static class ReversePolishNotation {
    private ReversePolishNotation() {}

    static int calculate(int formula[]) {
      return calculate(create(formula));
    }

    static private class FormulaElement {
      private int value;
      private Operator operator;
      FormulaElement(int value) {
        this.value = value;
        this.operator = null;
      }
      FormulaElement(Operator operator) {
        this.value = 0;
        this.operator = operator;
      }
      int getValue() { return value; }
      Operator getOperator() { return operator; }
      boolean isOperator() { return operator != null; }

      @Override
      public String toString() {
        return isOperator() ? operator.toString() : ""+value;
      }
    }

    //変換
    static private Deque<FormulaElement> create(int formula[]) {
      Deque<FormulaElement> fQueue = new ArrayDeque<>();
      Deque<Operator> oStack = new ArrayDeque<>();
      for(int i=0; i<formula.length; i++) {
        if(i%2 == 0) { //数値
          fQueue.add(new FormulaElement(formula[i]));
        } else { //演算子
          Operator t = Operator.valueOf(formula[i]);
          while(oStack.size() > 0 && oStack.peek().getPriority() >= t.getPriority()) {
            fQueue.add(new FormulaElement(oStack.pop()));
          }
          oStack.push(t);
        }
      }
      while(oStack.size() > 0) fQueue.add(new FormulaElement(oStack.pop()));
      return fQueue;
    }

    //計算
    static private int calculate(Deque<FormulaElement> fStack) {
      Deque<Integer> vStack = new ArrayDeque<>();
      while(fStack.size() > 0) {
        FormulaElement e = fStack.pop();
        if(e.isOperator()) {
          int v2 = vStack.pop();
          int v1 = vStack.pop();
          vStack.push(e.getOperator().calculate(v1,v2));
        } else {
          vStack.push(e.getValue());
        }
      }
      return vStack.pop();
    }
  }
}

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/08/26 22:13

    ご回答ありがとうございます。
    ゆっくり読み解いていこうと思います。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.63%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる