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

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

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

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Q&A

解決済

2回答

15360閲覧

C#での分数の計算方法と、帯分数表示、約分、整数表示について。

otintin

総合スコア6

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

0グッド

0クリップ

投稿2017/06/08 14:27

C#勉強中の初心者です。
こちらのサイトの問題 prob8-6について質問させていただきます。問題は以下の通りになります。

2つの分数同士の足し算をし、その結果を分数で表示するプログラムを作りなさい。このとき、分子、分母共に最大値が10で、最小値は分母が2、分子が1とする。それらの数値をランダムに発生させ、以下のように結果を表示させなさい。ただし、計算結果は、分子と分母がきちんと約分されていることとする。また、分子が分母の数で割り切れる場合は、整数として表示するものとする。


1/5 + 2/3 = 13/15 ← 通常のケース
2/3 + 3/8= 1.1/24 ← 帯分数になるケース
1/6 + 1/3= 1/2 ← 約分されるケース
1/2 + 1/2 = 1 ← 整数になるケース

ずばり、この問題のやり方を教えてください。
自分なりに出来る所までは考えました。(後述)
しかし、自分の方法だと何かが違うようです。
やってるうちに自分でも何を書いてるかわからなくなってきました…。
どうぞ、よろしくお願いいたします。

C#

1 class Program 2 { 3 static void Main(string[] args) 4 { 5 Random rnd = new Random(); 6 int[] bo = new int[2]; 7 int[] si = new int[2]; 8 9 for (int i = 0; i < 2; i++) { 10 int bo_a = rnd.Next(2, 10); 11 int si_b = rnd.Next(1, 11); 12 bo[i] = bo_a; 13 si[i] = si_b; 14 } 15 16 int bo_sum = bo[0] * bo[1]; 17 int si_sum = si[0] * (bo_sum / bo[0]) + si[1] * (bo_sum / bo[1]); 18 int yaku_si, yaku_bo; 19 20 Console.Write("{0}/{1} + {2}/{3} = ",si[0],bo[0],si[1],bo[1]); 21 22 if (si_sum == bo_sum) 23 { 24 Console.WriteLine("1"); 25 } 26 else if (si_sum % bo_sum == 0) 27 { 28 Console.WriteLine("{0}", si_sum / bo_sum); 29 } 30 else if (si_sum > bo_sum) 31 { 32 Console.WriteLine("1.{0}/{1}", si_sum - bo_sum, bo_sum); 33 } 34 else 35 { 36 if (bo[0] > bo[1]) 37 { 38 yaku_si = si_sum / bo[1]; 39 yaku_bo = bo_sum / bo[1]; 40 Console.WriteLine("{0}/{1}", yaku_si, yaku_bo); 41 } 42 else if (bo[1] > bo[0]) 43 { 44 yaku_si = si_sum / bo[0]; 45 yaku_bo = bo_sum / bo[0]; 46 Console.WriteLine("{0}/{1}", yaku_si, yaku_bo); 47 } 48 else 49 { 50 Console.WriteLine("{0}/{1}", si_sum, bo_sum); 51 } 52 } 53 54 } 55 }

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

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

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

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

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

guest

回答2

0

機能を小さな単位に分割してそれを別のメソッドやクラスに分ける練習によい問題な気がします。一つの関数内にだらだらーっと書いていると見通しも悪くなりますし。

せっかくC#のような言語を使っているなら分数をクラスにしてそこに加算、簡約、文字列化など機能ごとに実装すればクラス定義のよい練習にもなると思います。

C#

1public static class MathUtil { 2 // ユークリッドの互除法で最大公約数を求める関数を用意しておく 3 public static int Gcd(int a, int b) { ... } 4} 5 6// 変更不可なクラスにしてみる。 7// 変更不可なのでgetter/setterは不要。 8public class Ratio { 9 public const int numerator; 10 public const int denominator; 11 12 public Ratio(int numerator, int denominator) { 13 // コンストラクターで簡約するのも一つの手 14 int gcd = MathUtil.Gcd(numerator, denominator); 15 this.numerator = numerator / gcd; 16 this.denominator = denominator / gcd; 17 } 18 19 // こんなケースでもない限りなかなか演算子のオーバーロードを使う機会がないので使ってみる 20 public static Ratio operator+ (Ratio a, Ratio b) { 21 int nu = a.numerator * b.denominator 22 + b.numerator * a.denominator; 23 return new Ratio(nu, a.denominator * b.denominator); 24 } 25 26 ... 27 28 public override string ToString() { 29 ... 30 } 31} 32...

投稿2017/06/08 15:35

編集2017/06/09 00:34
KSwordOfHaste

総合スコア18394

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

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

otintin

2017/06/09 11:36

ご回答ありがとうございました。 なるほど…機能別に作っておくと確かに分かりやすいです。 オブジェクト指向、勉強したばかりで使えこなせないと思ってましたが、 使わないでやろうとすればかえってしんどくなるんですね…。 使わなかったから、オブジェクト指向が理解できなかったのかもしれません。 毛嫌いせず、使えそうなときには使っていくよう心がけます。 constで値を変更できなくなるのは知ってましたが、 このようにコンストラクターで入れられるんですね。勉強になりました。 オーバーロードのところは考えても分かりませんでした。。 そもそもオーバーロードというのが、同じ名のメソッドを書けるという認識しかなく、 それはオブジェクト指向の理解があやふやだから、オーバーロードの使うタイミングも 理解できてないのだと思います。そこら辺ちゃんと勉強してきます。
guest

0

ベストアンサー

概ねこんな感じかと

csharp

1 2using System; 3 4namespace ConsoleApp1 5{ 6 //分数を表現する構造体 7 struct Fraction 8 { 9 public Fraction(int numerator, int denominator) 10 :this() 11 { 12 Numerator = numerator; 13 Denominator = denominator; 14 } 15 public int Numerator { get; } 16 public int Denominator { get; } 17 } 18 19 class Program 20 { 21 //約分するときに使う素数(尚、数えてもあんまり意味は無い) 22 private static int[] PrimeNumbers = {5, 3, 2}; 23 24 static void Main() 25 { 26 //乱数 27 Random rnd = new Random(42); 28 29 //とりあえず10回 30 for (int i = 0; i < 10; i++) 31 { 32 var x = new Fraction(rnd.Next(2, 11), rnd.Next(2, 11)); 33 var y = new Fraction(rnd.Next(2, 11), rnd.Next(2, 11)); 34 35 var result = Add(x, y); 36 Console.WriteLine($"{x.Numerator}/{x.Denominator}+{y.Numerator}/{y.Denominator}={Display(result)}"); 37 } 38 39 40 } 41 42 43 //加算処理 44 static Fraction Add(Fraction x, Fraction y) 45 { 46 var denominator = x.Denominator * y.Denominator; 47 var numerator = x.Numerator * y.Denominator + x.Denominator * y.Numerator; 48 49 return Reduction(new Fraction(numerator, denominator)); 50 } 51 52 //約分 53 static Fraction Reduction(Fraction fraction) 54 { 55 56 int numerator = fraction.Numerator; 57 int denominator = fraction.Denominator; 58 59 60 foreach (var p in PrimeNumbers) 61 { 62 if (numerator % p == 0 && denominator % p == 0) 63 { 64 numerator /= p; 65 denominator /= p; 66 } 67 } 68 69 return new Fraction(numerator, denominator); 70 } 71 72 //通分して文字列に 73 static string Display(Fraction fraction) 74 { 75 var f = Reduction(fraction); 76 int tmp = f.Numerator / f.Denominator; 77 78 //整数だった場合 79 if (fraction.Numerator % fraction.Denominator == 0) 80 { 81 return tmp.ToString(); 82 } 83 84 //帯分数だった場合 85 if (tmp >= 1) 86 { 87 return $"{tmp}.{fraction.Numerator % fraction.Denominator}/{fraction.Denominator}"; 88 } 89 90 return $"{f.Numerator}/{f.Denominator}"; 91 } 92 93 } 94} 95 96 97

こっから先はお戯れなので、まぁこーいう風にも書けるのかー程度に

csharp

1 2 3using System; 4using System.Linq; 5 6namespace ConsoleApp1 7{ 8 //値型にしなよ~と言う意見は重々承知してるけど、0/0を避けたかったの。 9 //けどNullに成る病にかかった模様 10 class Fraction 11 { 12 13 private static readonly int[] PrimeNumbers = { 5, 3, 2 }; 14 15 //尚、範囲チェックはしてない模様(故、0以下の数が来ると宇宙の法則が乱れる) 16 public Fraction(int numerator, int denominator) 17 { 18 Numerator = numerator; 19 Denominator = denominator; 20 } 21 22 //イマドキなら逆にreadonly付けてpublicフィールドでも良いかもね? 23 public int Numerator { get; } 24 public int Denominator { get; } 25 26 //演算子のオーバーロードして、ホントに分数同士を足せるようにした 27 //Nullは来ない。良いね? 28 public static Fraction operator +(Fraction x, Fraction y) 29 { 30 var denominator = x.Denominator * y.Denominator; 31 var numerator = x.Numerator * y.Denominator + x.Denominator * y.Numerator; 32 33 return new Fraction(numerator, denominator); 34 } 35 36 //約分した分数を返す 37 //まぁImmutableにしたかった 38 //ついでに言うなら提示された範囲の中でしか約分できない 39 public Fraction Reduction() 40 { 41 var d = Denominator; 42 var n = Numerator; 43 44 //アライさん無理にLINQ使うのはやめなよ 45 foreach (var p in PrimeNumbers) 46 { 47 if (d % p == 0 && n % p == 0) 48 { 49 d /= p; 50 n /= p; 51 } 52 } 53 54 return new Fraction(n, d); 55 } 56 57 58 //異論は有るでしょーがまぁ仕様にあわせた 59 public override string ToString() 60 { 61 var tmp = Reduction(); 62 63 var integer = tmp.Numerator / tmp.Denominator; 64 65 if (tmp.Numerator % tmp.Denominator == 0) return integer.ToString(); 66 67 if (integer >= 1) 68 { 69 return $"{integer}.{tmp.Numerator % tmp.Denominator}/{tmp.Denominator}"; 70 } 71 72 return $"{tmp.Numerator}/{tmp.Denominator}"; 73 } 74 } 75 76 class Program 77 { 78 static void Main() 79 { 80 //乱数 81 Random rnd = new Random(42); 82 83 //とはいえやっぱりLINQはどこかで使いたいw 84 var hoge = from _ in Enumerable.Range(0, 10) 85 let x = new Fraction(rnd.Next(2, 11), rnd.Next(2, 11)) 86 let y = new Fraction(rnd.Next(2, 11), rnd.Next(2, 11)) 87 //ココで+を使いたかった。 88 let result = x + y 89 select result.ToString(); 90 91 foreach (var s in hoge) 92 { 93 Console.WriteLine(s); 94 } 95 } 96 97 98 } 99} 100 101 102

投稿2017/06/08 15:07

編集2017/06/09 00:52
Tokeiya3

総合スコア260

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

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

otintin

2017/06/09 11:34

ご回答ありがとうございました。 まず、自分の勉強(知識)不足ゆえ、理解が完璧でないことをお許し下さい。 約分する時には素数で割る、分子の和を出す際はクロスで考える、 約分は素数2・3・5で分母分子共に割りきれた時にのみ割る、 など数学的な面が勉強になりました。 常識なのかもしれませんが、自分の頭は中学で止まっていまして。。 そういう所もしっかり勉強していかねばと思いました。 文字列を返して表示させるというのも全く浮かびませんでした。 {0},{1}のような表示の仕方に囚われてたのが払拭できた気がします。 しかし、まだオブジェクト指向の知識があやふやなせいで、 頭を捻って考えても、なんとなくでしか理解できないのが悔しいところです。 もう一度基礎を固め、再度チャレンジしたいと思います。 折角書いてくださったのに申し訳ないのですが、お戯れはその時まで保留とさせてください…。
Tokeiya3

2017/06/09 15:37 編集

理解可能な範囲で理解した上で先に進みましょ :) それが一番の近道です。 お戯れは、所詮お戯れ、その先に進みたいときの一助になれば程度故 気にする必要も無いです。所詮は自己満足なのですから ;) けれど、貴兄の理解の一助になれば望外の喜びです。
otintin

2017/06/11 14:17

はい!そう仰っていただけて嬉しいです。 ですが、やっぱり理解できないのは歯痒いので、 勉強を継続し、必ずや理解できるようになってみせます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問