Eric Lippertのクイズ
A Simple Puzzle – Fabulous Adventures In Codingが面白かった。
度数分布表を作成するメソッドのバグを見つけられるか?というもの。
一応、度数分布表を説明しておくと、こういうやつね。
階級 | 度数 (人) |
---|---|
160cm未満 | 2 |
160〜164cm | 4 |
164〜168cm | 22 |
168〜172cm | 17 |
172〜176cm | 8 |
176〜180cm | 6 |
180cm以上 | 1 |
ただし今回のメソッドでは、最小値未満のデータや最大値以上のデータは無視して、こんな感じの表を得るという仕様。
階級 | 度数 (人) |
---|---|
160〜164cm | 4 |
164〜168cm | 22 |
168〜172cm | 17 |
172〜176cm | 8 |
176〜180cm | 6 |
/// <summary> /// 指定した範囲内の度数分布表を作成します。 /// </summary> /// <remarks> /// <paramref name="buckets"/>は1以上の値が与えられるものとします。 /// <paramref name="max"/>は<paramref name="min"/>より大きな値が与えられるものとします。 /// </remarks> /// <param name="data">度数分布表を作成するデータ。</param> /// <param name="buckets">作成する度数分布表の階級数(区間の数)。</param> /// <param name="min">作成する度数分布表の最小値。 /// この値以上のデータを集計し、この値未満のデータは無視します。</param> /// <param name="max">作成する度数分布表の最大値。 /// この値未満のデータを集計し、この値以上のデータは無視します。</param> /// <returns>累積度数の配列。長さは<paramref name="buckets"/>。</returns> private static int[] CreateHistogram(IEnumerable<double> data, int buckets, double min, double max) { // 累積度数の配列を作成。 int[] results = new int[buckets]; // 階級幅の逆数を計算。 double multiplier = buckets / (max - min); foreach (double datum in data) { // 幅で割って(=幅の逆数を掛けて)整数化。 int index = (int)((datum - min) * multiplier); // 度数分布表の範囲に収まっていたら度数をカウントアップ。 if (0 <= index && index < buckets) results[index] += 1; } return results; }
さて、どこにバグがあるでしょうか。
ヒント:ためしに実行してみればわかるかもよ。
static void Main() { var r = new Random(); // 0以上100未満のランダムな数を100000個生成。 var data = Enumerable.Repeat(0, 100000).Select(x => 100.0d * r.NextDouble()); // 10以上20未満の区間を10個に分割した度数分布表を作成。 var hist = CreateHistogram(data, 10, 10.0d, 20.0d); for (var i = 0; i < hist.Length; i++) Console.WriteLine("{0,2}: {1}", i, hist[i]); }
追記:Javaでも同じ。