【factpercom.cのソースコード】
/*
* 階乗・順列・組み合わせを計算するプログラム
* factpercom.c
*
* 階乗: factorial、順列: permutation、組み合わせ: combination
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h> /* strlen(), strcmp(), strcpy(), strcat() */
#include <ctype.h> /* isdigit() */
#include <time.h>
#define BUF_SIZE 256
#define MAX_VALUE 9223372036854775807ULL /* unsigned long long int の最大値 */
/*
* 自作のbool型の定義
*/
typedef enum {
false, /* 0 */
true /* 1 */
} bool;
/*
* 各分岐処理のラベル付け
*/
enum {
do_end, /* 0: プログラムの終了 */
fact, /* 1: 階乗 */
perm, /* 2: 順列 */
comb, /* 3: 組み合わせ */
};
/*
* ユーザー定義関数の関数原型宣言
*/
void main_menu(void);
void main_proc(void);
void print_title(void);
void input(void);
void calc(void);
void comma_sep(char *sp, unsigned long long int n);
unsigned long long int factorial(int n);
unsigned long long int permutation(int n, int r);
unsigned long long int combination(int n, int r);
bool is_overflow(unsigned long long int value);
void output(void);
void output_result(char strans[]);
void foutput_result(char filename[], char oldfilename[], unsigned long long int ans);
void switch_output(char strans[], unsigned long long int value);
void fswitch_output(FILE *fp, unsigned long long int value);
void gen_filename(char filename[]);
bool retry(void);
/*
* グローバル変数
*/
char strans[BUF_SIZE],
filename[BUF_SIZE] = {1}, /* filenameとoldfilenameの初期値を */
oldfilename[BUF_SIZE] = {0}; /* 異なる物に設定 */
/* (ファイルへの追記の判断に使用) */
int proc_num, n, r;
unsigned long long int ans;
/*
* メイン関数
*/
int main(void)
{
/* 乱数系列の設定 */
srand((unsigned int)time(NULL));
while (1) {
/* メインメニュー(処理の選択) */
main_menu();
/* 実処理 */
main_proc();
/* 処理を続けるかどうかの選択 */
if (!retry())
break;
}
return EXIT_SUCCESS;
}
/*
* メインメニュー部の関数
*/
void main_menu(void)
{
char buf[BUF_SIZE], ss[BUF_SIZE], ch, zz;
printf("【階乗・順列・組み合わせを計算します】\n");
printf("次のいずれかを選択してください。\n\n");
while (1) {
printf("1.階乗を計算する。\n");
printf("2.順列を計算する。\n");
printf("3.組み合わせを計算する。\n");
printf("0.終了する。\n\n");
printf("0~3のどれですか? ");
fgets(buf, sizeof buf, stdin);
sscanf(buf, "%c%c", &ch, &zz);
if (((ch != '1') && (ch != '2') && (ch != '3') && (ch != '0'))
|| (zz != '\n')) {
printf("無効な入力です。\n");
printf("0、1、2、3のいずれかを入力しください。\n\n");
continue;
} else
break;
}
/* 文字chを数値proc_numに変換する */
ss[0] = ch; ss[1] = '\0';
proc_num = atoi(ss);
}
/*
* 実処理を行う関数
* print_title()関数、input()関数、calc()関数を使用する。
*/
void main_proc(void)
{
/* 表題の表示 */
print_title();
/* 入力 */
input();
/* 計算 */
calc();
/* 出力 */
output();
}
/*
* 各処理の表題を表示する関数
*/
void print_title(void)
{
switch (proc_num) {
/* 処理(1/3):階乗の場合 */
case fact:
printf("\n【階乗n!を計算します】\n\n");
printf("nを入力してください。\n");
printf("(ただし、0 ≦ nとする)\n\n");
break;
/* 処理(2/3):順列の場合 */
case perm:
printf("\n【順列nPrを計算します】\n\n");
printf("nおよびrを、順に入力してください。\n");
printf("(ただし、0 ≦ r ≦ nとする)\n\n");
break;
/* 処理(3/3):組み合わせの場合 */
case comb:
printf("\n【組み合わせnCrを計算します】\n\n");
printf("nおよびrを、順に入力してください。\n");
printf("(ただし、0 ≦ r ≦ nとする)\n\n");
break;
/* 処理(0/3):終了の場合 */
case do_end:
exit(EXIT_SUCCESS);
break;
default:
break;
}
}
/*
* 各処理中の入力部の関数
* is_valid_number()関数を使用する。
*/
void input(void)
{
char buf[BUF_SIZE], zz;
switch (proc_num) {
/* 処理(1/3):階乗の場合 */
case fact:
while (1) {
printf("n = ? ");
fgets(buf, sizeof buf, stdin);
sscanf(buf, "%d%c", &n, &zz);
buf[strlen(buf) - 1] = '\0';
if (!is_valid_number(buf)) {
printf("無効な入力です。\n");
printf("半角整数値を入力してください。\n\n");
continue;
} else if (n < 0) {
printf("無効な入力です。\n");
printf("0以上の整数を入力してください。\n\n");
} else
break;
}
break;
/* 処理(2/3):順列の場合 */
case perm:
while (1) {
/* nの入力 */
while (1) {
printf("n = ? ");
fgets(buf, sizeof buf, stdin);
sscanf(buf, "%d%c", &n, &zz);
buf[strlen(buf) - 1] = '\0';
if (!is_valid_number(buf)) {
printf("無効な入力です。\n");
printf("半角整数値を入力してください。\n\n");
continue;
} else if (n < 0) {
printf("無効な入力です。\n");
printf("0以上の整数を入力してください。\n\n");
} else
break;
}
/* rの入力 */
while (1) {
printf("r = ? ");
fgets(buf, sizeof buf, stdin);
sscanf(buf, "%d%c", &r, &zz);
buf[strlen(buf) - 1] = '\0';
if (!is_valid_number(buf)) {
printf("無効な入力です。\n");
printf("半角整数値を入力してください。\n\n");
continue;
} else if (r < 0) {
printf("無効な入力です。\n");
printf("0以上の整数を入力してください。\n\n");
} else
break;
}
/* 「0≦r≦n」を満たさない場合の処理 */
if ((!(0 <= r)) || (!(r <= n))) {
printf("「0 ≦ r ≦ n」ではありません。\n");
printf("「0 ≦ r ≦ n」となるように入力してください。\n");
continue;
} else
break;
}
break;
/* 処理(3/3):組み合わせの場合 */
case comb:
while (1) {
/* nの入力 */
while (1) {
printf("n = ? ");
fgets(buf, sizeof buf, stdin);
sscanf(buf, "%d%c", &n, &zz);
buf[strlen(buf) - 1] = '\0';
if (!is_valid_number(buf)) {
printf("無効な入力です。\n");
printf("半角整数値を入力してください。\n\n");
continue;
} else if (n < 0) {
printf("無効な入力です。\n");
printf("0以上の整数を入力してください。\n\n");
} else
break;
}
/* rの入力 */
while (1) {
printf("r = ? ");
fgets(buf, sizeof buf, stdin);
sscanf(buf, "%d%c", &r, &zz);
buf[strlen(buf) - 1] = '\0';
if (!is_valid_number(buf)) {
printf("無効な入力です。\n");
printf("半角整数値を入力してください。\n\n");
continue;
} else if (r < 0) {
printf("無効な入力です。\n");
printf("0以上の整数を入力してください。\n\n");
} else
break;
}
/* 「0≦r≦n」を満たさない場合の処理 */
if ((!(0 <= r)) || (!(r <= n))) {
printf("「0 ≦ r ≦ n」ではありません。\n");
printf("「0 ≦ r ≦ n」となるように入力してください。\n");
continue;
} else
break;
}
break;
default:
break;
}
}
/*
* 文字列が正しい形式の数値かどうかを判定する関数
* true: 正しい形式、false: 無効な形式
*/
bool is_valid_number(char buf[])
{
int i = 0;
/* 最初は符号(省略可) */
if (buf[i] == '+' || buf[i] == '-')
i++;
/* 次に数字(省略不可) */
if (!isdigit(buf[i]))
return false;
i++;
/* 次に数字列(省略可) */
while (isdigit(buf[i]))
i++;
return buf[i] == '\0';
}
/*
* 各処理中の計算部の関数
* factorial()関数、comma_sep()関数、permutaion()関数、
* およびcombination()関数を使用する。
*/
void calc(void)
{
switch (proc_num) {
/* 処理(1/3):階乗の場合 */
case fact:
/* 階乗の計算 */
ans = factorial(n);
/* 計算結果を3桁ずつコンマで区切られた文字列に変換 */
comma_sep(strans, ans);
break;
/* 処理(2/3):順列の場合 */
case perm:
/* 順列の計算 */
ans = permutation(n, r);
/* 計算結果を3桁ずつコンマで区切られた文字列に変換 */
comma_sep(strans, ans);
break;
/* 処理(3/3):組み合わせの場合 */
case comb:
/* 組み合わせの計算 */
ans = combination(n, r);
/* 計算結果を3桁ずつコンマで区切られた文字列に変換 */
comma_sep(strans, ans);
break;
default:
break;
}
}
/*
* 数値を3桁ずつコンマで区切られた文字列に変換する関数
*/
void comma_sep(char *sp, unsigned long long int n)
{
char *p = sp, /* 作業用のポインタpを宣言し、spに初期化 */
c, /* 文字入れ換え時の作業用変数 */
ss_n[BUF_SIZE]; /* nの10進数表記の文字列の格納用変数 */
int digit = 0, /* 10進桁数のカウント用の変数 */
len; /* nの10進数表記での桁数の格納用変数 */
/* nの10進数表記での桁数をlenに取得 */
sprintf(ss_n, "%llu", n);
len = strlen(ss_n);
/* nが0の場合は文字0をp(=sp)に格納 */
if (n == 0)
sprintf(p, "0");
/* nが0より大の場合、1の位からコンマで区切ってp(=sp)に格納 */
else {
while (n > 0) {
sprintf(p++, "%llu", n % 10);
n /= 10;
digit++;
if (digit % 3 == 0 && digit != len)
sprintf(p++, ",");
}
*p-- = '\0';
}
/* 文字列を反転 */
while (sp < p) {
c = *sp;
*sp++ = *p;
*p-- = c;
}
}
/*
* 階乗を計算する関数
* is_overflow()関数を使用する。
*/
unsigned long long int factorial(int n)
{
int i;
unsigned long long int ans = 1;
if (n == 0)
;
else
for (i = 1; i <= n; i++) {
ans *= i;
if (is_overflow(ans))
return MAX_VALUE + 1;
}
return ans;
}
/*
* 順列を計算する関数
* is_overflow()関数を使用する。
*/
unsigned long long int permutation(int n, int r)
{
int i;
unsigned long long int ans = 1;
if ((n == 0) || (r == 0))
;
else
for (i = 0; i < r; i++) {
ans *= (n - i);
if (is_overflow(ans))
return MAX_VALUE + 1;
}
return ans;
}
/*
* 組み合わせを計算する関数
* is_overflow()関数を使用する。
*/
unsigned long long int combination(int n, int r)
{
int i;
unsigned long long int ans = 1;
if (r > n - r)
r = n - r;
if ((n == 0) || (r == 0))
;
else
for (i = 1; i <= r; i++) {
ans *= (n - r + i);
if (is_overflow(ans))
return MAX_VALUE + 1;
ans /= i;
}
return ans;
}
/*
* オーバーフローの判定をする関数
* true: オーバーフロー、false: オーバーフローしていない。
*/
bool is_overflow(unsigned long long int value)
{
return value > MAX_VALUE;
}
/*
* 各処理中の出力部の関数
* output_result()関数とfoutput_result()関数を使用する。
*/
void output(void)
{
char buf[BUF_SIZE], ch, zz;
output_result(strans);
while (1) {
printf("ファイルに出力しますか?(y/n) ");
fgets(buf, sizeof buf, stdin);
sscanf(buf, "%c%c", &ch, &zz);
if (((ch == 'y') || (ch == 'Y')) && (zz == '\n')) {
foutput_result(filename, oldfilename, ans);
break;
} else if (((ch == 'n') || (ch == 'N')) && (zz == '\n'))
break;
else {
printf("無効な入力です。\n");
printf("yまたはnを入力してください。\n\n");
continue;
}
}
}
/*
* 各計算結果を画面に出力する関数
* switch_output()関数を使用する。
*/
void output_result(char strans[])
{
switch (proc_num) {
/* 処理(1/3):階乗の場合 */
case fact:
printf("%d! = ", n);
switch_output(strans, ans);
printf("\n");
break;
/* 処理(2/3):順列の場合 */
case perm:
printf("%dP%d = ", n, r);
switch_output(strans, ans);
printf("\n");
break;
/* 処理(3/3):組み合わせの場合 */
case comb:
printf("%dC%d = ", n, r);
switch_output(strans, ans);
printf("\n");
break;
default:
break;
}
}
/*
* 各計算結果をファイルに出力する関数
* gen_filename()関数、fswitch_output()を使用する。
*/
void foutput_result(char filename[], char oldfilename[], unsigned long long int ans)
{
FILE *fp;
/* ifによる分岐は、2行目以降を同一ファイルに追記出力させるため */
if (strcmp(filename, oldfilename) == 0) { /* ファイル名が同じなら(2行目以降なら) */
fp = fopen(oldfilename, "a"); /* oldfilenameを開く */
if (fp == NULL) {
printf("ファイルを開けません。ファイル出力を中断します。\n");
return;
}
} else { /* ファイル名が違うなら(初めてのファイルなら) */
gen_filename(filename); /* 新規にファイル名を生成させて */
fp = fopen(filename, "a"); /* そのファイル(filename)を開いて */
strcpy(oldfilename, filename); /* oldfilenameを更新する */
if (fp == NULL) {
printf("ファイルを開けません。ファイル出力を中断します。\n");
return;
}
}
switch (proc_num) {
/* 処理(1/3):階乗の場合 */
case fact:
fprintf(fp, "%d!,", n);
fswitch_output(fp, ans);
fprintf(fp, "\n");
break;
/* 処理(2/3):順列の場合 */
case perm:
fprintf(fp, "%dP%d,", n, r);
fswitch_output(fp, ans);
fprintf(fp, "\n");
break;
/* 処理(3/3):組み合わせの場合 */
case comb:
fprintf(fp, "%dC%d,", n, r);
fswitch_output(fp, ans);
fprintf(fp, "\n");
break;
default:
break;
}
fclose(fp);
}
/*
* 場合に応じて形式を切り替えて画面出力する関数
*/
void switch_output(char strans[], unsigned long long int ans)
{
/* オーバーフローの場合 */
if (ans == MAX_VALUE + 1)
printf("#NUM!");
/* 計算結果が10億以上の場合は指数形式で出力する */
else if (ans >= 1000000000)
printf("%LE", (long double)ans);
/* それ以外の場合は3桁ずつコンマで区切られた整数形式で出力する */
else
printf("%s", strans);
}
/*
* 場合に応じて形式を切り替えてファイル出力する関数
*/
void fswitch_output(FILE *fp, unsigned long long int ans)
{
/* オーバーフローの場合 */
if (ans == MAX_VALUE + 1)
fprintf(fp, "#NUM!");
/* 計算結果が10億以上の場合は指数形式で出力する */
else if (ans >= 1000000000)
fprintf(fp, "%LE", (long double)ans);
/* それ以外の場合は整数形式で出力する */
else
fprintf(fp, "%llu", ans);
}
/*
* 現在日付時刻からファイル名を生成させる関数
*/
void gen_filename(char filename[])
{
struct tm *t;
time_t tt;
char strnumrand[10];
time(&tt);
t = localtime(&tt);
sprintf(filename, "factpercom%04d%02d%02d_%02d%02d%02d_",
1900+(t->tm_year), 1+(t->tm_mon), t->tm_mday,
t->tm_hour, t->tm_min, t->tm_sec);
sprintf(strnumrand, "%04d", rand() % 10000);
filename = strcat(filename, strnumrand);
filename = strcat(filename, ".csv");
}
/*
* 操作を続けるかどうかを選択する関数
* true: 続ける、false: 終了する
*/
bool retry(void)
{
bool ret;
char buf[BUF_SIZE], ch, zz;
while (1) {
printf("\n続けますか?(y/n) ");
fgets(buf, sizeof buf, stdin);
sscanf(buf, "%c%c", &ch, &zz);
if (((ch == 'y') || (ch == 'Y')) && (zz == '\n')) {
printf("\n+++++\n\n");
ret = true;
break;
} else if (((ch == 'n') || (ch == 'N')) && (zz == '\n')) {
ret = false;
break;
}
printf("無効な入力です。\n");
printf("yまたはnを入力してください。\n\n");
}
return ret;
}
C:\Users\skonishi\Documents>factpercom
【階乗・順列・組み合わせを計算します】
次のいずれかを選択してください。
1.階乗を計算する。
2.順列を計算する。
3.組み合わせを計算する。
0.終了する。
0~3のどれですか? 1
【階乗n!を計算します】
nを入力してください。
(ただし、0 ≦ nとする)
n = ? 10
10! = 3,628,800 ←画面出力では数値が3桁ずつコンマで区切って表示されている。
ファイルに出力しますか?(y/n) y
続けますか?(y/n) y
+++++
【階乗・順列・組み合わせを計算します】
次のいずれかを選択してください。
1.階乗を計算する。
2.順列を計算する。
3.組み合わせを計算する。
0.終了する。
0~3のどれですか? 2
【順列nPrを計算します】
nおよびrを、順に入力してください。
(ただし、0 ≦ r ≦ nとする)
n = ? 10
r = ? 10
10P10 = 3,628,800 ←画面出力では数値が3桁ずつコンマで区切って表示されている。
ファイルに出力しますか?(y/n) y
続けますか?(y/n) y
+++++
【階乗・順列・組み合わせを計算します】
次のいずれかを選択してください。
1.階乗を計算する。
2.順列を計算する。
3.組み合わせを計算する。
0.終了する。
0~3のどれですか? 3
【組み合わせnCrを計算します】
nおよびrを、順に入力してください。
(ただし、0 ≦ r ≦ nとする)
n = ? 20
r = ? 10
20C10 = 184,756 ←画面出力では数値が3桁ずつコンマで区切って表示されている。
ファイルに出力しますか?(y/n) y
続けますか?(y/n) y
+++++
【階乗・順列・組み合わせを計算します】
次のいずれかを選択してください。
1.階乗を計算する。
2.順列を計算する。
3.組み合わせを計算する。
0.終了する。
0~3のどれですか? 1
【階乗n!を計算します】
nを入力してください。
(ただし、0 ≦ nとする)
n = ? 15
15! = 1.307674E+12 ←10億(1E+9)を超えると指数形式で表示されている。
ファイルに出力しますか?(y/n) y
続けますか?(y/n) y
+++++
【階乗・順列・組み合わせを計算します】
次のいずれかを選択してください。
1.階乗を計算する。
2.順列を計算する。
3.組み合わせを計算する。
0.終了する。
0~3のどれですか? 2
【順列nPrを計算します】
nおよびrを、順に入力してください。
(ただし、0 ≦ r ≦ nとする)
n = ? 15
r = ? 15
15P15 = 1.307674E+12 ←10億(1E+9)を超えると指数形式で表示されている。
ファイルに出力しますか?(y/n) y
続けますか?(y/n) y
+++++
【階乗・順列・組み合わせを計算します】
次のいずれかを選択してください。
1.階乗を計算する。
2.順列を計算する。
3.組み合わせを計算する。
0.終了する。
0~3のどれですか? 3
【組み合わせnCrを計算します】
nおよびrを、順に入力してください。
(ただし、0 ≦ r ≦ nとする)
n = ? 40
r = ? 20
40C20 = 1.378465E+11 ←10億(1E+9)を超えると指数形式で表示されている。
ファイルに出力しますか?(y/n) y
続けますか?(y/n) y
+++++
【階乗・順列・組み合わせを計算します】
次のいずれかを選択してください。
1.階乗を計算する。
2.順列を計算する。
3.組み合わせを計算する。
0.終了する。
0~3のどれですか? 1
【階乗n!を計算します】
nを入力してください。
(ただし、0 ≦ nとする)
n = ? 30
30! = #NUM! ←オーバーフローすると「#NUM!」と表示されている。
ファイルに出力しますか?(y/n) y
続けますか?(y/n) y
+++++
【階乗・順列・組み合わせを計算します】
次のいずれかを選択してください。
1.階乗を計算する。
2.順列を計算する。
3.組み合わせを計算する。
0.終了する。
0~3のどれですか? 2
【順列nPrを計算します】
nおよびrを、順に入力してください。
(ただし、0 ≦ r ≦ nとする)
n = ? 30
r = ? 30
30P30 = #NUM! ←オーバーフローすると「#NUM!」と表示されている。
ファイルに出力しますか?(y/n) y
続けますか?(y/n) y
+++++
【階乗・順列・組み合わせを計算します】
次のいずれかを選択してください。
1.階乗を計算する。
2.順列を計算する。
3.組み合わせを計算する。
0.終了する。
0~3のどれですか? 3
【組み合わせnCrを計算します】
nおよびrを、順に入力してください。
(ただし、0 ≦ r ≦ nとする)
n = ? 70
r = ? 35
70C35 = #NUM! ←オーバーフローすると「#NUM!」と表示されている。
ファイルに出力しますか?(y/n) y
続けますか?(y/n) n
10!,3628800 ←ファイル出力では数値データがそのまま(コンマで区切られずに)出力されている。
10P10,3628800 ←ファイル出力では数値データがそのまま(コンマで区切られずに)出力されている。
20C10,184756 ←ファイル出力では数値データがそのまま(コンマで区切られずに)出力されている。
15!,1.307674E+12
15P15,1.307674E+12
40C20,1.378465E+11
30!,#NUM!
30P30,#NUM!
70C35,#NUM!