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

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

ただいまの
回答率

90.62%

  • C#

    6827questions

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

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

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 1,720

otintin

score 2

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            ← 整数になるケース

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

    class Program
    {
        static void Main(string[] args)
        {
            Random rnd = new Random();
            int[] bo = new int[2];
            int[] si = new int[2];

            for (int i = 0; i < 2; i++) {
                int bo_a = rnd.Next(2, 10);
                int si_b = rnd.Next(1, 11);
                bo[i] = bo_a;
                si[i] = si_b;
            }

            int bo_sum = bo[0] * bo[1];
            int si_sum = si[0] * (bo_sum / bo[0]) + si[1] * (bo_sum / bo[1]);
            int yaku_si, yaku_bo;

            Console.Write("{0}/{1} + {2}/{3} = ",si[0],bo[0],si[1],bo[1]);

            if (si_sum == bo_sum)
            {
                Console.WriteLine("1");
            }
            else if (si_sum % bo_sum == 0)
            {
                Console.WriteLine("{0}", si_sum / bo_sum);
            }
            else if (si_sum > bo_sum)
            {
                Console.WriteLine("1.{0}/{1}", si_sum - bo_sum, bo_sum);
            }
            else
            {
                if (bo[0] > bo[1])
                {
                    yaku_si = si_sum / bo[1];
                    yaku_bo = bo_sum / bo[1];
                    Console.WriteLine("{0}/{1}", yaku_si, yaku_bo);
                }
                else if (bo[1] > bo[0])
                {
                    yaku_si = si_sum / bo[0];
                    yaku_bo = bo_sum / bo[0];
                    Console.WriteLine("{0}/{1}", yaku_si, yaku_bo);
                }
                else
                {
                    Console.WriteLine("{0}/{1}", si_sum, bo_sum);
                }
            }

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+2

概ねこんな感じかと

using System;

namespace ConsoleApp1
{
    //分数を表現する構造体
    struct Fraction
    {
        public Fraction(int numerator, int denominator)
            :this()
        {
            Numerator = numerator;
            Denominator = denominator;
        }
        public int Numerator { get; }
        public int Denominator { get; }
    }

    class Program
    {
        //約分するときに使う素数(尚、数えてもあんまり意味は無い)
        private static int[] PrimeNumbers = {5, 3, 2};

        static void Main()
        {
            //乱数
            Random rnd = new Random(42);

            //とりあえず10回
            for (int i = 0; i < 10; i++)
            {
                var x = new Fraction(rnd.Next(2, 11), rnd.Next(2, 11));
                var y = new Fraction(rnd.Next(2, 11), rnd.Next(2, 11));

                var result = Add(x, y);
                Console.WriteLine($"{x.Numerator}/{x.Denominator}+{y.Numerator}/{y.Denominator}={Display(result)}");
            }


        }


        //加算処理
        static Fraction Add(Fraction x, Fraction y)
        {
            var denominator = x.Denominator * y.Denominator;
            var numerator = x.Numerator * y.Denominator + x.Denominator * y.Numerator;

            return Reduction(new Fraction(numerator, denominator));
        }

        //約分
        static Fraction Reduction(Fraction fraction)
        {

            int numerator = fraction.Numerator;
            int denominator = fraction.Denominator;


            foreach (var p in PrimeNumbers)
            {
                if (numerator % p == 0 && denominator % p == 0)
                {
                    numerator /= p;
                    denominator /= p;
                }
            }

            return new Fraction(numerator, denominator);
        }

        //通分して文字列に
        static string Display(Fraction fraction)
        {
            var f = Reduction(fraction);
            int tmp = f.Numerator / f.Denominator;

            //整数だった場合
            if (fraction.Numerator % fraction.Denominator == 0)
            {
                return tmp.ToString();
            }

            //帯分数だった場合
            if (tmp >= 1)
            {
                return $"{tmp}.{fraction.Numerator % fraction.Denominator}/{fraction.Denominator}";
            }

            return $"{f.Numerator}/{f.Denominator}";
        }

    }
}

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

using System;
using System.Linq;

namespace ConsoleApp1
{
    //値型にしなよ~と言う意見は重々承知してるけど、0/0を避けたかったの。
    //けどNullに成る病にかかった模様
    class Fraction
    {

        private static readonly int[] PrimeNumbers = { 5, 3, 2 };

        //尚、範囲チェックはしてない模様(故、0以下の数が来ると宇宙の法則が乱れる)
        public Fraction(int numerator, int denominator)
        {
            Numerator = numerator;
            Denominator = denominator;
        }

        //イマドキなら逆にreadonly付けてpublicフィールドでも良いかもね?
        public int Numerator { get; }
        public int Denominator { get; }

        //演算子のオーバーロードして、ホントに分数同士を足せるようにした
        //Nullは来ない。良いね?
        public static Fraction operator +(Fraction x, Fraction y)
        {
            var denominator = x.Denominator * y.Denominator;
            var numerator = x.Numerator * y.Denominator + x.Denominator * y.Numerator;

            return new Fraction(numerator, denominator);
        }

        //約分した分数を返す
        //まぁImmutableにしたかった
                //ついでに言うなら提示された範囲の中でしか約分できない
        public Fraction Reduction()
        {
            var d = Denominator;
            var n = Numerator;

            //アライさん無理にLINQ使うのはやめなよ
            foreach (var p in PrimeNumbers)
            {
                if (d % p == 0 && n % p == 0)
                {
                    d /= p;
                    n /= p;
                }
            }

            return new Fraction(n, d);
        }


        //異論は有るでしょーがまぁ仕様にあわせた
        public override string ToString()
        {
            var tmp = Reduction();

            var integer = tmp.Numerator / tmp.Denominator;

            if (tmp.Numerator % tmp.Denominator == 0) return integer.ToString();

            if (integer >= 1)
            {
                return $"{integer}.{tmp.Numerator % tmp.Denominator}/{tmp.Denominator}";
            }

            return $"{tmp.Numerator}/{tmp.Denominator}";
        }
    }

    class Program
    {
        static void Main()
        {
            //乱数
            Random rnd = new Random(42);

            //とはいえやっぱりLINQはどこかで使いたいw
            var hoge = from _ in Enumerable.Range(0, 10)
                let x = new Fraction(rnd.Next(2, 11), rnd.Next(2, 11))
                let y = new Fraction(rnd.Next(2, 11), rnd.Next(2, 11))
                //ココで+を使いたかった。
                let result = x + y
                select result.ToString();

            foreach (var s in hoge)
            {
                Console.WriteLine(s);
            }
        }


    }
}

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/06/09 20:34

    ご回答ありがとうございました。
    まず、自分の勉強(知識)不足ゆえ、理解が完璧でないことをお許し下さい。

    約分する時には素数で割る、分子の和を出す際はクロスで考える、
    約分は素数2・3・5で分母分子共に割りきれた時にのみ割る、
    など数学的な面が勉強になりました。
    常識なのかもしれませんが、自分の頭は中学で止まっていまして。。
    そういう所もしっかり勉強していかねばと思いました。

    文字列を返して表示させるというのも全く浮かびませんでした。
    {0},{1}のような表示の仕方に囚われてたのが払拭できた気がします。

    しかし、まだオブジェクト指向の知識があやふやなせいで、
    頭を捻って考えても、なんとなくでしか理解できないのが悔しいところです。
    もう一度基礎を固め、再度チャレンジしたいと思います。
    折角書いてくださったのに申し訳ないのですが、お戯れはその時まで保留とさせてください…。

    キャンセル

  • 2017/06/10 00:36 編集

    理解可能な範囲で理解した上で先に進みましょ :)
    それが一番の近道です。

    お戯れは、所詮お戯れ、その先に進みたいときの一助になれば程度故
    気にする必要も無いです。所詮は自己満足なのですから ;)

    けれど、貴兄の理解の一助になれば望外の喜びです。

    キャンセル

  • 2017/06/11 23:17

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

    キャンセル

+2

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

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

public static class MathUtil {
  // ユークリッドの互除法で最大公約数を求める関数を用意しておく
  public static int Gcd(int a, int b) { ... }
}

// 変更不可なクラスにしてみる。
// 変更不可なのでgetter/setterは不要。
public class Ratio {
  public const int numerator;
  public const int denominator;

  public Ratio(int numerator, int denominator) {
     // コンストラクターで簡約するのも一つの手
     int gcd = MathUtil.Gcd(numerator, denominator);
     this.numerator = numerator / gcd;
     this.denominator = denominator / gcd;
  }

  // こんなケースでもない限りなかなか演算子のオーバーロードを使う機会がないので使ってみる
  public static Ratio operator+ (Ratio a, Ratio b) {
    int nu = a.numerator * b.denominator
           + b.numerator * a.denominator;
    return new Ratio(nu, a.denominator * b.denominator);
  }

  ...

  public override string ToString() {
    ...
  }
}
...

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/06/09 20:36

    ご回答ありがとうございました。
    なるほど…機能別に作っておくと確かに分かりやすいです。
    オブジェクト指向、勉強したばかりで使えこなせないと思ってましたが、
    使わないでやろうとすればかえってしんどくなるんですね…。
    使わなかったから、オブジェクト指向が理解できなかったのかもしれません。
    毛嫌いせず、使えそうなときには使っていくよう心がけます。

    constで値を変更できなくなるのは知ってましたが、
    このようにコンストラクターで入れられるんですね。勉強になりました。

    オーバーロードのところは考えても分かりませんでした。。
    そもそもオーバーロードというのが、同じ名のメソッドを書けるという認識しかなく、
    それはオブジェクト指向の理解があやふやだから、オーバーロードの使うタイミングも
    理解できてないのだと思います。そこら辺ちゃんと勉強してきます。

    キャンセル

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

  • ただいまの回答率 90.62%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • C#

    6827questions

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