質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.32%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Q&A

解決済

3回答

462閲覧

C言語での大きな数の出力:エクセルに似たようなことをしたい

sanshirou

総合スコア9

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

0グッド

1クリップ

投稿2025/03/06 03:50

以下のような、順列nPrと組み合わせnCrを、n、rを0~100の範囲で変化させて計算して結果をCSVファイルに出力するプログラムを、C言語で作成してみました。

該当のソースコード

C言語

1#include <stdio.h> 2#include <stdlib.h> 3 4void procedure(int i); 5unsigned long long int permutation(int n, int r); 6unsigned long long int combination(int n, int r); 7 8int main(void) 9{ 10 int i; 11 12 for (i = 0; i < 2; i++) 13 procedure(i); 14 15 return EXIT_SUCCESS; 16} 17 18void procedure(int i) 19{ 20 int j, k; 21 FILE *fp1, *fp2; 22 unsigned long long int ans; 23 24 switch (i) { 25 case 0: 26 fp1 = fopen("output1.csv", "a"); 27 if (fp1 == NULL) { 28 printf("ファイルを開けません。処理を中断します。\n"); 29 return; 30 } 31 for (j = 0; j <= 100; j++) 32 for (k = 0; k <= j; k++) { 33 ans = permutation(j, k); 34 fprintf(fp1, "%dP%d,%llu\n", j, k, ans); 35 } 36 fclose(fp1); 37 break; 38 case 1: 39 fp2 = fopen("output2.csv", "a"); 40 if (fp2 == NULL) { 41 printf("ファイルを開けません。処理を中断します。\n"); 42 return; 43 } 44 for (j = 0; j <= 100; j++) 45 for (k = 0; k <= j; k++) { 46 ans = combination(j, k); 47 fprintf(fp2, "%dC%d,%llu\n", j, k, ans); 48 } 49 fclose(fp2); 50 break; 51 default: 52 break; 53 } 54} 55 56/* 順列 */ 57unsigned long long int permutation(int n, int r) 58{ 59 int i; 60 unsigned long long int ans = 1; 61 62 if ((n == 0) || (r == 0)) 63 ; 64 else 65 for (i = 0; i < r; i++) 66 ans *= (n - i); 67 68 return ans; 69} 70 71/* 組み合わせ */ 72unsigned long long int combination(int n, int r) 73{ 74 int i; 75 unsigned long long int ans = 1; 76 77 if (r > n - r) 78 r = n - r; 79 80 if ((n == 0) || (r == 0)) 81 ; 82 else 83 for (i = 1; i <= r; i++) { 84 ans *= (n - r + i); 85 ans /= i; 86 } 87 88 return ans; 89}

結果、当然と言えば当然ですが、大きな数値に対しては、出力がおかしくなりました。値のオーバーフローによるものだと思います。

エクセルのPERMUT関数とCOMBIN関数の結果と照合している途中に気付き、少し調べてみたら、エクセルでは

「値がある程度大きくなったら、『●●E▲▲』(●●掛ける10の▲▲乗)の形で出力し、エクセルの処理できる正の最大値9.99999999999999E+307を超えたら、#NUM!と出力する」

ことが分かりました。

そこで、エクセルとは値の範囲が異なりますが、エクセルに似たことができるように改変したいのですが、どのように変えればよいか、見当が付きません。

実際に、「60種のうちから5種を選ぶ組み合わせ(60C5)」を計算する必要があり(計算結果は5,461,512通り)、実用に耐えるものを作りたい、というのが、そもそもの作成動機です。

お助けください。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

maisumakun

2025/03/06 04:08

> 実際に、「60種のうちから5種を選ぶ組み合わせ(60C5)」を計算する必要があり(計算結果は5,461,512通り)、実用に耐えるものを作りたい、というのが、そもそもの作成動機です。 それぐらいの値なら、このコードで正常動作します。 https://paiza.io/projects/JMfHBy67z8r0IWyahxnvGw
sanshirou

2025/03/06 04:25

コメントありがとうございます。nPrまたはnCrのn、rを、固定ではなく可変にしたいのです。キーボードからn、rを入力を受け付けるが、さまざまなn、rの値に対して、エクセルに似たような出力ができるようにしたい、というのが希望です。
melian

2025/03/06 04:28

GNU MP の様な多倍長整数演算ライブラリを利用してみてはどうでしょうか。 The GNU MP Bignum Library https://gmplib.org/
utm.

2025/03/06 05:15

そもそもとして100の階乗ですら桁数がとんでもなくなるので、大きい数値を扱えるようにするしかないのでは無いでしょうか
guest

回答3

0

質問のコメントで言及した The GNU MP Bignum Library を利用する場合です。

c

1#include <stdio.h> 2#include <stdlib.h> 3#include <float.h> 4#include <gmp.h> 5 6void procedure(int i); 7void permutation(uint j, uint k, mpz_t *r); 8void combination(uint j, uint k, mpz_t *r); 9void save_result_to_file(void (*func)(uint, uint, mpz_t*), char *file_name); 10 11#define LIMIT 1e9 12 13int main(void) 14{ 15 save_result_to_file(&permutation, "output1.csv"); 16 save_result_to_file(&combination, "output2.csv"); 17} 18 19void save_result_to_file(void (*func)(uint, uint, mpz_t*), char *file_name) { 20 FILE *fp = fopen(file_name, "w"); 21 if (fp == NULL) { 22 fprintf(stderr, "ファイルを開けません。処理を中断します。\n"); 23 return; 24 } 25 26 uint j, k; 27 mpz_t result, overflow; 28 mpf_t tmp_float; 29 char result_type = (func == &permutation) ? 'P' : 'C'; 30 char *result_str; 31 32 mpz_init(result); 33 mpf_init(tmp_float); 34 mpz_set_d(overflow, DBL_MAX); 35 for (j = 0; j <= 100; j++) 36 for (k = 0; k <= j; k++) { 37 (*func)(j, k, &result); 38 if (mpz_cmp(result, overflow) > 0) { 39 result_str = NULL; 40 } else if (mpz_cmp_ui(result, LIMIT) > 0) { 41 mpf_set_z(tmp_float, result); 42 gmp_asprintf(&result_str, "%.4FE", tmp_float); 43 } else { 44 result_str = mpz_get_str(NULL, 10, result); 45 } 46 fprintf(fp, "%d%c%d,%s\n", j, result_type, k, 47 result_str ? result_str : "#NUM!"); 48 free(result_str); 49 } 50 fclose(fp); 51 mpz_clear(result); 52 mpz_clear(overflow); 53 mpf_clear(tmp_float); 54} 55 56void permutation(uint m, uint n, mpz_t *r) { 57 if (m == 0 || n == 0) { 58 mpz_set_ui(*r, 1); 59 return; 60 } 61 mpz_init(*r); 62 for(mpz_init_set_ui(*r,m);n>1;m--,n--,mpz_mul_ui(*r,*r,m)); 63 return; 64} 65 66void combination(uint m, uint n, mpz_t *r) { 67 if (m == 0 || n == 0) { 68 mpz_set_ui(*r, 1); 69 return; 70 } 71 mpz_init(*r); 72 mpz_t r1, r2; 73 mpz_init(r1); mpz_init(r2); 74 permutation(m, n, &r1); permutation(n, n, &r2); 75 mpz_cdiv_q(*r, r1, r2); 76 mpz_clear(r1); mpz_clear(r2); 77 return; 78}

投稿2025/03/06 09:04

編集2025/03/06 09:29
melian

総合スコア21038

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

自己解決

・結果が10億以上の場合は、double型にキャストして、指数形式で出力する。
・結果がオーバーフローしたかどうかの関数を作って判定し、その場合は「#NUM!」と出力する。
という風にしました。ご回答ありがとうございました。

投稿2025/03/06 07:36

sanshirou

総合スコア9

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

PingHermit

2025/03/09 13:11

エクセルは、double を使っているようですので、 下位桁の誤差も同様に出ますから、 必要であればそれの確認も必要かと思います。 誤差程度であれば問題ないならdoubleでも大丈夫ですが。
guest

0

必ずしも1の位まで完全に正しい結果が出なくていいのであれば、浮動小数点数(double)を使って計算する、という手段は考えられます。

投稿2025/03/06 04:29

編集2025/03/06 04:30
maisumakun

総合スコア146467

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

sanshirou

2025/03/06 07:32

ありがとうございます。検討してみます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.32%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問