/****************************/ /* Fisher の直接確率 */ /* coded by Y.Suganuma */ /****************************/ #include <stdio.h> #include <math.h> /**********************/ /* n の階乗(対数計算) */ /**********************/ double kaijo(int n) { double s = 0.0; for (int i1 = 2; i1 <= n; i1++) s += log((double)i1); return s; } /*******************************************/ /* 特定の状態における確率の分母(対数計算) */ /* n_row,n_col : 行と列数 */ /* d_sum : 総和の階乗(対数) */ /*******************************************/ double cal(int **mx, int n_row, int n_col, double d_sum) { double s = d_sum; int i1, i2; for (i1 = 0; i1 < n_row; i1++) { for (i2 = 0; i2 < n_col; i2++) s += kaijo(mx[i1][i2]); } return s; } /********************************************/ /* 様々な状態における確率を計算 */ /* matrix : 基本状態 */ /* mx : 状態 */ /* n_row,n_col : 行と列数 */ /* d_sum : 総和の階乗(対数) */ /* bunshi,bunbo : 基準行列の分子と分母 */ /* sum : 基準行列の総和 */ /* base : 基準確率 */ /* i : 値を設定する位置 */ /* i/n_col 行 i%n_col 列 */ /********************************************/ double set(int **matrix, int **mx1, int n_row, int n_col, double bunshi, double bunbo, int sum, double d_sum, double base, int i) { double x, y, s = 0.0; int i1, i2, k, k1, k2, n, n1, n2, sw = 0, ss = 0, **mx2; // 周辺度数と一致しているかのチェック for (i1 = 0; i1 < n_row; i1++) mx1[i1][n_col] = 0; for (i1 = 0; i1 < n_col; i1++) mx1[n_row][i1] = 0; for (i1 = 0; i1 < n_row; i1++) { for (i2 = 0; i2 < n_col; i2++) { mx1[i1][n_col] += mx1[i1][i2]; mx1[n_row][i2] += mx1[i1][i2]; ss += mx1[i1][i2]; } } for (i1 = 0; i1 < n_row && sw == 0; i1++) { if (mx1[i1][n_col] != matrix[i1][n_col]) sw = 1; } if (sw == 0) { for (i1 = 0; i1 < n_col && sw == 0; i1++) { if (mx1[n_row][i1] != matrix[n_row][i1]) sw = 1; } } // 一致している場合 if (sw == 0) { y = cal(mx1, n_row, n_col, d_sum); x = exp(bunshi - y); if (x < base + 1.0e-10) s = x; // デバッグ用(計算した組み合わせの出力) // for (i1 = 0; i1 < n_row; i1++) { // for (i2 = 0; i2 < n_col; i2++) // printf(" %d", mx1[i1][i2]); // printf("\n"); // } // printf("\n"); } // 一致していない場合 else { if (i < n_row*n_col-1 && (cal(mx1,n_row,n_col,d_sum) + kaijo(sum-ss)) > bunbo - 1.0e-10) { i++; k1 = i / n_col; k2 = i % n_col; n1 = 0; n2 = 0; for (i1 = 0; i1 < n_row; i1++) n1 += mx1[i1][k2]; for (i1 = 0; i1 < n_col; i1++) n2 += mx1[k1][i1]; n1 = matrix[n_row][k2] - n1; n2 = matrix[k1][n_col] - n2; n = (n1 < n2) ? n1 : n2; k = 0; mx2 = new int *[n_row+1]; for (i1 = 0; i1 < n_row+1; i1++) mx2[i1] = new int [n_col+1]; while (k <= n) { if (k2 < n_col-1 || k2 == n_col-1 && mx1[k1][n_col]+k == matrix[k1][n_col]) { if (k1 < n_row-1 || k1 == n_row-1 && mx1[n_row][k2]+k == matrix[n_row][k2]) { for (i1 = 0; i1 < n_row; i1++) { for (i2 = 0; i2 < n_col; i2++) mx2[i1][i2] = mx1[i1][i2]; } mx2[k1][k2] = k; s += set(matrix, mx2, n_row, n_col, bunshi, bunbo, sum, d_sum, base, i); } } k++; } for (i1 = 0; i1 < n_row+1; i1++) delete [] mx2[i1]; delete [] mx2; } } return s; } /********/ /* main */ /********/ int main() { double bunshi, bunbo, d_sum, base, s; int i1, i2, k, n_row, n_col, **mx, **matrix, sum; // データの入力 // printf("行と列の数は? "); scanf("%d %d", &n_row, &n_col); matrix = new int *[n_row+1]; for (i1 = 0; i1 < n_row+1; i1++) matrix[i1] = new int [n_col+1]; for (i1 = 0; i1 < n_row; i1++) { // printf("%d 行目のデータは(スペースで区切って)? ", i1+1); for (i2 = 0; i2 < n_col; i2++) scanf("%d", &matrix[i1][i2]); } // 和の計算 for (i1 = 0; i1 < n_row; i1++) matrix[i1][n_col] = 0; for (i1 = 0; i1 < n_col; i1++) matrix[n_row][i1] = 0; sum = 0; for (i1 = 0; i1 < n_row; i1++) { for (i2 = 0; i2 < n_col; i2++) { sum += matrix[i1][i2]; matrix[i1][n_col] += matrix[i1][i2]; matrix[n_row][i2] += matrix[i1][i2]; } } // 生起確率の計算 d_sum = kaijo(sum); bunshi = 0.0; for (i1 = 0; i1 < n_row; i1++) bunshi += kaijo(matrix[i1][n_col]); for (i1 = 0; i1 < n_col; i1++) bunshi += kaijo(matrix[n_row][i1]); bunbo = cal(matrix, n_row, n_col, d_sum); base = exp(bunshi - bunbo); printf("生起確率: %f\n", base); // base より小さい確率の和 mx = new int *[n_row+1]; for (i1 = 0; i1 < n_row+1; i1++) mx[i1] = new int [n_col+1]; k = 0; s = 0.0; while (k <= matrix[0][n_col] && k <= matrix[n_row][0]) { for (i1 = 0; i1 < n_row; i1++) { for (i2 = 0; i2 < n_col; i2++) mx[i1][i2] = 0; } mx[0][0] = k; s += set(matrix, mx, n_row, n_col, bunshi, bunbo, sum, d_sum, base, 0); k++; } printf("確率の和: %f\n", s); // 領域の開放 for (i1 = 0; i1 < n_row+1; i1++) delete [] matrix[i1]; delete [] matrix; for (i1 = 0; i1 < n_row+1; i1++) delete [] mx[i1]; delete [] mx; return 0; }