わさっきhb

大学(教育研究)とか ,親馬鹿とか,和歌山とか,とか,とか.

割合から総数を求める〜実例に適用

一昨日のエントリの続きです.

回答率が以下の表で示されるアンケートがあるときに,N≦1000の範囲で,アンケートの回答者数を求めてみましょう.

選択肢 回答率(%)
学力を身につけてほしい 8.6
健康な体と体力をつけてほしい 30.8
責任感の強い子になってほしい 8.2
何事にも粘り強く努力する子になってほしい 23.5
自立心を身につけてほしい 28.9
モラルを身につけてほしい 25.8
人の心の痛みや辛さがわかる人になってほしい 57.1
多くの友人に恵まれてほしい 17.9
不明 1.7

(教師格差―ダメ教師はなぜ増えるのか (角川oneテーマ21), p.191 「図4 我が子の成長に対して願うこと」より)

選択肢 回答率(%)
教育実践の実感として 74.1
テストの読み書きが以前よりできない 49.4
親が,学力が低下していると言うから 2.4
マスコミが,学力が低下していると言うから 3.5
教育委員会などが低下していると言うから 1.2
学力比較のテストをやってみたから 11.8
何となく 2.4
社会全体が学力低下について騒いでいるから 3.5
家庭学習をやらなくなったから 40.0
わからない 0.0
不明 7.1

(上掲書, p.195 「図5 学力低下を感じる証拠」より)

一つ目の表は,保護者を対象としたアンケートで,選択肢から2つを選ぶというものです.パーセンテージの合計は,202.5です.って誤差大きいなあ.3つ以上選んでいる人もいたのかな.
二つ目の表は,学校関係者を対象としたアンケートで,複数回答可です.
ともあれ,プログラムです.

#!/usr/bin/env ruby

# actualcount2.rb

class ActualCount
  def initialize(rate_a_, max_ = 100)
    @rate_a = rate_a_.map {|rate| rate.to_i}
    @max = max_
  end

  def get_actualcount
    d_a = [] # 解(実数の配列)
    1.upto(@max) do |d|
      counter = 0 # 条件を満たす分子が見つかった個数
      n_a = []    # 分子の配列
      @rate_a.each do |rate|
        # (rate-0.5)/@max≦n/d<(rate0.5)/@max を満たす整数nを探す
        # 変形すると,(rate*10-5)*d/(@max*10)≦n<(rate*10+5)*d/(@max*10)
        n_min = ((rate.to_f * 10.0 - 5.0) * d.to_f / (@max.to_f * 10.0)).ceil
        n_max = ((rate.to_f * 10.0 + 5.0) * d.to_f / (@max.to_f * 10.0)).floor
        if n_min <= n_max
          counter += 1
          n_a << n_min
        end
      end
      if counter == @rate_a.length
        d_a << d
        puts "Found! N=#{d}"
        @rate_a.each do |rate|
          n = n_a.shift
          puts "  %5d * %5d / %5d = %8.2f -> %5d" % \
            [@max, n, d, @max.to_f * n.to_f / d.to_f, rate]
        end
      end
    end
    d_a
  end
end

if __FILE__ == $0
  if ARGV.empty?
    max = 1000
    rate1_a = %w(86 308 82 235 289 258 571 179 17)
    rate2_a = %w(741 494 24 35 12 118 24 35 400 0 71)
    puts "<case 1>"
    ac1_a = ActualCount.new(rate1_a, max).get_actualcount
    puts "<case 2>"
    ac2_a = ActualCount.new(rate2_a, max).get_actualcount
#    ac1and2_a = ac1_a & ac2_a
#    puts "<intersection>"
#    puts ac1and2_a.join(', ')
  else
    max = (ARGV.shift || 100).to_i
    rate_a = ARGV.empty? ? [14] : ARGV
    ActualCount.new(rate_a, max).get_actualcount
  end
end

そして実行してみます.ですが,出力はたくさん出るので,割愛します.
明らかにN=1000は解ですが,それ以下でも,それなりに解の候補が出ています.一つ目の表の最小解はN=643でした.二つ目の表の最小解はN=85で,しばらく85の倍数が解として出てきますが,N=425とN=510の間にN=490というのが出てきます.
Rubyスクリプトの終わりのほうに3行,コメントにしているところがありますが,コメントを外して実行すると,二つの表の共通解を出力します.最小はN=660とでます.なのですが,上に書いた通り,回答者は全く違うので,この共通化は意味のないお遊びです.Arrayの&を試してみたかっただけです.
プログラムについては,一昨日のプログラムをロードするのでも求めることは可能ですが,それだと結果表示が分かりにくいなと考えまして,同じクラス名・メソッド名を使って,中身を変えました.
Array変数の名前に「_a」をつけるのは,ここ最近の習慣です.ちなみにHash変数の名前には「_h」をつけます.数値や文字列には,特に何もつけません.またそのうち,書き方が変わりそうな気もします.
コンストラクタの引数は,「rate_a, max」よりも「max, *rate_a」にすべきでした.そうすれば,1個でも10個でも自然に呼び出せるのに.