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

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

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

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

再帰

情報工学における再帰とは、プログラムのあるメソッドの処理上で自身のメソッドが再び呼び出されている処理の事をいいます。

Q&A

解決済

3回答

1322閲覧

【C言語 再帰呼び出し】 表示方法と硬貨の支払いについて

ccodereader

総合スコア7

C

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

再帰

情報工学における再帰とは、プログラムのあるメソッドの処理上で自身のメソッドが再び呼び出されている処理の事をいいます。

0グッド

1クリップ

投稿2022/05/22 01:17

C言語で再帰呼び出し使ってプログラムをつくりたいのですが、
難解でどうすればいいのか四苦八苦しています。
どなたかアドバイスを頂けないでしょうか?

問題

200円を1円、5円、10円、50円、100円の5種類の硬貨を使って、何通りの支払い方法があるかを求めるプログラムを再帰呼び出しを使って作る。関数change(int n, int k)を定義(n 円をk 円以下の硬貨で支払う支払い方の数を返す関数)し、main()関数では、支払う金額を1円から200円まで1円きざみで繰り返して「1円 = 1通り」、、「200円 = ???通り」のように、200行の出力を得られるようにする。

現状として1円から200円まで1円きざみで繰り返すことはできたのですが、
「1円 = 1通り」、、「200円 = ???通り」と表す時に、下記のようになってしまいます。
1円=1通りのように横並びに書くにはどうすればいいのでしょうか。

1円= 2円= 3円= 4円= . . . 196円= 197円= 198円= 199円= 200円= 0通り

また、何通りか表す時に0になってしまうのですが、
どう組み合わせを変更したら良いものか全く手が出ず困っています。

サンプルプログラムしかりアドバイスしかり、
ご助力願えないでしょうか。
どうかよろしくお願いします。

現状

c

1#include <stdio.h> 2#include <stdlib.h> 3 4// カウンタ変数 5int i, count; 6//支払い方法の数を数える関数(n:200円をk:100円以下の硬貨で) 7int change(int n, int k){ 8 9 int count; 10 for(int i = 1; i <= 200; i++){ 11 count = i; 12 printf("%d円=\n",count); 13 } 14 15 //change(n, 1) は,1円玉だけしか使えないので1通り 16 if (count == 1) { 17 return 1; 18 } else if(count == 5) { //change(n, 5) は,change(n, 1) + change(n −5, 5) 19 return change(n, 1) + change(n - 5, 5); 20 } else if(count == 10) { //change(n, 10) は,change(n, 5)+change(n−10, 10) 21 return change(n, 5)+change(n - 10, 10); 22 } else if(count == 50) { //change(n, 50) は,change(n, 10)+change(n−50, 50) 23 return change(n, 10)+change(n - 50, 50); 24 } else if(count == 100) { //change(n, 100) は,change(n, 50)+change(n−100, 100) 25 return change(n, 50)+change(n - 100, 100); 26 } else{ 27 return 0; 28 } 29 } 30 31 32int main(){ 33 int way; 34 way = change(200, 100); 35 printf("%d通り\n", way); 36 exit(0); 37}

試したこと

横に並べようと思い、以下のようにしてみたのですが、上手くいきませんでした。

c

1#include <stdio.h> 2#include <stdlib.h> 3 4//支払い方法の数を数える関数(n:200円をk:100円以下の硬貨で) 5int change(int n, int k){ 6 7 //change(n, 1) は,1円玉だけしか使えないので1通り 8 if (k == 1) { 9 return 1; 10 } else if(k == 5) { //change(n, 5) は,change(n, 1) + change(n −5, 5) 11 return change(n, 1) + change(n - 5, 5); 12 } else if(k == 10) { //change(n, 10) は,change(n, 5)+change(n−10, 10) 13 return change(n, 5)+change(n - 10, 10); 14 } else if(k == 50) { //change(n, 50) は,change(n, 10)+change(n−50, 50) 15 return change(n, 10)+change(n - 50, 50); 16 } else if(k == 100) { //change(n, 100) は,change(n, 50)+change(n−100, 100) 17 return change(n, 50)+change(n - 100, 100); 18 } else{ 19 return 0; 20 } 21 } 22 23 24int main(){ 25 int i, count, way; 26 way = change(200, 100); 27 for(int i = 1; i <= 200; i++){ 28 count = i; 29 printf("%s円=\n","%d通り\n", count, way); 30 } 31 exit(0); 32}

上記のエラー

c

1test6.c:29:37: warning: data argument not used by format string [-Wformat-extra-args] 2 printf("%s円=\n","%d通り\n", count, way); 3 ~~~~~~~~~ ^ 41 warning generated.

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

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

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

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

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

dodox86

2022/05/22 02:22

まず、問題への解答の前提として『「main()関数では、支払う金額を1円から200円まで1円きざみで繰り返して「1円 = 1通り」...』とあるのですから、main関数の形態は「試したこと」で示したようなものでないといけないと思います。「現状」のものは無条件で不可なはずです。 「上記のエラー」とありますが、コンパイルエラーなのではなく、警告(Warning)です。printf()関数の書式制御文字列に対して与えられた引数(count, way)が合っていないと報告しています。printf()関数の使い方を再度ちゃんと確認しましょう。
jimbe

2022/05/22 04:16 編集

複数の問題に同時に対処するのは、良いことではありません。 まずは別のプログラム (test6a.c とか ) として、 "x円, 0 通り" と x=1~200 分出すプログラムを作成して練習?したほうが良いかもしれません。
jimbe

2022/05/22 07:17

再帰が難しければ、再帰を用いない change 関数を作ってから再帰化するという手順もあると思います
guest

回答3

0

ベストアンサー

1円=1通りのように横並びに書くにはどうすればいいのでしょうか。

printf を次のように main に書けばよいでしょう。

C

1#include <stdio.h> 2 3int change(int n, int k) 4{ 5 return n/5 * (n/5) * 1013 / 1600 + 1; 6} 7 8int main(void) 9{ 10 for (int n = 1; n <= 200; n++) 11 printf("%d円 = %d通り\n", n, change(n, 100)); 12}

change は嘘のコードなので、自分で考えてください。

追記
嘘のコードは気持ちが悪いので、ちゃんと動くコードを示します。

C

1int change(int n, int k) 2{ 3 return n < 0 ? 0 : k == 1 ? 1 : 4 change(n-k, k) + change(n, k==5 ? 1 : k==10 ? 5 : k==50 ? 10 : 50); 5}

これは模範解答ではありません。
?: の三項演算子を使わないほうが分かりやすくなり、
質問のコードに近いものになるでしょう。

でも、printf("abc\n"); printf("def\n"); を
printf("abc\n", "def\n") としてもなぜ横に並ばないか
が分からないようではどうしようもないですね。

投稿2022/05/22 17:27

編集2022/05/24 02:00
kazuma-s

総合スコア8224

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

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

rubato6809

2022/05/23 02:23

私の回答の不足分を補っていただき、ありがとうございます。
rubato6809

2022/05/24 07:49

200円の組み合わせ数が同じになったので安心できました。
guest

0

まず横に表示することについて。
コンパイルでワーニングが出るのは、書式が間違っているからです。
printf("%s円=\n","%d通り\n", count, way);

printf("%s円=\n%d通り\n", count, way);
としたら、ワーニングは消えるでしょう。
ちなみに、"%s円 は "%d円 でしょう。
次に、
printf("%d円=%d通り\n", count, way);
とすれば、=の後改行されなくなります。
それでも次の表示がかぶってしまう場合は、
printf()のあとに
fflush(stdout);
を追加します。
これで、バッファに残っていた未表示の文字列が表示されます。
試してみてください。

投稿2022/05/24 07:07

編集2022/05/24 11:06
HidekoSaeki

総合スコア42

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

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

0

200円を払う組合せとは、100円*2枚から1円*200枚までカウントしてよいですね?私が作ってみたコードが正しければ200円を払う組合せは1000通りを超えました。

再帰は、部分が全体の相似(自己相似)になるような場合に有効です。
私はこんな風に考えてみました。例えば200円を100円以下のコインで払う場合、即ちchange(200, 100) は

  • 100円を一枚も使わず、200円を50円以下のコインで払う組合せの数、即ち change(200, 50)の値
  • 100円を一枚使い、残り100円を50円以下のコインで払う組合せの数、即ち change(100, 50)の値
  • 100円を二枚使い、残り0円を50円以下のコインで払う組合わせの数、即ち1

以上の合計です。つまり

  • change(200, 100) = change(200, 50) + change(100, 50) + change(0, 50)

です。change(0, 50) は要するに1(一通り)です。そこをプログラムにどう表すかは工夫次第で、再帰呼出しの終了条件と関係します。
さらに、例えば上記にある change(100, 50) は、50円が0~2枚に対してそれぞれ change(100, 10) と change(50, 10) と change(0, 10) を足した数・・・という具合いに
change(n, k) とは、k円コインを使う複数のパターンに対し、それぞれ残りの金額をより小さなコインの組合せで数えさせる(再帰する)ことで求められると考えられます。ここの再帰する箇所は、自分より小さな自己相似形を呼ぶ形になっているというわけです。具体的に列挙すると

  • 100円以下を使って払う組合せは、50円以下を使って払う組合わせから求められる
  • 50円以下を使って払う組合せは、10円以下を使って払う組合わせから求められる
  • 10円以下を使って払う組合せは、5円以下を使って払う組合わせから求められる
  • 5円以下を使って払う組合せは、1円を使って払う組合わせ(==1)

となりますので、再帰呼出しは4~5段になりそうです。ただし実際に書くのは自分が自分を呼出す一段階だけを一般化し、再帰の終了条件を加えて、コード化します。
文章だけで書くと正しく伝わりそうにないので、コードの核心を示します(完成形ではない)。

C

1// change(n, k) n円を k円コイン以下で払う組合せの数を返す 2int change(int n, int k) 3{ 4 // 終了条件をどうするか? 5 6 int knext = smallerCoin(k); // 次の小さなコイン(100の次は50、50の次は10...) 7 int max = n / k; // k 円コインが使える最大枚数 8 int ans = 0; // n 円を k 円以下のコインで払う組合せ数 9 10 // k 円コインは 0~max枚使える。 11 for (int i = 0; i <= max; i++) { 12 // それぞれ残りの金額を、より小さなコインの組合せで数えさせる(ここで再帰)。 13 ans += change(n - k * i, knext); // 残りを knext 円以下のコインで払う組合せ数 14 } 15 return ans; 16}

上のコードには再帰の終了条件がありません、change(?, 1) 即ち1円だけの組合わせは一通りしかないことなどが終了条件になるでしょう。

投稿2022/05/22 15:09

編集2022/05/23 02:22
rubato6809

総合スコア1380

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問