Up

2項分布の信頼区間

openSUSE13.2でのg++イントール日本語入力設定の説明もこのウェブサイトの後半で行っている。

 

2項分布

のパラメータについて、信頼係数の信頼区間を求める。

N回の試行においてk回当該の事象が生起したときの信頼区間は、次式

で与えられる(岡本、1998pp. 176-179)。

 上記の方法で与えられる信頼区間と確率との関係は、「芝・南風原(1990)、pp. 90-92」で論じられている。

確率の計算(2)および(3)における計算の精度を上げるため、double 型ではなく、long double 型を用いる。long double 型の精度を確かめるプログラムを、このウェブサイトの後半に用意している。

式(2)、(3)によって信頼区間を求めるプログラムを以下に示す。

 

================= ファイル main.cpp ===========================

#include <iostream>

#include <cmath>

#include "common.h"

 

using namespace std;

using namespace cmmn;

 

int main(){

      while (true) {

      L1:     cout << "p(%) = ";

              cin >> p;

              if (p <= 0.0) break;

              if ((p < 50.0) || (100.0 <= p)){

                      goto L1;

              }

      L2:     pa = (100.0 - p)/100.0;

              cout << "N = ";

              cin >> N;

              if (N < 1){

                      goto L2;

              }

      L3:     cout << "k = ";

              cin >> k;

              if ((k < 0) || (k > N)){

                      goto L3;

              }

              LogCombi = new long double[N+1];

              for (int i = 0; i <= N; i++){

                      if ((i == 0) || (i == N)) LogCombi[i] = 0.0;

                      else {

                              long double v = 0.0;

                              for (int j = i; j >= 1; j--)

                                      v += log(N + 1.0 - j) - log( j );

                              LogCombi[i] = v;

                      }

              }

 

              long double LB, UB;

              try{

                      if (UCum_prob(0.0) > (pa/2.0)){

                              LB = 0.0;

                              Bisection( Cum_prob, -1.0, pa/2.0, 0.0, 1.0, UB, 1.0e-9 );

                      }else{

                              if (Cum_prob(1.0) > (pa/2.0)){

                                      UB = 1.0;

                                      Bisection( UCum_prob, 1.0, pa/2.0, 0.0, 1.0, LB, 1.0e-9 );

                              }else{

                                      Bisection( Cum_prob, -1.0, pa/2.0, 0.0, 1.0, UB, 1.0e-9 );

                                      Bisection( UCum_prob, 1.0, pa/2.0, 0.0, 1.0, LB, 1.0e-9 );

                              }

                      }

                      cout << p << "% Interval[" << LB << ", " << UB << "]" << endl;

              }

              catch (char const * e){

                      cout << "Error-> " << e << endl;

              }

 

              delete [] LogCombi;

      }

 

      return 0;

}

==================== ファイル main.cpp ここまで =========================

 

ヘッダーファイルcommon.hとそのソースファイルcommon.cppは、以下に示す。

 

=================== ファイル common.h ==================================

#ifndef COMMON_CK

#define COMMON_Ck

 

namespace cmmn {

 

      extern long double p, pa;

 

      extern int N, k;

 

      extern long double * LogCombi;

 

 

      //     Bisection 

      void Bisection( long double (*f)(long double),

                        long double s,                        //  f is increasing -> s > 0.0

                                                              //  f is decreasing -> s < 0.0

                        long double c,                        //  f(Root) = c

                        long double L_b,                      //  L_b < Root < U_b

                        long double U_b,     

                        long double & Root,           //  Set Root so that f(Root) = c

                        long double acc );                    //  accuracy

 

 

      long double prob_term( long double p, int N, int i );

 

      long double Cum_prob( long double p );

 

 

      long double UCum_prob( long double p );

 

}

 

#endif

==================== common.hここまで ==============================

 

==================== ファイルcommon.cpp =============================

#include <sstream>

#include <cmath>

#include "common.h"

 

using namespace std;

 

namespace cmmn {

 

      long double p, pa;

 

      int N, k;

 

      long double * LogCombi;

 

 

 

      //     Bisection

 

      void Bisection( long double (*f)(long double),

                        long double s,                        //  f is increasing -> s > 0.0

                                                              //  f is decreasing -> s < 0.0

                        long double c,                        //  f(Root) = c

                        long double L_b,                      //  L_b < Root < U_b

                        long double U_b,             

                        long double & Root,           //  Set Root so that f(Root) = c

                        long double acc ){                    //  accuracy

              if ( (f(L_b) - c) * (f(U_b) - c) > 0.0 ){

                      ostringstream sout;

                      sout << "Invalid values...L_b = " << L_b << "  U_b = " << U_b;

                      string strmsg = sout.str();

                      char * cstr = new char[strmsg.length() + 1];

                      for (int i = 0; i < strmsg.length(); i++)

                              cstr[i] = strmsg[i];

                      cstr[strmsg.length()] = 0;

 

                      throw cstr;

              }

              do {

                      long double m = 0.5 * (L_b + U_b);

                      long double v = f(m);

                      if ( s * (v - c) > 0.0 ) {

                              U_b = m;

                      } else {

                              L_b = m;

                      }

              } while ( (abs(U_b - L_b) >= acc * abs(U_b))

                                      &&

                                ( (abs(U_b) + abs(L_b)) >= 1.0e-14)

                              );

              Root = 0.5 * (L_b + U_b);

      }

 

 

 

      long double prob_term( long double p, int N, int i ){

              if (p <= 0.0){

                              if (i >= 1) return 0.0;

                              else            return 1.0;

              }else if (p >= 1.0){

                              if (i == N) return 1.0;

                              else            return 0.0;

              }else{

                      return exp(LogCombi[i] + (i * log(p) + (N - i) * log(1.0 - p)));

              }

      }

 

      long double Cum_prob( long double p ){

              long double v = 0.0;

              for (int i = 0; i <= k; i++)

                      v += prob_term( p, N, i );

              return v;

      }

 

 

      long double UCum_prob( long double p ){

              long double v = 0.0;

              for (int i = N; i >= k; i--)

                      v += prob_term( p, N, i );

              return v;

      }

 

 

}

=================== common.cppここまで ==============================

 

上の3つのファイルを、圧縮ファイルPrgBinomial.zipにまとめた。圧縮ファイル名PrgBinomial.zipをクリックしてダウンロードすることができる。

 

2項分布(1)における2項係数は、そのまま計算するとオーバーフローを起こしやすい。2項係数の計算におけるオーバーフローと確率計算におけるアンダーフローを避けるため、式(1)を対数変換を利用した式

を用いて計算している。

 

上のプログラムをopenSUSE 13.2 で実行した例を図1に示す。

図1

 

 

コマンド g++ は、openSUSE13.2 (DVD)をインストールしただけでは使えないので、g++ のインストールを行う必要がある。g++ のインストールは、以下の手順で出来る。

まず、「YaST2モジュ」を選ぶ(図2)。

 

図2

 

次に、「ソフトウェア管理」を選ぶ(図3)。

図3

 

表示された「YaST2」において、検索ページの検索欄に「gcc-c++」を設定して検索すると、右のペインに「gcc-c++」と「gcc-c++-32bit」が表示されるので、チェックを入れて選択する(図4)。

図4

 

上の設定の下で「了解」ボタンをクリックするとインストールが始まる。

 

また、openSUSE13.2のインストール後は、画面上のバーの「_ja」をクリックすると図5のように表示されるが、この状態では日本語入力はできない。

図5

 

日本語入力は、以下の手順でできるようになる。

まず、「設定」を選ぶ(図6)。

図6

 

次に、「地域と言語」を選ぶ(図7)。

図7

 

「入力ソース」で「+」をクリックする(図8)。

図8

 

表示された「入力ソースの追加」において、「日本語」を選び(クリックし)(図9)、続いて「日本語(Mozc – JP layout)」を選んで「追加」をクリックする(図10)。

図9

 

図10

 

 

 

上の操作の後は、「日本語(Mozc – JP layout)」が選べる(図11)。

図11

 

「日本語(Mozc – JP layout)」を選んだ状態では、「半角/全角 漢字」キーの押下で、日本語入力(全角)、英数字(半角)の切り替えができる。

 

 

long double 型の精度

long double 型の精度を確かめるプログラムを以下のように用意した。

 

==============================================

#include <iostream>

#include <iomanip>

 

using namespace std;

 

int main(){

        long double v = 1.0, x = 1.0, u;

        int i = 0;

        do {

                i++;

                x *= 0.1;

                u = v + x;

                cout << "i = " << i << "   x = "

                        << setprecision(10) << x << "   u = "

                        << setprecision(21) << u << endl;

        } while (u > v);

        return 0;

}

===============================================

 

上のプログラムをopenSUSE13.2で実行すると図12の結果を得る。

図12

 

20桁ぐらいの精度であることがわかる。

 

long double型の値の範囲をdouble型のものと比較するプログラムを次のように作成した。

 

==============================================

#include <iostream>

#include <iomanip>

#include <cmath>

 

using namespace std;

 

int main(){

        long double v = 11000.0;

        double u = 600.0;

        for (int i = 0; i < 15; i++){

                cout << "v = " << v << ":  " << setw(20) << left << exp(v)

                        << "   u = " << u << ":  " << exp(u) << endl;

                v += 100.0;

                u += 10.0;

        }

 

        return 0;

}

=============================================

 

上のプログラムの実行結果を図13に示す。

図13

 

long double型のvは、double型のuより広い範囲の数値を表すことがわかる。

 

 

参考文献

岡本安晴(1998)「Delphiで学ぶデータ分析法」CQ出版

祐順・南風原朝和(1990)「行動科学における統計解析法」東京大学出版会

 

Up