Fisher の直接確率

/****************************/
/* 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;
}