ラベル C・C++ の投稿を表示しています。 すべての投稿を表示
ラベル C・C++ の投稿を表示しています。 すべての投稿を表示

2016年7月29日金曜日

数字の桁数を取得する方法

数字の桁数を取得する関数って意外とない。。
結構使うものだと思うんだけど、なんで提供されてないんだろ?
備忘録にまとめとく

2016年7月23日土曜日

ユークリッドの互除法による約分プログラム


問題
次の分数は約分できますか。できるのであれば、約分してもとっとも簡単な分数で表しなさい
\[ \frac{10033}{12877} \]
ユークリッドやなーって感じの問題が数学検定1級の練習問題で出てきた
そういえば
ユーグリットの互除法は、プログラムだと再起関数の例題でよく見かけるが、実際組んだことなかったので
プログラムでこの問題を解いてみることにする

2016年5月21日土曜日

掃き出し法による連立方程式の解法プログラム

以前、クラメルの公式を使った連立方程式の解法を紹介したが、13元くらいからめちゃくちゃ遅くなることがわかった
そこで今回は一番よくつかわれてる掃き出し法による解法で作ってみる

N元連立1次方程式の解法プログラム(マルチスレッド版)
N元連立1次方程式の解法プログラム組んでみた


掃き出し法とは

$N$元一次方程式$Ax=b$の解法の一つ
係数行列$A$に定数項の列ベクトル$b$を加えた行列 $[A|b]$ に「行列の基本変形」を行い $[E|u]$ に変形したとき、方程式の解$x_i=u_i$となる
行列の基本変形
行の入れ替え:n行目とm行目を入れ替える
行の定数倍:n行目を定数倍する ( 定数≠0 )
行の加減算:n行目を定数倍した値をm行目に加減算する
上記「行列の基本変形」を使うことで、行列を変形させていき、解を導いていく方法が掃き出し法だ
言葉だけじゃよくわからないので例で計算してみる

2016年4月23日土曜日

モンティホール問題をシミュレーションしてみる

最近、統計学にハマってるので、有名なモンティホール問題をベイズの定理で解いてみる
また、プログラムも組んで理論解と数値解の比較もしてみる
モンティ・ホール問題
3つの箱があり、その内1つにアタリがある
最初に自分は箱を1つ選ぶ
その後、司会者は「自分が選んだ箱」以外からハズレの箱を1つ公開する
自分は箱を選び直す権利があるが、選び直すべきか?

2016年1月2日土曜日

android - ラズパイ間でのソケット通信

新年一発目はラズパイとアンドロイドでの通信!
C++言語(サーバ側)と、Java言語(クライアント)の入り乱れたややこしい分野から開始

2015年11月15日日曜日

C++の数値入力で無限ループにはまった件

ちょっとC++の勉強をしてる時に入力処理で無限ループにはまる事態に陥ったのでその対応方法を書いとく

問題の処理:1~9までの数字を入力する際、不正入力されたとき再入力をさせる処理
// C++の再入力処理.
#include<iostream>
using namespace std;
int main() {

    int num;

    // 1~9が入力されていなければ再入力
    cout << "Please enter the numbers(1-9) --> ";
    while(1) {
        cin >> num;
        if(num > 0 && num < 10)break;
        cout << "Please enter it again.(1-9) -->";
    }

    return 0;
}
このとき、半角英数字「a」を入力すると.......

2015年6月30日火曜日

確率とモンテカルロシミュレーション

問題
Aさんには三人の子供がいる
「日曜日生まれの男の子はいるか?」と訊くと「いる」と答えた
Aさんの子供が三人とも男の子である確率は?
最近2ちゃんまとめで上の問題やってみて、
これをモンテカルロでやってみたら結果は理論解に収束するのかな?
って思ったのでやってみた

 モンテカルロシミュレーションとは
シミュレーションや数値計算を乱数を用いて行う手法の総称。元々は、中性子が物質中を動き回る様子を探るために考案された。
強みは基礎的な理論さえ把握していれば、一定のアプローチに従って幅広い問題を解決できるという点
by Wiki
まぁ簡単にいえば、乱数つかって事象を表現して、それをループ文で何回も試し大数の法則でまとめ上げる手法

例:コインを投げた時の裏表の確率
表=1,裏=0として、0か1の乱数を生成。これを10万回(試行回数)繰り返し、1(表)が出たときの数をカウントしておく
コインが表になる確率は、「カウント数 / 10万(試行回数)」で求まる。


 理論解(ベイズの定理)
初見で1/4って思って恥じ書いたww三人のうち、一人が男確定だから、残り二人が男の確立Pは下の通り \[ P=\frac{1}{2}\cdot\frac{1}{2} =\frac{1}{4} \] ( ̄ー ̄) ドヤッ!
はい。日曜日を軽視してましたーww

これはどう見ても事後確率だから、ベイズの定理を使って解いてくのが定石
事象$A$:3人とも男が生まれる
事象$B$:少なくとも1人は日曜日に生まれの男がいる
  とおくと、求める確率$P(A|B)$はベイズの定理より \[ P(A|B)=\frac{P(A \land B)}{P(B)} \]
$P(B)$は「3人とも日曜生まれの男でない」場合の余事象なので \[ P(B)=1-\frac{ 13^3}{14^3}=\frac{547}{2744} \]
$P(A \land B)$は3人とも男で、うち少なくとも一人は日曜日に生まれてる \[ P(A \land B)= _3C_0\left(\frac{1}{14}\right)^3\left(\frac{6}{14}\right)^0+ _3C_1\left(\frac{1}{14}\right)^2\left(\frac{6}{14}\right)^1+ _3C_2\left(\frac{1}{14}\right)^1\left(\frac{6}{14}\right)^2\\ =\frac{127}{2744} \] よって \[ P(A|B)=\frac{P(A \land B)}{P(B)}=\frac{127}{547} \]
 数値解(モンテカルロ法)
今回はCで書いてみた
誕生曜日、性別を持つ構造体をつくり、事象Bを満たす三兄弟を乱数使って生成
三兄弟が全員男である場合をカウントしていく。
最後に、試行回数(ループ数)とカウンターの比率がP(A|B)の確率となる
#include<stdio.h>
#include<math.h>
#include <time.h>

#define SEX_BOY 0
#define SEX_GIRL 1

#define WEEK_SUNDAY    0
#define WEEK_MONDAY    1
#define WEEK_TUESDAY   2
#define WEEK_WEDNESDAY 3
#define WEEK_THURSDAY  4
#define WEEK_FRIDAY    5
#define WEEK_SATURDAY  6

#define LOOP_MAX 10000000

typedef struct {
    int sex;
    int week;
} human_t;

main() {
    srand((unsigned)time(NULL));
    long unsigned loop_count, check_count = 0;
    const human_t target_child = {SEX_BOY, WEEK_SUNDAY};
    human_t children[3];

    for (loop_count = 0; loop_count < LOOP_MAX; loop_count++) {

        // 事象B:少なくとも一人日曜生まれの男がいる条件を成立させる
        int check_flg = -1;
        while(check_flg != 0) {
            int i;
            for (i = 0; i < 3; i++) {
                human_t child = {rand() % 2, rand() % 7};
                children[i] = child;
                if (memcmp(&target_child, &child, sizeof(human_t)) == 0) {
                    check_flg = 0;
                }
            }
        }

        // 事象A:全員男の場合を調べる
        if( (children[0].sex == SEX_BOY)
              &&(children[1].sex == SEX_BOY)
              &&(children[2].sex == SEX_BOY)) {
            check_count++;
        }
    }
    double reslt = check_count/(double)LOOP_MAX;
    printf("reslt = %f",reslt);
}

結果は
reslt = 0.232030

理論解を小数点表記にすると、 127/547 = 0.232175503
よって、誤差は0.0006
いい感じ(^q^)

★ちなみに★

事象Bのロジックについて。。。。
ソースの31~42行目で生成してる三兄弟は
三兄弟を乱数で生成 → その中に日曜生まれ男がいるかチェック → いれば三兄弟を指標として採用。いなければ作り直し
// 事象B:少なくとも一人日曜生まれの男がいる条件を成立させる
int check_flg = -1;
while(check_flg != 0) {
    int i;
    for (i = 0; i < 3; i++) {
        human_t child = {rand() % 2, rand() % 7};
        children[i] = child;
        if (memcmp(&target_child, &child, sizeof(human_t)) == 0) {
            check_flg = 0;
        }
    }
}
となってる。
これが面倒で
三兄弟を乱数で生成、その後、兄弟の誰かを日曜生まれ男に差し替える
// 事象B:少なくとも一人日曜生まれの男がいる条件を成立させる
human_t children[3] = {
    {rand()%2, rand()%7},
    {rand()%2, rand()%7},
    {rand()%2, rand()%7}
};
// 三人のうち誰かを日曜生まれ男に差し替え
children[rand()%3] = target_child;
とか
長男を日曜生まれ男で固定し、残り二人を乱数で生成
// 事象B:少なくとも一人日曜生まれの男がいる条件を成立させる
human_t children[3] = {
    {SEX_BOY, WEEK_SUNDAY}, // 日曜男
    {rand()%2, rand()%7},
    {rand()%2, rand()%7}
};
などをすると、数値解析結果は
  • 三兄弟を乱数で生成、その後、兄弟の誰かを日曜生まれ男に差し替える
  • reslt = 0.249997
  • 長男を日曜生まれ男で固定し、残り二人を乱数で生成
  • reslt = 0.249940

僕が理論解でミスった値、1/4に収束していることがわかる
これは事後確率であることがプログラム上で示せていないため
つまり、ちょっと難しいが、モンテカルロシミュレーションでも事後確率と普通の確率は区別して求めることができる!

 まとめ
モンテカルロシミュレーションで事後確率を示すには、
前提条件の定義さえしっかりプログラムできていれば求めることができる!