############################ # 分散分析 # coded by Y.Suganuma ############################ ############################################ # Γ(x)の計算(ガンマ関数,近似式) # ier : =0 : normal # =-1 : x=-n (n=0,1,2,・・・) # return : 結果 # coded by Y.Suganuma ############################################ def gamma(x, ier) ier[0] = 0 if x > 5.0 v = 1.0 / x s = ((((((-0.000592166437354 * v + 0.0000697281375837) * v + 0.00078403922172) * v - 0.000229472093621) * v - 0.00268132716049) * v + 0.00347222222222) * v + 0.0833333333333) * v + 1.0 g = 2.506628274631001 * Math.exp(-x) * (x ** (x-0.5)) * s else err = 1.0e-20 w = x t = 1.0 if x < 1.5 if x < err k = Integer(x) y = Float(k) - x if y.abs() < err or (1.0-y).abs() < err ier[0] = -1 end end if ier[0] == 0 while w < 1.5 t /= w w += 1.0 end end else if w > 2.5 while w > 2.5 w -= 1.0 t *= w end end end w -= 2.0 g = (((((((0.0021385778 * w - 0.0034961289) * w + 0.0122995771) * w - 0.00012513767) * w + 0.0740648982) * w + 0.0815652323) * w + 0.411849671) * w + 0.422784604) * w + 0.999999926 g *= t end return g end ################################################ # 標準正規分布N(0,1)の計算(P(X = x), P(X < x)) # w : P(X = x) # return : P(X < x) ################################################ def normal(x, w) # 確率密度関数(定義式) w[0] = Math.exp(-0.5 * x * x) / Math.sqrt(2.0*Math::PI) # 確率分布関数(近似式を使用) y = 0.70710678118654 * x.abs() z = 1.0 + y * (0.0705230784 + y * (0.0422820123 + y * (0.0092705272 + y * (0.0001520143 + y * (0.0002765672 + y * 0.0000430638))))) pp = 1.0 - z ** (-16.0) if x < 0.0 pp = 0.5 - 0.5 * pp else pp = 0.5 + 0.5 * pp end return pp end ####################################### # f分布の計算(P(X = ff), P(X < ff)) # dd : P(X = ff) # df1,df2 : 自由度 # return : P(X < ff) ####################################### def f(ff, dd, df1, df2) if ff < 1.0e-10 ff = 1.0e-10 end x = ff * df1 / (ff * df1 + df2) if df1%2 == 0 if df2%2 == 0 u = x * (1.0 - x) pp = x ia = 2 ib = 2 else u = 0.5 * x * Math.sqrt(1.0-x) pp = 1.0 - Math.sqrt(1.0-x) ia = 2 ib = 1 end else if df2%2 == 0 u = 0.5 * Math.sqrt(x) * (1.0 - x) pp = Math.sqrt(x) ia = 1 ib = 2 else u = Math.sqrt(x*(1.0-x)) / Math::PI pp = 1.0 - 2.0 * Math.atan2(Math.sqrt(1.0-x), Math.sqrt(x)) / Math::PI ia = 1 ib = 1 end end if ia != df1 i1 = ia while i1 < df1-1 pp -= 2.0 * u / i1 u *= x * (i1 + ib) / i1 i1 += 2 end end if ib != df2 i1 = ib while i1 < df2-1 pp += 2.0 * u / i1 u *= (1.0 - x) * (i1 + df1) / i1 i1 += 2 end end dd[0] = u / ff return pp end ############################################ # 二分法による非線形方程式(f(x)=0)の解 # x1,x2 : 初期値 # eps1 : 終了条件1(|x(k+1)-x(k)|<eps1) # eps2 : 終了条件2(|f(x(k))|<eps2) # max : 最大試行回数 # ind : 実際の試行回数 # (負の時は解を得ることができなかった) # fn : f(x)を計算する関数名 # return : 解 # coded by Y.Suganuma ############################################ def bisection(x1, x2, eps1, eps2, max, ind, &fn) x0 = 0.0 f1 = fn.call(x1) f2 = fn.call(x2) if f1*f2 > 0.0 ind[0] = -1 else ind[0] = 0 if f1*f2 == 0.0 if f1 == 0.0 x0 = x1 else x0 = x2 end else sw = 0 while sw == 0 && ind[0] >= 0 sw = 1 ind[0] += 1 x0 = 0.5 * (x1 + x2) f0 = fn.call(x0) if f0.abs() > eps2 if ind[0] <= max if (x1-x2).abs() > eps1 && (x1-x2).abs() > eps1*x2.abs() sw = 0 if f0*f1 < 0.0 x2 = x0 f2 = f0 else x1 = x0 f1 = f0 end end else ind[0] = -1 end end end end end return x0 end ############################################ # Newton法による非線形方程式(f(x)=0)の解 # x0 : 初期値 # eps1 : 終了条件1(|x(k+1)-x(k)|<eps1) # eps2 : 終了条件2(|f(x(k))|<eps2) # max : 最大試行回数 # ind : 実際の試行回数 # (負の時は解を得ることができなかった) # fn : f(x)とその微分を計算する関数名 # return : 解 # coded by Y.Suganuma ############################################ def newton(x0, eps1, eps2, max, ind, &fn) x1 = x0 x = x1 ind[0] = 0 sw = 0 while sw == 0 and ind[0] >= 0 sw = 1 ind[0] += 1 g = fn.call(0, x1) if g.abs() > eps2 if ind[0] <= max dg = fn.call(1, x1) if dg.abs() > eps2 x = x1 - g / dg if (x-x1).abs() > eps1 && (x-x1).abs() > eps1*x.abs() x1 = x sw = 0 end else ind[0] = -1 end else ind[0] = -1 end end end return x end ################################################################ # 標準正規分布N(0,1)のp%値(P(X > u) = 0.01p)(二分法を使用) # ind : >= 0 : normal(収束回数) # = -1 : 収束しなかった ################################################################ def p_normal(ind) normal_snx = Proc.new { |x| y = Array.new(1) 1.0 - $p - normal(x, y) } u = bisection(-7.0, 7.0, 1.0e-6, 1.0e-10, 100, ind, &normal_snx) return u end ###################################### # f分布のp%値(P(X > u) = 0.01p) # ind : >= 0 : normal(収束回数) # = -1 : 収束しなかった ###################################### def p_f(ind) f_snx = Proc.new { |sw, x| y = Array.new(1) z = f(x, y, $dof1, $dof2) if sw == 0 z - 1.0 + $p else z = y[0] end } max = 340 ff = 0.0 sw = 0 # 初期値計算の準備 # while文は,大きな自由度によるガンマ関数の # オーバーフローを避けるため while sw >= 0 df1 = 0.5 * ($dof1 - sw) df2 = 0.5 * $dof2 a = 2.0 / (9.0 * ($dof1 - sw)) a1 = 1.0 - a b = 2.0 / (9.0 * $dof2) b1 = 1.0 - b yq = p_normal(ind) e = b1 * b1 - b * yq * yq if e > 0.8 or $dof1+$dof2-sw <= max sw = -1 else sw += 1 if ($dof1-sw) == 0 sw = -2 end end end if sw == -2 ind[0] = -1 else # f0 : 初期値 if e > 0.8 x = (a1 * b1 + yq * Math.sqrt(a1*a1*b+a*e)) / e f0 = x ** 3.0 else y1 = Float($dof2) ** (df2-1.0) y2 = Float($dof1) ** df2 x = gamma(df1+df2, ind) / gamma(df1, ind) / gamma(df2, ind) * 2.0 * y1 / y2 / $p f0 = x ** (2.0/$dof2) end # ニュートン法 ff = newton(f0, 1.0e-6, 1.0e-10, 100, ind, &f_snx) end return ff end ####################################### # 分散分析(一元配置法と二元配置法) # method : 1 or 2 # np[0] : 因子1の水準数 # [1] : 因子2の水準数 # nn : データ数 # name[0] : 因子1の名前 # [1] : 因子2の名前 # a : a %値 # x : データ # coded by Y.Suganuma ####################################### def aov(method, np, nn, name, a, x) # 一元配置法 if method == 1 xi = Array.new(np[0]) for i1 in 0 ... np[0] xi[i1] = 0.0 for i2 in 0 ... nn xi[i1] += x[i1][0][i2] end xi[i1] /= nn end xa = 0.0 for i1 in 0 ... np[0] for i2 in 0 ... nn xa += x[i1][0][i2] end end xa /= (np[0] * nn) sp = 0.0 for i1 in 0 ... np[0] sp += (xi[i1] - xa) * (xi[i1] - xa) end sp *= nn se = 0.0 for i1 in 0 ... np[0] for i2 in 0 ... nn se += (x[i1][0][i2] - xi[i1]) * (x[i1][0][i2] - xi[i1]) end end vp = sp / (np[0] - 1) ve = se / (np[0] * (nn - 1)) fp = vp / ve $p = 0.01 * a $dof2 = np[0] * (nn - 1) sw = Array.new(1) print("全変動: 平方和 " + String(sp+se) + " 自由度 " + String(np[0]*nn-1) + "\n") $dof1 = np[0] - 1 print(name[0]) print("の水準間: 平方和 " + String(sp) + " 自由度 " + String(np[0]-1) + " 不偏分散 " + String(vp) + " F " + String(fp) + " " + String(a) + "%値 " + String(p_f(sw)) + "\n") print("水準内: 平方和 " + String(se) + " 自由度 " + String(np[0]*(nn-1)) + " 不偏分散 " + String(ve) + "\n") # 二元配置法 else xi = Array.new(np[0]) for i1 in 0 ... np[0] xi[i1] = 0.0 for i2 in 0 ... np[1] for i3 in 0 ... nn xi[i1] += x[i1][i2][i3] end end xi[i1] /= (np[1] * nn) end xj = Array.new(np[1]) for i1 in 0 ... np[1] xj[i1] = 0.0 for i2 in 0 ... np[0] for i3 in 0 ... nn xj[i1] += x[i2][i1][i3] end end xj[i1] /= (np[0] * nn) end xij = Array.new(np[0]) for i1 in 0 ... np[0] xij[i1] = Array.new(np[1]) for i2 in 0 ... np[1] xij[i1][i2] = 0.0 for i3 in 0 ... nn xij[i1][i2] += x[i1][i2][i3] end xij[i1][i2] /= nn end end xa = 0.0 for i1 in 0 ... np[0] for i2 in 0 ... np[1] for i3 in 0 ... nn xa += x[i1][i2][i3] end end end xa /= (np[0] * np[1] * nn) sp = 0.0 for i1 in 0 ... np[0] sp += (xi[i1] - xa) * (xi[i1] - xa) end sp *= (np[1] * nn) sq = 0.0 for i1 in 0 ... np[1] sq += (xj[i1] - xa) * (xj[i1] - xa) end sq *= (np[0] * nn) si = 0.0 for i1 in 0 ... np[0] for i2 in 0 ... np[1] si += (xij[i1][i2] - xi[i1] - xj[i2] + xa) * (xij[i1][i2] - xi[i1] - xj[i2] + xa) end end si *= nn se = 0.0 for i1 in 0 ... np[0] for i2 in 0 ... np[1] for i3 in 0 ... nn se += (x[i1][i2][i3] - xij[i1][i2]) * (x[i1][i2][i3] - xij[i1][i2]) end end end vp = sp / (np[0] - 1) vq = sq / (np[1] - 1) vi = si / ((np[0] - 1) * (np[1] - 1)) ve = se / (np[0] * np[1] * (nn - 1)) fp = vp / ve fq = vq / ve fi = vi / ve $p = 0.01 * a $dof2 = np[0] * np[1] * (nn - 1) sw = Array.new(1) print("全変動: 平方和 " + String(sp+sq+si+se) + " 自由度 " + String(np[0]*np[1]*nn-1) + "\n") $dof1 = np[0] - 1 print(name[0]) print("の水準間: 平方和 " + String(sp) + " 自由度 " + String(np[0]-1) + " 不偏分散 " + String(vp) + " F " + String(fp) + " " + String(a) + "%値 " + String(p_f(sw)) + "\n") $dof1 = np[1] - 1 print(name[1]) print("の水準間: 平方和 " + String(sq) + " 自由度 " + String(np[1]-1) + " 不偏分散 " + String(vq) + " F " + String(fq) + " " + String(a) + "%値 " + String(p_f(sw)) + "\n") $dof1 = (np[0] - 1) * (np[1] - 1) print("相互作用: 平方和 " + String(si) + " 自由度 " + String((np[0]-1)*(np[1]-1)) + " 不偏分散 " + String(vi) + " F " + String(fi) + " " + String(a) + "%値 " + String(p_f(sw)) + "\n") print("水準内: 平方和 " + String(se) + " 自由度 " + String(np[0]*np[1]*(nn-1)) + " 不偏分散 " + String(ve) + "\n") end end $p = 0.0 $dof1 = 1 $dof2 = 2 name = ["", ""] np = [1, 1] ss = gets().split(" ") method = Integer(ss[0]) # 因子の数 nn = Integer(ss[1]) # 各水準におけるデータ数 a = Float(ss[2]) # 有意水準(%) if method == 1 or method == 2 for i1 in 0 ... method ss = gets().split(" ") name[i1] = ss[0] np[i1] = Integer(ss[1]) end x = Array.new(np[0]) for i1 in 0 ... np[0] x[i1] = Array.new(np[1]) for i2 in 0 ... np[1] x[i1][i2] = Array.new(nn) end end for i1 in 0 ... np[1] for i2 in 0 ... nn ss = gets().split(" ") for i3 in 0 ... np[0] x[i3][i1][i2] = Float(ss[i3]) end end end aov(method, np, nn, name, a, x) else print("一元配置法,または,二元配置法だけです!\n") end =begin -------- 一元配置法に対するデータ例(コメント部分を除いて下さい)-------- 1 6 5 # 因子の数 各水準におけるデータ数 有意水準(%) 工場 3 # 因子の名前 水準の数 3.1 4.7 5.1 # 各水準に対する1番目のデータ 4.1 5.6 3.7 # 各水準に対する2番目のデータ 3.3 4.3 4.5 # 各水準に対する3番目のデータ 3.9 5.9 6.0 # 各水準に対する4番目のデータ 3.7 6.1 3.9 # 各水準に対する5番目のデータ 2.4 4.2 5.4 # 各水準に対する6番目のデータ -------- 二元配置法に対するデータ例(コメント部分を除いて下さい)-------- 2 3 5 # 因子の数 各水準におけるデータ数 有意水準(%) 薬剤 5 # 1番目の因子の名前 その水準の数 品種 2 # 2番目の因子の名前 その水準の数 3 4 12 -4 -4 # 1番目の因子の各水準に対して,2番目の因子の水準1に対する1番目のデータ 8 -8 31 12 19 # 1番目の因子の各水準に対して,2番目の因子の水準1に対する2番目のデータ 7 -5 8 0 23 # 1番目の因子の各水準に対して,2番目の因子の水準1に対する3番目のデータ 8 -10 9 10 15 # 1番目の因子の各水準に対して,2番目の因子の水準2に対する1番目のデータ -5 11 26 -1 13 # 1番目の因子の各水準に対して,2番目の因子の水準2に対する2番目のデータ 10 -6 13 -7 -6 # 1番目の因子の各水準に対して,2番目の因子の水準2に対する3番目のデータ =end