演習問題11解答例

問1 分数表現による加減乗除
問2 時刻の加算と減算
問3 日付の加算と減算
問4 行列の加減乗除

[問1]例えば,
のように,加減乗除に対する分数演算を実行するためのクラスを定義せよ.ただし,約分は考慮しなくても良い.
/****************************/
/* 分数の加減乗除           */
/*      coded by Y.Suganuma */
/****************************/
#include <iostream>
/*
     クラスBunsuの定義
*/
class Bunsu {
		int sei;   // 整数部分
		int shi;   // 分子
		int bo;    // 分母
	public:

		/******************/
		/* コンストラクタ */
		/******************/
		Bunsu() {}

		Bunsu(int n, int m, int l=0)
		{
			sei = l;
			shi = n;
			bo  = m;
		}

		/**********************/
		/* +のオーバーロード */
		/**********************/
		Bunsu operator + (const Bunsu &b)
		{
			Bunsu c;

			c.shi  = (sei * bo + shi) * b.bo + (b.sei * b.bo + b.shi) * bo;
			c.bo   = bo * b.bo;
			c.sei  = c.shi / c.bo;
			c.shi -= c.bo * c.sei;

			return c;
		}

		/**********************/
		/* -のオーバーロード */
		/**********************/
		Bunsu operator - (const Bunsu &b)
		{
			Bunsu c;

			c.shi  = (sei * bo + shi) * b.bo - (b.sei * b.bo + b.shi) * bo;
			c.bo   = bo * b.bo;
			c.sei  = c.shi / c.bo;
			c.shi -= c.bo * c.sei;

			return c;
		}

		/**********************/
		/* *のオーバーロード */
		/**********************/
		Bunsu operator * (const Bunsu &b)
		{
			Bunsu c;

			c.shi  = (sei * bo + shi)  * (b.sei * b.bo + b.shi);
			c.bo   = bo * b.bo;
			c.sei  = c.shi / c.bo;
			c.shi -= c.bo * c.sei;

			return c;
		}

		/*********************/
		/* /のオーバーロード */
		/*********************/
		Bunsu operator / (const Bunsu &b)
		{
			Bunsu c;

			c.shi  = (sei * bo + shi)  * b.bo;
			c.bo   = bo * (b.sei * b.bo + b.shi);
			c.sei  = c.shi / c.bo;
			c.shi -= c.bo * c.sei;

			return c;
		}

	friend istream &operator >> (istream &, Bunsu &);   // >> のオーバーロード
	friend ostream &operator << (ostream &, Bunsu);     // << のオーバーロード
};

/********************************/
/* 入力( >> のオーバーロード) */
/********************************/
istream &operator >> (istream &stream, Bunsu &a)
{
	std::cout << "n (m / l)? ";
	stream >> a.sei >> a.shi >> a.bo;
	return stream;
}

/********************************/
/* 出力( << のオーバーロード) */
/********************************/
ostream &operator << (ostream &stream, Bunsu a)
{
	if (a.sei == 0)
		stream << "(" << a.shi << " / " << a.bo << ")\n";
	else
		stream << a.sei << "(" << a.shi << " / " << a.bo << ")\n";
	return stream;
}

/************/
/* main関数 */
/************/
int main()
{
	Bunsu a, b;

	std::cout << "Bunsu a  ";
	std::cin >> a;
	std::cout << "Bunsu b  ";
	std::cin >> b;
					// +
	std::cout << "--- a+b ---\n";
	std::cout << "   " << a+b;
					// -
	std::cout << "--- a-b ---\n";
	std::cout << "   " << a-b;
					// *
	std::cout << "--- a*b ---\n";
	std::cout << "   " << a*b;
					// /
	std::cout << "--- a/b ---\n";
	std::cout << "   " << a/b;

	return 0;
}
		
[問2]時刻の加算及び減算を行うクラスを定義せよ.
/****************************/
/* 時間の加算と減算         */
/*      coded by Y.Suganuma */
/****************************/
#include <iostream>
/*
     クラスTimeの定義
*/
class Time {
		int hour;              // 時間
		int min;               // 分
		int sec;               // 秒
	public:

		/******************/
		/* コンストラクタ */
		/******************/
		Time() {}

		Time(int n, int m=0, int l=0)
		{
			hour = n;
			min  = m;
			sec  = l;
		}

		/**********************/
		/* +のオーバーロード */
		/**********************/
		Time operator + (const Time &b)
		{
			Time c;
			int k;

			k       = hour * 3600 + min * 60 + sec + b.hour * 3600 + b.min * 60 + b.sec;
			c.hour  = k / 3600;
			k      -= c.hour * 3600;
			c.min   = k / 60;
			c.sec   = k % 60;

			return c;
		}

		/**********************/
		/* -のオーバーロード */
		/**********************/
		Time operator - (const Time &b)
		{
			Time c;
			int k;

			k       = hour * 3600 + min * 60 + sec - b.hour * 3600 - b.min * 60 - b.sec;
			c.hour  = k / 3600;
			k      -= c.hour * 3600;
			c.min   = k / 60;
			c.sec   = k % 60;

			return c;
		}

	friend istream &operator >> (istream &, Time &);   // >> のオーバーロード
	friend ostream &operator << (ostream &, Time);     // << のオーバーロード
};

/********************************/
/* 入力( >> のオーバーロード) */
/********************************/
istream &operator >> (istream &stream, Time &a)
{
	std::cout << "hour, min, sec? ";
	stream >> a.hour >> a.min >> a.sec;
	return stream;
}

/********************************/
/* 出力( << のオーバーロード) */
/********************************/
ostream &operator << (ostream &stream, Time a)
{
	stream << a.hour << ":" << a.min << ":" << a.sec << "\n";
	return stream;
}

/************/
/* main関数 */
/************/
int main()
{
	Time a(12, 10), b;

	std::cin >> b;
					// 加算
	std::cout << a << " + " << b;
	std::cout << "    = " << a+b;
					// 減算
	std::cout << a << " - " << b;
	std::cout << "    = " << a-b;

	return 0;
}
		
[問3]日付(紀元後とし,1990年6月23日 なら 1990/6/30 と表現する)の加算及び減算を行うクラスを定義せよ.ただし,
加算:1990/6/30 + 45 → 1990年6月23日 から 45 日後の年月日を計算
   1990/6/30 + (-23) → 1990年6月23日 から 23 日前の年月日を計算
減算:1990/6/30 - 1985/12/1 → 1985年12月1日 は 1990年6月23日 からみて何日前かを計算
   1990/6/30 - 1995/12/1 → 1995年12月1日 は 1990年6月23日 からみて何日後かを計算
また,閏年は,年号が 4 で割り切れ,かつ,100 で割り切れない年か,または,400 で割り切れる年である.
/****************************/
/* 年月日の加算と減算       */
/*      coded by Y.Suganuma */
/****************************/
#include <iostream>
/*
     クラスDateの定義
*/
class Date {
		int year;              // 年
		int month;             // 月
		int day;               // 日
		int tu;                // 西暦1年1月1日からの通算日
	public:

		/******************************/
		/* コンストラクタ(引数無し) */
		/******************************/
		Date() {}

		/********************************/
		/* コンストラクタ(年月日入力) */
		/********************************/
		Date(int n, int m, int l)
		{
			int i1;

			year  = n;
			month = m;
			day   = l;
			tu    = day;

			for (i1 = 1; i1 < year; i1++) {
				if ((i1%4 == 0 && i1%100 != 0) || i1%400 == 0)
					tu += 366;
				else
					tu += 365;
			}

			for (i1 = 1; i1 < month; i1++) {
				if (i1 == 2) {
					if ((year%4 == 0 && year%100 != 0) || year%400 == 0)
						tu += 29;
					else
						tu += 28;
				}
				else {
					if (i1 == 4 || i1 == 6 || i1 == 9 || i1 == 11)
						tu += 30;
					else
						tu += 31;
				}
			}
		}

		/********************************/
		/* コンストラクタ(通算日入力) */
		/********************************/
		Date(int n)
		{
			int k = 0, sw = 0;

			tu    = 0;
			year  = 1;
			month = 1;

			while (sw == 0) {
				if ((year%4 == 0 && year%100 != 0) || year%400 == 0)
					k += 366;
				else
					k += 365;
				if (k < n) {
					tu = k;
					year++;
				}
				else
					sw = 1;
			}

			while (sw > 0) {
				if (month == 2) {
					if ((year%4 == 0 && year%100 != 0) || year%400 == 0)
						k = 29;
					else
						k = 28;
				}
				else {
					if (month == 4 || month == 6 || month == 9 || month == 11)
						k = 30;
					else
						k = 31;
				}
				if (tu+k < n) {
					tu += k;
					month++;
				}
				else {
					sw   = 0;
					day  = n - tu;
					tu  += day;
				}
			}
		}

		/**********************/
		/* +のオーバーロード */
		/**********************/
		Date operator + (const int n)
		{
			Date c(tu+n);
			return c;
		}

		/**********************/
		/* -のオーバーロード */
		/**********************/
		int operator - (const Date &b)
		{
			return tu - b.tu;
		}

	friend ostream &operator << (ostream &, Date);     // << のオーバーロード
};

/********************************/
/* 出力( << のオーバーロード) */
/********************************/
ostream &operator << (ostream &stream, Date a)
{
	stream << a.year << "/" << a.month << "/" << a.day;
	stream << "  通算 " << a.tu << "\n";
	return stream;
}

/************/
/* main関数 */
/************/
int main()
{
	int year, month, day, n;
                    // 加算
	std::cout << "年 月 日? ";
	std::cin >> year >> month >> day;
	Date a(year, month, day);
	std::cout << "日? ";
	std::cin >> n;
	std::cout << n << "日後  " << a+n;
	n = -n;
	std::cout << -n << "日前  " << a+n;
                    // 減算
	std::cout << "年 月 日? ";
	std::cin >> year >> month >> day;
	Date b(year, month, day);
	n = a - b;
	if (n < 0)
		std::cout << "   " << -n << "日後\n";
	else
		std::cout << "   " << n << "日前\n";

	return 0;
}
		
[問4]行列の加減乗除を実行するためのクラスを定義せよ.ただし,ここで割り算 A / B とは,B の逆行列を左から掛けること,つまり,B-1A を意味するものとする.
/************************************/
/* 行列の加減乗除                   */
/* (A/BはBの逆行列を左から掛ける) */
/*      coded by Y.Suganuma         */
/************************************/
#include <iostream>
#include <stdlib.h>

/**************************/
/*     クラスMatrixの定義 */
/**************************/
class Matrix {       /* 2次元行列 */
		int n;              // 行の数
		int m;              // 列の数
		double **mat;       // 行列本体
	public:
		Matrix(int, int);   // コンストラクタ(引数あり)
		Matrix(const Matrix &);   // 初期化のためのコンストラクタ
		Matrix() {n = 0;}   // コンストラクタ(引数無し)
		~Matrix()   // デストラクタ
		{
			if (n > 0) {
				int i1;
				for (i1 = 0; i1 < n; i1++)
					delete [] mat[i1];
				delete [] mat;
			}
		}
		Matrix &operator= (const Matrix &);   // =のオーバーロード
		Matrix operator+ (const Matrix &);          // +のオーバーロード
		Matrix operator- (const Matrix &);          // -のオーバーロード
		Matrix operator* (const Matrix &);          // *のオーバーロード
		Matrix operator/ (const Matrix &);          // /のオーバーロード
	friend istream &operator >> (istream &, Matrix &);   // >> のオーバーロード
	friend ostream &operator << (ostream &, Matrix);     // << のオーバーロード
};

/*****************************/
/*コンストラクタ(引数あり) */
/*****************************/
Matrix::Matrix(int n1, int m1)
{
	int i1;
	n    = n1;
	m    = m1;
	mat  = new double * [n];
	for (i1 = 0; i1 < n; i1++)
		mat[i1] = new double [m];
}

/********************************/
/* 初期化のためのコンストラクタ */
/********************************/
Matrix::Matrix(const Matrix &A)
{
	int i1, i2;
	n    = A.n;
	m    = A.m;
	mat  = new double * [n];
	for (i1 = 0; i1 < n; i1++) {
		mat[i1] = new double [m];
		for (i2 = 0; i2 < m; i2++)
			mat[i1][i2] = A.mat[i1][i2];
	}
}

/********************************/
/* 入力( >> のオーバーロード) */
/********************************/
istream &operator >> (istream &stream, Matrix &A)
{
	int i1, i2;

	std::cout << "***" << A.n << "行" << A.m << "列の行列***\n";
	for (i1 = 0; i1 < A.n; i1++) {
		std::cout << i1+1 << "行目のデータ\n";
		for (i2 = 0; i2 < A.m; i2++) {
			std::cout << "   " << i2+1 << "列目の値は? ";
			stream >> A.mat[i1][i2];
		}
	}

	return stream;
}

/********************************/
/* 出力( << のオーバーロード) */
/********************************/
ostream &operator << (ostream &stream, Matrix A)
{
	int i1, i2;

	for (i1 = 0; i1 < A.n; i1++) {
		for (i2 = 0; i2 < A.m; i2++)
			stream << " " << A.mat[i1][i2];
		stream << "\n";
	}

	return stream;
}

/*******************************/
/* =のオーバーロード          */
/*      A = B (A.operator=(B)) */
/*******************************/
Matrix& Matrix::operator= (const Matrix &B)
{
	int i1, i2;

	if (&B != this) {   // 自分自身への代入を防ぐ
		if (n > 0) {   // 代入する前のメモリを解放
			for (i1 = 0; i1 < n; i1++)
				delete [] mat[i1];
			delete [] mat;
		}
		n        = B.n;
		m        = B.m;
		mat  = new double * [n];   // メモリの確保と代入
		for (i1 = 0; i1 < n; i1++) {
			mat[i1] = new double [m];
			for (i2 = 0; i2 < m; i2++)
				mat[i1][i2] = B.mat[i1][i2];
		}
	}

	return *this;
}

/**********************/
/* +のオーバーロード */
/**********************/
Matrix Matrix::operator + (const Matrix &B)
{
	int i1, i2;

	if (n != B.n || m != B.m) {
		std::cout << "***error  invalid data\n";
		exit(1);
	}

	Matrix C(n, m);

	for (i1 = 0; i1 < n; i1++) {
		for (i2 = 0; i2 < m; i2++)
			C.mat[i1][i2] = mat[i1][i2] + B.mat[i1][i2];
	}

	return C;
}

/**********************/
/* -のオーバーロード */
/**********************/
Matrix Matrix::operator - (const Matrix &B)
{
	int i1, i2;

	if (n != B.n || m != B.m) {
		std::cout << "***error  invalid data\n";
		exit(1);
	}

	Matrix C(n, m);

	for (i1 = 0; i1 < n; i1++) {
		for (i2 = 0; i2 < m; i2++)
			C.mat[i1][i2] = mat[i1][i2] - B.mat[i1][i2];
	}

	return C;
}

/**********************/
/* *のオーバーロード */
/**********************/
Matrix Matrix::operator* (const Matrix &B)
{
	int i1, i2, i3;

	if (m != B.n) {
		std::cout << "***error  invalid data\n";
		exit(1);
	}

	Matrix C(n, B.m);

	for (i1 = 0; i1 < n; i1++) {
		for (i2 = 0; i2 < B.m; i2++) {
			C.mat[i1][i2] = 0.0;
			for (i3 = 0; i3 < m; i3++)
				C.mat[i1][i2] += mat[i1][i3] * B.mat[i3][i2];
		}
	}

	return C;
}

/*******************************************************/
/* 線形連立方程式を解く(逆行列を求める)              */
/*      w : 方程式の左辺及び右辺                       */
/*      n : 方程式の数                                 */
/*      m : 方程式の右辺の列の数                       */
/*      eps : 正則性を判定する規準                     */
/*      return : =0 : 正常                             */
/*               =1 : 逆行列が存在しない               */
/*******************************************************/
#include <math.h>

int gauss(double **w, int n, int m, double eps)
{
	double y1, y2;
	int ind = 0, nm, m1, m2, i1, i2, i3;

	nm = n + m;

	for (i1 = 0; i1 < n && ind == 0; i1++) {

		y1 = .0;
		m1 = i1 + 1;
		m2 = 0;

		for (i2 = i1; i2 < n; i2++) {
			y2 = fabs(w[i2][i1]);
			if (y1 < y2) {
				y1 = y2;
				m2 = i2;
			}
		}

		if (y1 < eps)
			ind = 1;

		else {

			for (i2 = i1; i2 < nm; i2++) {
				y1        = w[i1][i2];
				w[i1][i2] = w[m2][i2];
				w[m2][i2] = y1;
			}

			y1 = 1.0 / w[i1][i1];

			for (i2 = m1; i2 < nm; i2++)
				w[i1][i2] *= y1;

			for (i2 = 0; i2 < n; i2++) {
				if (i2 != i1) {
					for (i3 = m1; i3 < nm; i3++)
						w[i2][i3] -= w[i2][i1] * w[i1][i3];
				}
			}
		}
	}

	return ind;
}

/**********************/
/* /のオーバーロード */
/**********************/
Matrix Matrix::operator/ (const Matrix &B)
{
	int i1, i2;

	if (n != B.n || B.n != B.m) {
		std::cout << "***error  invalid data\n";
		exit(1);
	}

	Matrix C(n, m);

	double **w = new double * [n];
	for (i1 = 0; i1 < n; i1++)
		w[i1] = new double [n+m];

	double eps = 1.0e-10;

	for (i1 = 0; i1 < n; i1++) {
		for (i2 = 0; i2 < n; i2++)
			w[i1][i2] = B.mat[i1][i2];
	}

	for (i1 = 0; i1 < n; i1++) {
		for (i2 = 0; i2 < m; i2++)
			w[i1][n+i2] = mat[i1][i2];
	}

	int ind = gauss(w, n, m, eps);

	if (ind != 0) {
		std::cout << "***error  no inverse\n";
		exit(1);
	}
	else {
		for (i1 = 0; i1 < n; i1++) {
			for (i2 = 0; i2 < m; i2++)
				C.mat[i1][i2] = w[i1][n+i2];
		}
	}

	for (i1 = 0; i1 < n; i1++)
		delete [] w[i1];
	delete [] w;

	return C;
}

/************/
/* main関数 */
/************/
int main()
{
	int n, m, l, sw;

	std::cout << "演算は?(=0:+, =1:-, =2:*, =3:/) ";
	std::cin >> sw;

	switch (sw) {

		case 0:       // +
		{
			std::cout << "--- A+B ---\n";
			std::cout << "n行m列? ";
			std::cin >> n >> m;
			Matrix A(n, m), B(n, m);
			std::cout << "Matrix A  ";
			std::cin >> A;
			std::cout << "Matrix B  ";
			std::cin >> B;
			std::cout << "Matrix (A+B)\n";
			std::cout << A+B;
			break;
		}

		case 1:       // -
		{
			std::cout << "--- A-B ---\n";
			std::cout << "n行m列? ";
			std::cin >> n >> m;
			Matrix A(n, m), B(n, m);
			std::cout << "Matrix A  ";
			std::cin >> A;
			std::cout << "Matrix B  ";
			std::cin >> B;
			std::cout << "Matrix (A-B)\n";
			std::cout << A-B;
			break;
		}

		case 2:       // *
		{
			std::cout << "--- A*B ---\n";
			std::cout << "n行m列(A)? ";
			std::cin >> n >> m;
			std::cout << "行列Bの列数は? ";
			std::cin >> l;
			Matrix A(n, m), B(m, l);
			std::cout << "Matrix A  ";
			std::cin >> A;
			std::cout << "Matrix B  ";
			std::cin >> B;
			std::cout << "Matrix (A*B)\n";
			std::cout << A*B;
			break;
		}

		case 3:       // /
		{
			std::cout << "--- A/B ---\n";
			std::cout << "n行m列(A)? ";
			std::cin >> n >> m;
			Matrix A(n, m), B(n, n);
			std::cout << "Matrix A  ";
			std::cin >> A;
			std::cout << "Matrix B  ";
			std::cin >> B;
			std::cout << "Matrix (A/B)\n";
			std::cout << A/B;
			break;
		}
	}

	return 0;
}
		

菅沼ホーム 演習解答例目次 本文目次 付録 索引