ーーーーーーー AWT を利用した場合 ーーーーーーーー
/****************************/
/* Fisher の直接確率(AWT) */
/*      coded by Y.Suganuma */
/****************************/
import java.awt.*;
import java.awt.event.*;

public class Test {
	public static void main (String[] args)
	{
		Fisher fr = new Fisher("Fisher の直接確率");
	}
}

class Fisher extends Frame implements ActionListener, TextListener
{
	double bunshi, bunbo, d_sum, base;
	int n_row, n_col, matrix[][], sum;
	TextField row, col, tx[][];
	TextArea ta;
	Button bt;
	ScrollPane sp;

	/******************/
	/* コンストラクタ */
	/******************/
	Fisher(String name)
	{
					// Frameクラスのコンストラクタ
		super(name);
					// Windowの大きさ
		setSize(600, 400);
					// レイアウト,背景色,フォント
		setLayout(new BorderLayout(5, 10));
		setBackground(new Color(225, 255, 225));
		Font f = new Font("TimesRoman", Font.BOLD, 20);
		setFont(f);
					// 上のパネル
		Panel pn1 = new Panel();
		add(pn1, BorderLayout.NORTH);
		pn1.add(new Label("行の数"));
		row = new TextField(3);
		row.addTextListener(this);
		pn1.add(row);
		pn1.add(new Label("列の数"));
		col = new TextField(3);
		col.addTextListener(this);
		pn1.add(col);
		pn1.add(new Label(" "));
		bt = new Button("OK");
		bt.setBackground(Color.pink);
		bt.addActionListener(this);
		pn1.add(bt);
					// 中央のパネル
		sp = new ScrollPane(ScrollPane.SCROLLBARS_AS_NEEDED);
		add(sp, BorderLayout.CENTER);
					// 下のパネル
		Panel pn3 = new Panel();
		add(pn3, BorderLayout.SOUTH);
		ta = new TextArea(3, 35);
		pn3.add(ta);
					// ウィンドウを表示
		setVisible(true);
					// イベントアダプタ
		addWindowListener(new WinEnd());
	}

	/******************************/
	/* 上,左,下,右の余白の設定 */
	/******************************/
	public Insets getInsets()
	{
		return new Insets(70, 20, 20, 20);
	}

	/******************************/
	/* ボタンが押されたときの処理 */
	/******************************/
	public void actionPerformed(ActionEvent e)
	{
		double x, s;
		int i1, i2, k;
		String str;
							// 和の計算
		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++) {
				str = tx[i1][i2].getText();
				if (str.length() > 0) {
					matrix[i1][i2] = Integer.parseInt(str);
					sum += matrix[i1][i2];
				}
				else
					matrix[i1][i2] = 0;
				matrix[i1][n_col] += matrix[i1][i2];
				matrix[n_row][i2] += matrix[i1][i2];
			}
		}
							// 実行
		if (sum == 0) {
			ta.setForeground(Color.red);
			ta.setText(" データを入力してください\n");
		}
		else {
			ta.setForeground(Color.black);
			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);
			base  = Math.exp(bunshi - bunbo);
			ta.setText("生起確率: " + base + "\n");
									// base より小さい確率の和
			int mx[][] = new int [n_row+1][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(mx, 0);
				k++;
			}
			ta.append("確率の和: " + s + "\n");
		}
	}

	/*******************************************/
	/* 特定の状態における確率の分母(対数計算) */
	/*******************************************/
	double cal(int mx[][])
	{
		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;
	}

	/**********************/
	/* n の階乗(対数計算) */
	/**********************/
	double kaijo(int n)
	{
		double s = 0.0;
		for (int i1 = 2; i1 <= n; i1++)
			s += Math.log((double)i1);
		return s;
	}

	/************************************/
	/* 様々な状態における確率を計算     */
	/*      mx : 状態                   */
	/*      i : 値を設定する位置        */
	/*            i/n_col 行 i%n_col 列 */
	/************************************/
	double set(int mx1[][], int i)
	{
		double x, y, s = 0.0;
		int i1, i2, k, k1, k2, n, n1, n2, sw = 0, ss = 0;
					// 周辺度数と一致しているかのチェック
		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);
			x = Math.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++)
//					System.out.print(" " + mx1[i1][i2]);
//				System.out.println();
//			}
//			System.out.println();
		}
					// 一致していない場合
		else {
			if (i < n_row*n_col-1 && (cal(mx1) + 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;
				int mx2[][] = new int [n_row+1][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(mx2, i);
						}
					}
					k++;
				}
			}
		}

		return s;
	}

	/******************************/
	/* 列数が変更されたときの処理 */
	/******************************/
	public void textValueChanged(TextEvent e)
	{
		try {
			if ((row.getText()).length() > 0 && (col.getText()).length() > 0) {
				ta.setForeground(Color.black);
				n_row  = Integer.parseInt(row.getText());
				n_col  = Integer.parseInt(col.getText());
				matrix = new int [n_row+1][n_col+1];
				tx     = new TextField [n_row][n_col];
				remove(sp);
				sp = new ScrollPane(ScrollPane.SCROLLBARS_AS_NEEDED);
				add(sp, BorderLayout.CENTER);
				Panel pn2 = new Panel();
				pn2.setLayout(new GridLayout(n_row, n_col, 10, 10));
				for (int i1 = 0; i1 < n_row; i1++ ) {
					for (int i2 = 0; i2 < n_col; i2++) {
						tx[i1][i2] = new TextField(2);
						pn2.add(tx[i1][i2]);
					}
				}
				sp.add(pn2);
				validate();
			}
		}
		catch (NumberFormatException em) {}
	}

	/************/
	/* 終了処理 */
	/************/
	class WinEnd extends WindowAdapter
	{
		public void windowClosing(WindowEvent e) {
			System.exit(0);
		}
	}
}
		
ーーーーーーー Swing を利用した場合 ーーーーーーーー
/*****************************/
/* Fisher の直接確率(Swing)*/
/*      coded by Y.Suganuma  */
/*****************************/
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;

public class Test {
	public static void main (String[] args)
	{
		Fisher fr = new Fisher("Fisher の直接確率");
	}
}

class Fisher extends JFrame implements ActionListener, DocumentListener
{
	double bunshi, bunbo, d_sum, base;
	int n_row, n_col, matrix[][], sum;
	JTextField row, col, tx[][];
	JTextArea ta;
	JButton bt;
	JScrollPane sp;
	Container cp;
	Font f = new Font("TimesRoman", Font.BOLD, 20);

	/******************/
	/* コンストラクタ */
	/******************/
	Fisher(String name)
	{
					// Frameクラスのコンストラクタ
		super(name);
					// Windowの大きさ
		setSize(600, 400);
					// レイアウト,背景色,フォント
		cp = getContentPane();
		cp.setLayout(new BorderLayout(5, 5));
		cp.setBackground(new Color(225, 255, 225));
		cp.setFont(f);
					// 上のパネル
		Panel pn1 = new Panel();
		cp.add(pn1, BorderLayout.NORTH);
		pn1.add(new Label("行の数"));
		row = new JTextField(3);
		row.setFont(f);
		row.getDocument().addDocumentListener(this);
		pn1.add(row);
		pn1.add(new Label("列の数"));
		col = new JTextField(3);
		col.setFont(f);
		col.getDocument().addDocumentListener(this);
		pn1.add(col);
		pn1.add(new Label(" "));
		bt = new JButton("OK");
		bt.setBackground(Color.pink);
		bt.setFont(f);
		bt.addActionListener(this);
		pn1.add(bt);
					// 中央のパネル
		sp = new JScrollPane();
		cp.add(sp, BorderLayout.CENTER);
					// 下のパネル
		Panel pn3 = new Panel();
		cp.add(pn3, BorderLayout.SOUTH);
		ta = new JTextArea(3, 24);
		ta.setFont(f);
		JScrollPane scroll2 = new JScrollPane(ta);
		pn3.add(scroll2);
					// ウィンドウを表示
		setVisible(true);
					// イベントアダプタ
		addWindowListener(new WinEnd());
	}

	/******************************/
	/* 上,左,下,右の余白の設定 */
	/******************************/
	public Insets getInsets()
	{
		return new Insets(70, 20, 20, 20);
	}

	/******************************/
	/* ボタンが押されたときの処理 */
	/******************************/
	public void actionPerformed(ActionEvent e)
	{
		double x, s;
		int i1, i2, k;
		String str;
							// 和の計算
		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++) {
				str = tx[i1][i2].getText();
				if (str.length() > 0) {
					matrix[i1][i2] = Integer.parseInt(str);
					sum += matrix[i1][i2];
				}
				else
					matrix[i1][i2] = 0;
				matrix[i1][n_col] += matrix[i1][i2];
				matrix[n_row][i2] += matrix[i1][i2];
			}
		}
							// 実行
		if (sum == 0) {
			ta.setForeground(Color.red);
			ta.setText(" データを入力してください\n");
		}
		else {
			ta.setForeground(Color.black);
			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);
			base  = Math.exp(bunshi - bunbo);
			ta.setText("生起確率: " + base + "\n");
									// base より小さい確率の和
			int mx[][] = new int [n_row+1][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(mx, 0);
				k++;
			}
			ta.append("確率の和: " + s + "\n");
		}
	}

	/*******************************************/
	/* 特定の状態における確率の分母(対数計算) */
	/*******************************************/
	double cal(int mx[][])
	{
		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;
	}

	/**********************/
	/* n の階乗(対数計算) */
	/**********************/
	double kaijo(int n)
	{
		double s = 0.0;
		for (int i1 = 2; i1 <= n; i1++)
			s += Math.log((double)i1);
		return s;
	}

	/************************************/
	/* 様々な状態における確率を計算     */
	/*      mx : 状態                   */
	/*      i : 値を設定する位置        */
	/*            i/n_col 行 i%n_col 列 */
	/************************************/
	double set(int mx1[][], int i)
	{
		double x, y, s = 0.0;
		int i1, i2, k, k1, k2, n, n1, n2, sw = 0, ss = 0;
					// 周辺度数と一致しているかのチェック
		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);
			x = Math.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++)
//					System.out.print(" " + mx1[i1][i2]);
//				System.out.println();
//			}
//			System.out.println();
		}
					// 一致していない場合
		else {
			if (i < n_row*n_col-1 && (cal(mx1) + 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;
				int mx2[][] = new int [n_row+1][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(mx2, i);
						}
					}
					k++;
				}
			}
		}

		return s;
	}

	/******************************/
	/* 列数が変更されたときの処理 */
	/******************************/
	public void changedUpdate(DocumentEvent e) {}
	public void removeUpdate(DocumentEvent e) {}
	public void insertUpdate(DocumentEvent e)
	{
		try {
			if ((row.getText()).length() > 0 && (col.getText()).length() > 0) {
				remove(sp);
				ta.setForeground(Color.black);
				n_row  = Integer.parseInt(row.getText());
				n_col  = Integer.parseInt(col.getText());
				matrix = new int [n_row+1][n_col+1];
				tx     = new JTextField [n_row][n_col];
				Panel pn2 = new Panel();
				pn2.setBackground(new Color(225, 255, 225));
				pn2.setLayout(new GridLayout(n_row, n_col, 5, 5));
				for (int i1 = 0; i1 < n_row; i1++ ) {
					for (int i2 = 0; i2 < n_col; i2++) {
						tx[i1][i2] = new JTextField(2);
						tx[i1][i2].setFont(f);
						pn2.add(tx[i1][i2]);
					}
				}
				sp = new JScrollPane(pn2);
				cp.add(sp, BorderLayout.CENTER);
				validate();
			}
		}
		catch (NumberFormatException em) {}
	}

	/************/
	/* 終了処理 */
	/************/
	class WinEnd extends WindowAdapter
	{
		public void windowClosing(WindowEvent e) {
			System.exit(0);
		}
	}
}