アプリ版:「スタンプのみでお礼する」機能のリリースについて

2次方程式の解を求めるプログラミングの課題なのですが、どのようにすればよいのでしょうか。
プログラミングについてまだまだなので、解説もお願いしたいです。問題は以下のとおりです。

2次方程式ax2+bx+c=0 (a≠0)の係数a,b,c,を入力してその解を表示するプログラムを作成しなさい。2次方程式 の係数a、b、cと解を格納する配列を引数として、2つの異なる実数解を持つ場合は整数1を、重解を持つ場合は整数0を、虚数解を持つ場合は整数-1を戻り値とする関数solveQeを定義して利用すること。解の値は、小数点以下第2位まで表示するものとする。
(注) 平方根を求める関数 double sqrt(double x) の引数xの値は正でなければ エラーとなる。


(実行例)
2次方程式ax^2+bx+c=0の解を求めます
係数a,b,c >> 1,7,9
2つの異なる実数解: -1.70, -5.30

2次方程式ax^2+bx+c=0の解を求めます
係数a,b,c >> 1,8,16
重解: -4.00

2次方程式ax^2+bx+c=0の解を求めます
係数a,b,c >> 2,6,9
虚数解: -1.50 ± 1.50i


(ソースコードイメージ)

#include <stdio.h>
#include <math.h>

int solveQe(int a, int b, int c, double []); //プロトタイプ宣言

int main(void)
{
int a,b,c,d;
double ans[2];

・・・
printf("虚数解: %.2lf ± %.2lf\n",ans[0],ans[1]);
・・・

}

/*
* 2次方程式ax^2+bx+c=0の解
* @引数 係数a,b,c 解m[]
*/
int solveEq(int a, int b, int c, double m[])
{

・・・

}

A 回答 (5件)

あー、うん、C言語慣れてないとメンドくせぇ問題だな、ってのは分かります(笑)。


僕も

「2つの異なる実数解を持つ場合は整数1を、重解を持つ場合は整数0を、虚数解を持つ場合は整数-1を戻り値とする関数solveQeを定義して利用すること」

って文を読んだ時、

「何でやねん。直接解を返さないで何で1、0、-1を返さなアカンねん?」

って初見でそう思いました(笑)。
基本的にモダンな言語のユーザーだと皆同じように思うでしょうね(笑)。「なんてムダなんだ!」と(笑)。これはある意味、C言語らしい問題ですね(笑)。

さて、ポイントは2つあります。
まず一つ目。
今書いたように、問題文の指定だと関数solveQeは「計算する」けど「計算結果」は返さない、と言う不思議な挙動を仕様とします。
つまり、例えばmain関数内で適当な変数dを用意して

d = solveQe(a, b, c, ans);

とした場合、dには何が入るでしょうか?ちょっと考えてみてください。

そうですね、1か0か-1のどれか、です。実は全く計算結果は入らないんです。
言い換えると事実上、2次方程式の解、つまり計算結果は「捨てられてる」って事なんですね。非常に不思議なんですが、これが実はこの問題のポイントなんです。
じゃあ、ここでdは一体何に使われるべきなんでしょうか?答えは一種のフラグなんですね。表示を切り替える「サイン」として働きます。
つまり、問題の要求はmain関数上に次のようなコードを書け、って言ってるんです。

d = solveQe(a, b, c, ans);
if (d > 0)
   {
      printf("係数a, b, c >> %d, %d, %d\n", a, b, c);
      printf("2つの異なる実数解: %.2lf, %.2lf", ans[0] + ans[1], ans[0] - ans[1]);
   }
   else if (d == 0)
   {
      printf("係数a, b, c >> %d, %d, %d\n", a, b, c);
      printf("重解: %.2lf", ans[0] + ans[1]);
   }
   else
   {
      printf("係数a, b, c >> %d, %d, %d\n", a, b, c);
      printf("虚数解: %.2lf ± %.2lfi", ans[0], ans[1]);
   }

つまり、dの中身によって印字される内容を変えろ、って事です。
「計算上」の話をすると実はsolveQeが行ってる事はこの「切り替え」のサインのみの「返し」で、計算結果自体は全く関係ない、事実上その計算結果「自体」は捨てられてるんです。これが実はこの問題の一つ目のポイントです。
いやちょっと待てよ、と。「計算結果自体が捨てられてるのなら計算結果が見れないじゃん」と思うでしょう。その通りなんですが、もう1つのポイントがC言語特有(でもねぇけどモダンな言語じゃちょっと無い)の性質絡みの事があるんです。
ではその2つ目のポイント。
C言語では別の関数に配列を渡すと基本的に配列の内容を「書き換えてしまう」って特徴があるんですね(正確に言うとC言語だけじゃなくって低レベルの言語だとそう言う事がしばしばある。メモリ効率を優先してるから)。難しい言葉で言うと「副作用」(計算結果が問題にならずに、メモリの状態を書き換えたり、とか印字する「計算自体とは何の関係も無い作用)って言うんですが、このプログラムでは要するにそれが用いられてるわけです。
一般的に昨今のモダンな言語ですと、配列、あるいはリストなんかを別の関数に手渡す場合、大体のケースではそのものを渡さずにコピーを渡すんですね。ところがC言語なんかの低レベル言語だと「データそのものを渡す」ように設計されてて、例えば関数Aから関数Bに配列を渡す、何らかの計算を行って手渡された配列に結果を書き込む(ここが副作用)、そうすると関数Aに戻った時、元あったデータが書き換えられる、って事です(破壊的操作、等と呼んだりします)。
昨今のモダン言語だとこの「破壊的操作」は非常に危険で、データをあっちこっちに手渡した場合、いつ書き換えられるのか、書き換えられた事で元の関数に戻った時にとんでもない挙動が起きたり、とか予測が付かない、ってんで嫌われてるんですが(要するにバグの原因になり得る)、この宿題ではそう言う「破壊的操作」を用いる形式となってるわけです。

solveQe自体は高校数学での「判別式」を知ってればそれほど難しくないです。つまりreturnで「1、0、-1のどれを返すのか」と言うのは判別式に従う、って事ですね。当然if文が活躍します。

int solveQe(int a, int b, int c, double m[2])
{
   int determinant = pow(b,2)-4*a*c;

   printf("%d\n", determinant);

   if (determinant > 0)
   {
      m[0] = -(double)b/(2*a); // ここが破壊的変更
      m[1] = sqrt(determinant)/(2*a); // ここが破壊的変更
      return 1; // 返り値
   }
   else if (determinant == 0)
   {
      m[0] = -(double)b/(2*a); // ここが破壊的変更
      m[1] = sqrt(determinant)/(2*a); // どっちにせよ結果は0
      return 0; // 返り値
   }
   else
   {
      m[0] = -(double)b/(2*a); // ここが破壊的変更
      m[1] = sqrt(-determinant)/(2*a); // ここが破壊的変更
      return -1; // 返り値
   }

}

もう一度言うと、deteminant(判別式)に従ってif文で返り値を変えます。判別式は高校数学が教えるところの

determinant = b^2 - 4*a*c

ですよね。determinantが0より大きければ実数解、0の時は重解、0より小さければ複素数解になります。
そしてこのコードに於いて、引数で与えられた配列mに「計算結果を書き込む」事が配列への「破壊的変更」で、一旦ここで数値が書き込まれたらどの関数から参照されようと「配列の中身は変わってしまってる」って事です。また、この作用自体は「計算結果を返してる」わけじゃないです(この辺ちょっと難しいかもしれませんが、取り敢えず「データ書き換え」は返り値ではない、つまりsolveQe関数が計算結果自体を返してるわけじゃない、って理解してください)。
もう1つの注意は、僕もちとハマってたんですが(笑)、例えば2次方程式の解を計算する際に

解の一部 = -b/(2*a)

を計算しなければいけませんが、この時点でaもbも整数型ですが、これキチンとbをキャストして型をdoubleとかに変更しないと計算が上手く出来ないんですね(笑)。キャストしないと結果がヘンになるんで、キャストを忘れないようにしましょう。

さて、ここまで書けば、あとはmain関数の方でsolveQe関数の「返り値」を受け取って(つまり結果の1、0、-1に従って)、printfで表示を変えるだけ、ってのが残りの作業です。
この辺はイイですよね。ハナクソです。

int main(void)
{
   int a, b, c, d;
   double ans[2];

   printf("a を入力して下さい: ");
   scanf("%d", &a);
   printf("b を入力して下さい: ");
   scanf("%d", &b);
   printf("c を入力して下さい: ");
   scanf("%d", &c);

   d = solveQe(a, b, c, ans);
   if (d > 0)
   {
      printf("係数a, b, c >> %d, %d, %d\n", a, b, c);
      printf("2つの異なる実数解: %.2lf, %.2lf", ans[0] + ans[1], ans[0] - ans[1]);
   }
   else if (d == 0)
   {
      printf("係数a, b, c >> %d, %d, %d\n", a, b, c);
      printf("重解: %.2lf", ans[0] + ans[1]);
   }
   else
   {
      printf("係数a, b, c >> %d, %d, %d\n", a, b, c);
      printf("虚数解: %.2lf ± %.2lfi", ans[0], ans[1]);
   }

   return 0;
}

まあ、こんなモンじゃないでしょうか。
    • good
    • 3
この回答へのお礼

本当に丁寧でわかりやすくて、とても助かりました。ありがとうございました。参考にさせていただきます。

お礼日時:2015/12/04 01:08

二次方程式は解の公式が分かっていれば解けますよね。


ただ複素数は使わない問題設定のようですので、判別式を使って場合分けして解くことになるでしょう。
また解を受け取ったmain関数の方でも返値で判断して場合分けして表示する必要があるでしょう。

質問中にsolveQeとsolveEqが出てきますが両者は同じものの積もりですか。
関数から解を返すとき、解が虚数になる場合は解を浮動小数点でどのように表すかよく検討してください。
main関数内のprintfの記述がある意味ヒントになっています。
    • good
    • 0

算術演算子の使い方のページを貼っておきますね。


http://www9.plala.or.jp/sgwr-t/c/sec03.html
    • good
    • 0

2次方程式の解き方が分かっているならそれをプログラムにするだけだ.

    • good
    • 0
この回答へのお礼

それがわからないので質問したのですが、伝わらないのであれば仕方がないですね。
ありがとうございます。

お礼日時:2015/12/03 16:45

プログラムのことは分かりませんが・・・・


xを0.01ずつ増やしていけば、何処かで関数が0になるか+-が逆転する数があるはずです。
逆転するxをA、Bとすると。
ABの区間をさらに10分の1ずつ増やしていけば必ず解は見つかります。無理数の場合は無限にそれが続くかもしれません
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!