わさっきhb

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

割合から実数を求める

久々ですが問題です.

1. あるアンケートの設問で,「該当する」と回答した割合が14%だった.N≦100の範囲で,アンケートの回答者数を求めなさい.
2. あるアンケートの設問で,「該当する」と回答した割合が1%だった.N≦100の範囲で,アンケートの回答者数を求めなさい.

プログラムを書く前に,いくつか計算してみます.「14%」について,1/7 = 0.14285以下略なので,N=7は解になります.「1%」のほうは,1/100 = 0.01なので,N=100は当然,解です.
Nって何?という人がいるかもしれないので補足しておくと,統計処理の結果をグラフにする際,実数すなわち上の例では「アンケートの回答者数」のことを,とくに断ることなく「N=数」で表すことがあります.もう一つ補足ですが,ここでの「実数」は,real numberではなくactual count(あるいはactual number)といいます.
さて,この問題の厄介なところは,割合は,その分母と分子となる整数値があって初めて求められますが,問題文の通り,そのどちらも明示されていないということです.
それで,1/7 = 0.14285以下略を1/0.14285以下略 = 1と式変形できるので,「100/パーセンテージ」で求めればいいのかというと,不十分です.「1%」の問題で,67人中1人が「該当する」と回答したら,1/67 = 0.01492以下略で,パーセンテージにして小数点以下を四捨五入すれば1%となり,N=67も解になるわけです.
単純な割り算では求められないし,どうやらいくつも解があるようなので,プログラムを書いてループを回さないといけないなと思えてきます.
では,プログラムです.

#!/usr/bin/env ruby

# actualcount1.rb

class ActualCount
  def initialize(rate_, max_ = 100)
    @rate = rate_
    @max = max_
  end

  def get_actualcount
    1.upto(@max) do |d|
      # (@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
        n = n_min.to_i
        puts "Found! N=#{d}"
        puts "  %d * %d / %d = %g -> %d" % \
        [@max, n, d, @max.to_f * n.to_f / d.to_f, @rate]
      end
    end
  end
end

if __FILE__ == $0
  max = (ARGV.shift || 100).to_i
  rate = (ARGV.shift || 14).to_i
  ActualCount.new(rate, max).get_actualcount
end

実行結果は,「ruby actualcount1.rb」と「ruby actualcount1.rb 100 1」でご確認ください.100以下の範囲で,前者は52個,後者は34個の解(「実数」の候補)が出てきます.前者すなわち「14%」は,初めのうちは7の倍数かなと見ていくと,28のほかに29も,35のほかに36も37も解になることがわかります.後者すなわち「1%」では,67≦N≦100はすべて解になります.「14%」に戻ると,N=88,90は解ですが,間のN=89は解に含まれていません.
プログラムの勘所は,コメントに書いているとおり,r=@rate, M=@maxが与えられたときに,
\frac{r-0.5}{M}\le\frac{n}{d}<\frac{r+0.5}{M}
を満たす分子n, 分母dの組を,dを1から@maxの範囲でループさせて求めることです.そのような整数n,dの組が見つかれば,その分母dが,求めるべきNの一つとなり,そのときのnは,「該当する」と回答した人の数になります.
dはループを回すことで常に整数になるので,nが存在するか否かだけを見ればいいわけです.これは,

  • \frac{r-0.5}{M}\le\frac{n}{d}を満たす最小の整数nをn_min
  • \frac{n}{d}<\frac{r+0.5}{M}を満たす最大の整数nをn_max

としたとき,n_min≦n_maxとなるかどうかで判定できます.@rate=7, @max=100を固定して,

  • d=6のときは,\frac{14-0.5}{100}\le\frac{n}{6}<\frac{14+0.5}{100} より,0.81\le n<0.87 …解なし
  • d=7のときは,\frac{14-0.5}{100}\le\frac{n}{7}<\frac{14+0.5}{100} より,0.945\le n<1.015 …n=1が解

ということで,うまく解が求められそうです.
Rubyスクリプトについて,「to_f」でFloat型にしているので,そこに計算誤差が起こる可能性が一応はあります.ただし,maxが100や1000くらいなら,問題はなさそうです.
以下は,次回の予告編です.

3. あるアンケートの設問で,「該当する」と回答した割合が30.8%だった.N≦1000の範囲で,アンケートの回答者数を求めなさい.

答えは,「ruby actualcount1.rb 1000 308」で求められますが,たくさん出てきます.もう少しプログラムを修正して,この計算を,最近読んだある本に適用してみる予定です.
(2008年4月6日18:00ごろ:あちこち改訂しました.)