10/19の課題について


10/19の課題


解答例(1)

// Randomクラスを使うために必要
import java.util.*;
// 入力に関するクラスを使う時は必要
import java.io.*;

class Kadai1019{
    // throws IOException で内部で入出力エラーが起きる可能性があることを示す
    public static void main(String[] args)throws IOException{
    // 乱数の元となるオブジェクトを作成
        Random r=new Random();      
    // 1-10000までの乱数を作成
        int secret=r.nextInt(10000)+1;                                          
     // 入力をするためには,System.inからBufferedReaderを作らなくてはいけない
        BufferedReader d=new BufferedReader(new InputStreamReader(System.in));
        System.out.println("Guess a number");
        int i;
    for(i=1;;i++){
      // 1行読み込んで整数に変換する
      int num=Integer.parseInt(d.readLine());
      // 入力した数が乱数と一致した時は抜ける。
      if(num==secret) break;
      if(num < secret) System.out.println("LOW");
      if(num > secret) System.out.println("HIGH");
    }
    System.out.println(i);
    }
}

コメント

インデント(字下げ幅)がずれているところがあるようですね.また,
      if(num==secret) break;
      if(num < secret) System.out.println("LOW");
      if(num > secret) System.out.println("HIGH");
のように,一つしか成立しないif文はif-else文を使って
      if(num==secret) break;
      else if(num < secret) System.out.println("LOW");
      else System.out.println("HIGH");
と書いた方が,効率も良いし,人が読む時も読みやすいでしょう.

解答例(2)

import java.io.*;//入出力
import java.util.*;//乱数

class HighAndLow{
    private int number;//当てるべき数
    
//////////////////////////////////////////////////////////
// HighAndLow関数(コンストラクタ)
// 機能:初期化。SetRandomNumber()を実行するだけ
//////////////////////////////////////////////////////////
    HighAndLow(){
        SetRandomNumber();
    }
//////////////////////////////////////////////////////////
// SetRandomNumber関数
// 機能:numberに1から10000までの乱数を代入する
// 引数:なし
// 返り値:なし
//////////////////////////////////////////////////////////
    public void SetRandomNumber(){
        number=(new Random()).nextInt(10000)+1;
    }
//////////////////////////////////////////////////////////
// Try関数
// 機能:数当てを実行する
// 引数:int i:試してみる数
// 返り値:int:試した数が
//             大きすぎるときは正の数、
//             小さすぎるときは負の数、
//             あたったときは0
//             を返す。
//////////////////////////////////////////////////////////
    public int Try(int i){
        return i-number;
    }
}



class Kadai1019{
    public static void main(String[] args) throws IOException{
        
        int inputNumber;//入力された数
        int result;//数あての結果
        
        //数あてクラスのインスタンスを作成
        HighAndLow hal= new HighAndLow();
        //入出力準備
        BufferedReader d=new BufferedReader(new InputStreamReader(System.in));
        
        //入力を促すメッセージを表示
        System.out.println("1から10000までの数を入力してください。");

        for(int i=1;;i++){//i:試行回数
            //ユーザの入力を受け取って整数型に変換
            try{
                inputNumber=Integer.parseInt(d.readLine());
            }
            //入力エラーの場合はエラーメッセージを表示して次の試行に移る
            catch(java.lang.NumberFormatException e){
                System.out.println("1から10000までの数を入力してください");
                continue;
            }
            
            //数あてを実行する
            result=hal.Try(inputNumber);

            //実行結果の判断

            //実行結果の判断
            if(result>0){//はずれ:大きすぎる
                System.out.println("HIGH");
            }
            else if(result < 0){//はずれ:小さすぎる
                System.out.println("LOW");
            }
            else{//あたり:試行回数を表示し、for文を抜ける
                System.out.println(i);
                break;
            }
        }
            
    }
}

コメント

このような問題ではクラスを使って書こうとするとかえって,クラス設計が難しくなりがちで,がんばって書いていますね.

オプション課題

14名が提出.説明不足のためか混乱があった.

解答例(1)

/*
今度は最大値と最小値を定めた変数を作って中間値を求める方法。
これだと1-10000に限らなくてもよくなり、せっかくなので範囲設定を可能にする。
引数として最初に値の範囲を指定してargs[0],args[1]に渡し、
今度は任意の数を何度も入力できて、自由に終わらせられるようにしてみる。
実験してみたら前バージョンより精度もよいようだ。
*/

import java.io.*;

class Answer1019_2{
    public static void main(String[] args) throws IOException{
	BufferedReader d=new BufferedReader(new InputStreamReader(System.in));
	//引数より変数min,maxを定める(固定)。
	int min=Integer.parseInt(args[0]);
	int max=Integer.parseInt(args[1]);
	for(;;){
	    System.out.println(min+" - "+max+" の範囲で任意の数を入力して下さい");
	    //入力から一行読み込み整数型に変換
	    int secret=Integer.parseInt(d.readLine()),i;
	    //このあと下層ループ内で変動する最小値,最大値,中間値を入れる変数の宣言
	    //p,qの初期値はもちろんmin,maxより代入
	    int p=min,q=max,r;
	    //終了判定。0が入力されたらループを抜けてプログラム終了
	    if(secret==0) break;
	    //以下の二文はエラーを防ぐもの。範囲外の値が入力されたらやり直させる
	    if(secret < min) continue;
	    if(secret > max) continue;
	    for(i=1;;i++){
		//中間値を変数rに代入
		r=(p+q)/2;
		System.out.println(r);
		//rが入力した数と一致したら、ループを抜けて回数表示
		if(r==secret) break;
		if(r < secret)
		    //secretの範囲が狭まったので、最小値を更新
		    p=r+1;
		else
		    //上と同様に、こちらでは最大値を更新
		    q=r-1;
	    }
	    System.out.println("回答に要した回数は、"+i+"回");
	}
    }
}

コメント

このような問題では,上のプログラムのように二分法で正解の範囲を狭めていく 方法が有効です.ただし,範囲の狭め方が人によって異なっていました.一般には, 上のプログラムのように, 上限と下限を押さえて中央の値を次の問いにする方法が ベストな解になります.

解答例(2)

import java.util.*;
class Option1019{
    public static void main(String[] args){
        //Random変数の設定
        Random r = new Random();
        //解答(ans)の初期設定、回数を表す変数のtime導入
        int ans=5000,time=1,i;
        //配列を定義,配列の各値には
        //5000を2で割って切り上げという動作を繰り返し、値を配列に代入 
        int grade[]={2500,1250,625,313,157,78,39,20,10,5,3,2,1,};
        //隠し変数の値を設定
        int secret = r.nextInt(10000)+1;
        System.out.println("Guess a number");
        for(i=0;;i++){
            System.out.println(ans);
            if(ans < secret){
                System.out.println("LOW");
                ans += grade[i];
            }
            else if(ans > secret){
                System.out.println("HIGH");
                ans -= grade[i];
            }
            else if(ans==secret){
                System.out.println(i+1);
                break;
            }
        }
    }
}

コメント

次に聞く数と前の数との差をあらかじめ決めておくという方法です.正解が0の 時に,0と聞いてLOWと答えるという無駄な質問で一回分質問を無駄にすることが あります.

解答例(3)

/*
  計算機プログラミングI 火曜五限(田中先生)
  10/19 課題問題 - オプション課題回答

  XXXX/ XXXXXXX XXXXXX (XXXXXXX@mail.ecc.u-tokyo.ac.jp)
  2004/10/31(Sun)
 */

import java.util.*;
import java.io.*;
import java.lang.Math;

public class Option1019 {
    public static void main(String[] args) throws IOException
    {
        // cur_num is for Current Number
        int TRIAL_NUM, MAX_NUM;
        int ans_num, cur_num, diff;
        int count[];

        Random r = new Random();

        if (args.length < 2) {
            System.out.println("usage: java Option1019 MAX_NUM TRIAL_NUM");
            return;
        }

        MAX_NUM = Integer.parseInt(args[0]);
        TRIAL_NUM = Integer.parseInt(args[1]);

        count = new int[TRIAL_NUM];

        // 試行部分
        for(int i = 0; i < TRIAL_NUM; i++) {
            ans_num = r.nextInt(MAX_NUM) + 1;
            diff = MAX_NUM / 2;

            // 探索領域をどんどん半分にして探していく。
            for(count[i] = 1, cur_num = diff; ; count[i]++) {
                if (cur_num > ans_num) {
                    cur_num -= diff;
                } else if (cur_num < ans_num) {
                    cur_num += diff;
                } else {
                    break;
                }

                diff /= 2; if (diff == 0) diff = 1;
            }
        }

        // 平均試行回数を計算
        double average_trial_num = 0;
        for(int i = 0; i < TRIAL_NUM; i++) {
            average_trial_num += (double)count[i];
        }
        average_trial_num /= (double)TRIAL_NUM;
        
        System.out.println("Average Trial Number is: " + average_trial_num);
        System.out.println("FYI: The logarithm of " + MAX_NUM + " to base 2 is: 
" + (Math.log(MAX_NUM) / Math.log(2)));
    }
}

コメント

探索幅を前回の半分にするというアイデアは一緒ですが,元の幅が奇数の時に 切り捨てにすると幅が1になっても当たらないということがあります.そのため, 幅が1になってから場所を一つづつずらしながら質問を繰り返す必要があります.

解答例(4)

import java.util.*;
class Option1019{
  public static void main(String[] args){
    int s=(new Random()).nextInt(10000)+1;
    System.out.println("Guess a number");
    int i,n,h=10000,l=0;
    for(i=1;;i++){
        n=(h+l)/2;
            System.out.println(n);
            if(n > s) {System.out.println("HIGH"); h=n;}
            else if(n < s) {System.out.println("LOW"); l=n;}
      else break;
    }
    System.out.println(i);
  }
}

コメント

あと一歩.sがたまたま10000になった時はループしてしまいます.プログラム をテストする場合は,1とか10000などの境界の値に関しておこなってみると,プロ グラムのミスを見つける助けになります.逆に,境界の値を与えてプログラムを 走らせることが容易にできるようにプログラムを作るのもコツです.