二分探索プログラムの添削願い

このQ&Aのポイント
  • 以下のプログラムは、方程式の解を求めるための二分探索プログラムです。
  • プログラムでは、ユーザーに関数fのパラメーターを入力させ、その次数に応じて処理を分岐します。
  • fが定数項が0でない三次式の場合は解の公式を用いて虚数解を含むすべての解を求め、それ以外の場合は二分法を用いて解を求めます。
回答を見る
  • ベストアンサー

二分探索プログラムの添削願い

方程式の解を求める以下のプログラムの添削やアドバイスをお願いします。プログラムが行う処理手順の概要は、以下の通りです。解くべき方程式はmainの2行目に(i)と示されています。 (1)方程式を表示し、ユーザーに関数fのパラメーターを入力させる。 (2)関数の次数に応じて処理が分岐し、fが”定数項が0でない三次式”の場合は(4)へ飛ぶ。それ以外は(3)へ。 (3)解の公式などを用いて虚数解を含むすべての解を求めて表示し、終了。 (4)二分法を用いて解を求める。二分法の区間を入力するようユーザーを誘導する。 (5)設定された区間をさらに1000等分してそれぞれの区間に対して(上限値に近いほうから順番に)二分法を用い、見つけられるだけの実数解を探索し、結果を表示して終了。 ・・・以上です。載せているソースコードは前文ではなく抜粋です。本当はソースコードを全部載せたかったのですが、字数制限の都合上かなり省略があります。 実際には、*には方程式のパラメータ入力への誘導部が、**には二分法の区間設定への誘導が省略されています。これらの部分で、関数のパラメータをa,b,c,dに、二分法の区間をupper,lowerに格納します。  また、ユーザーを誘導するメッセージ、求めた方程式の解を表示するメッセージなども全て省略しています。途中に出てくるメソッドですが、 ・checkDegree(コードは省略されています。) fのパラメータを渡すと、fの次数をdegreeに返す。 ・soluteEq(コードは省略されています。) fの次数とパラメータを渡すと、a=0かつd=0の場合を除き(i)の解を全て表示し、終了フラグを立てる。 ・foo(省略されています。) 変数xと、方程式のパラメータを与えると、fの値を返す。 ・doBisection 二分法の区間と方程式のパラメータを渡すとその区間を1000等分して新たな区間を 1000個作成し、それぞれの区間に(上限値に近いほうから)二分法を実行。見つけられた解の個数を返す。 となっています。 自分が作成したプログラムをEclipse上で動かしてみると、一応正確な答えは帰ってきました。 ただ自分の作ったプログラムの問題として、doBisectionの処理において、たまたまn番目の区間の下限値が方程式(i)の解だった場合、n+1番目の区間に対して二分法処理が行われないことです。 これはn番目の区間の下限値が、n+1番目の区間の上限値になってしまうためなのですが、何か良い解決策をご提示願えないでしょうか。 また他にも、ソースコードをより可読的にするための改善点などありましたら、 ご提案願えないでしょうか。プログラミング初学者なのですが、独学でやっていますので 変な癖がついたりするのが結構不安です。 以下ソースコード抜粋を貼ります。皆様のお言葉、お待ちしております・・・ (自分は返信をスルーすることはありません。ベストアンサーも必ず指定して質問を完結させます。) //以下main methodの中身 double a = 0, b = 0, c = 0, d = 0, upper = 1, lower = 0; System.out.println("f(x) = a * x^3 + b * x^2 + c * x + d = 0 ・・・(i).");//* int degree = checkDegree(a,b,c,d); int endflag = soluteEq(degree,a,b,c,d); if(endflag == 1){return;}//** if(upper == lower){return;} if(upper < lower){ double change = lower; lower = upper; upper = change; } int find = doBisection(upper,lower,a,b,c,d); }//main以上 //以下doBisection int i = 0, find = 0; while(true){ double u = (1 - 0.001*i)*upper + 0.001*i*lower; double l = (1 - 0.001*(i+1))*upper + 0.001*(i+1)*lower; if(find == 3){break;} if(i == 999){break;} while(true){ double fl = foo(l,a,b,c,d); double fu = foo(u,a,b,c,d); if(fu*fl > 0){ i++; break;} if(fl == 0){ find++; i++; break;} if(fu == 0){ System.out.println("x = "+ u +"\r\nは方程式(i)の解です。"); find++; i++; break;} if(u - l < 10E-10){ i++; find++; break;} double m = (u + l) / 2; double fm = foo(m,a,b,c,d); if(fm == 0){ find++; i++; break;} if(fu*fm > 0){u = m;} if(fl*fm > 0){l = m;} } } return find; }//doBisection以上。

  • Java
  • 回答数3
  • ありがとう数9

質問者が選んだベストアンサー

  • ベストアンサー
  • f272
  • ベストアンサー率46% (7992/17078)
回答No.3

> 危険というのは、区間の境界値が実数解の十分な近似である場合に、==0の判定文ではそれを捉え損ねるという理解で間違いないでしょうか。 そういうことだ。 > whileをforに変えれば「n番目の下限値がn+1番目の上限値になってしまう」という問題も解決するのでしょうか。 そんなわけないでしょ。epsを小さい正数として 1区間の2分探索に与える上限uと下限lに関してfu*fl<-epsとなっていれば2分探索は簡単に書けるよね。この場合には必ずこの範囲に1個または3個の解が存在するのだけれど,あなたの与えられた課題では,3個の解がある場合でも1個だけ発見して残りの2個は見逃してもいいのだから簡単でしょ。 fu*fl>=epsであればこの範囲には解がないとして次の範囲に行く。 それ以外の場合にはuかlのどちらか,または両方が解になるけど,例えばuだけを判定するようにしても,lは次回で判定されるから見逃すことはないでしょう。 でも,もう一度言うけど,方程式の解を求めるのが目的ならこんな下手くそなアルゴリズムは捨てたほうがマシだと思うぞ。

moguraduka
質問者

お礼

ありがとうございます、わかりやすかったです。 方程式の解を求めたいわけでなく、単に課題の内容がそうなっていただけなので、ひとまずこのまま進める予定です。

その他の回答 (2)

  • f272
  • ベストアンサー率46% (7992/17078)
回答No.2

if(fl == 0){ とか if(fu == 0){ を==0で判定するのは危険でしょう。 判定したい数の絶対値<小さい数 で判定すべきです。 しかし,それ以前にどうして区間を1000等分するのか? それでは解が区間の1000分の1未満の差しかないときに対応できません。 ちゃんとそれぞれの解を挟むようにアルゴリズムを考えてください。

moguraduka
質問者

お礼

ありがとうございます。 危険というのは、区間の境界値が実数解の十分な近似である場合に、==0の判定文ではそれを捉え損ねるという理解で間違いないでしょうか。 今回作成するアルゴリズムの内容の問題点私も承知していますが、ひとまずそのようなプログラムを作る課題を与えられたということで、そこは問題にしないということでお願いします。

  • kmee
  • ベストアンサー率55% (1857/3366)
回答No.1

1つのwhileで全部やろうとして、こんがらがってるだけではないでしょうか? for(1000 区間分 ) { 1区間の2分探索 } と考えてコーディングしてみては? 構造化プログラミングといった、プログラミングの考え方を勉強していくとよいでしょう。

moguraduka
質問者

お礼

ありがとうございます。 構造化プログラミングというキーワードで自学します。 for文を使って二分法をせよとのことですが、 whileをforに変えれば「n番目の下限値がn+1番目の上限値になってしまう」という問題も解決するのでしょうか。

関連するQ&A

  • C++で二次方程式のプログラム

    大分前に二次方程式のプログラムを作るって問題が出ていました。 しかし、セットで作ったプログラムのフローチャートを書くんですがフローチャートが分かりません。 条件は 虚数解 a=0 実数解 の場合分けをして、解を出すってプログラムなんですが、多分、それ自体は合っていると思います。 しかし、そのフローチャートを書きなさいって問題があったんですが、それが未だに分からないんですが、これをフローチャートに書くとすると、どう書けばいいですか? #include <iostream> #include <cmath> using namespace std; int main() { double a,b,c,d,x0,x1; cout << "aを入力してください\n"; cin >> a; cout << "bを入力してください\n"; cin >> b; cout << "cを入力してください\n"; cin >> c; d=b*b-4*a*c; x0 = (-b + sqrt(d)) / (2 * a); x1 = (-b - sqrt(d)) / (2 * a); if(a==0) { cout << "解は1つで" << -c/b << "です\n"; } else if(d>0) { cout << "解は二つの実数解で,解は" << x0 << "," << x1 << "です\n"; } else { cout << "解は二つの虚数解で,解は" << (-b) / (2 * a) << "+i" << sqrt(-d) / (2 * a) << " , " << (-b) / (2 * a) << "-i" << sqrt(-d) / (2 * a) << "です\n"; } return 0; }

  • C++でのプログラミングについてです

    プログラミング初心者です C++で二次方程式の解のプログラムを作成したのですがうまく作動させることができません…どこがおかしいのでしょうか、またどのように変更すればよいでしょうか 発生したエラーは 15行 型voidの値をintのエンティティに割り当てることはできません 34行 宣言が必要です 55行 宣言が必要です 15行 voidが他の型と同時に使われました 34行 '{'を見つけました(関数のヘッダーがないかもしれません). 68行 構文エラー:'}' です よろしくお願いいたします #include<stdlib.h> #include<math.h> void solve(double, double, double); int main(void) { double a, b, c; /*二次方程式の定数*/ double D, x1, x2, r1, r2; int ret; printf("ax^2 + bx + c = 0 の係数 a, b, c を入力してください---> \n"); scanf_s("%lf %lf %lf", &a, &b, &c); printf("2次方程式を解いた結果は次の通りです。\n"); ret = solve(a, b, c, &x1, &x2, &r1, &r2); switch (ret) { case-1: printf("係数がおかしい\n"); break; case 0: printf("解は虚数解で%.2f+%.2fi と%.2f-%.2fi です\n", r1, r2, r1, r2); break; case 1: printf("解は実数解となり、%f です。\n", x1); break; case 2: printf("解は実解解で、%f と %f です。¥n", x1, x2); break; } return 0; } void solve(double a, double b, double c, double x1, double x2, double r1, double r2); { if (a == 0.0) { if (b == 0.0) { return -1; } { x1 = -c / b; return 1; } } else { D = b * b - 4 * a * c; if (D >= 0) { x1 = (-b + sqrt(D)) / (2.0 * a); x2 = (-b - sqrt(D)) / (2.0 * a); return 1; } if (D == 0) { x1 = -b / (2 * a); return 1; } else { r1 = -b / (2 * a); r2 = sqrt(-D) / (2 * a); return 0; } } }

  • 二次方程式のプログラム

    C++で二次方程式の解を求めるんですが、虚数解の場合、a=0の場合、実数解の場合で求めるようにしているんですが、 #include <iostream> #include <cmath> using namespace std; int main() { double a,b,c; cin >> a >> b >> c; if(a==0) { cout << (-c/b) << '\n'; } else if((b*b-4*a*c)<0) { cout << (-b/2/a) << 'i' << sqrt(4*a*c-b*b)/2/a << '\n'; } else { この先の最後の一文教えてください。抜けてて書いてないんです。

  • C++でのプログラムについての質問です

    このような二次関数の解を求めるプログラムを作成したのですが、自作関数solveをvoid solve(double, double, double)のように変更し同じ動作をするように変更したいです どのようにへんこうすればよいでしょうか #include<stdio.h> #include<stdlib.h> #include<math.h> int main(void) { double a, b, c; /*二次方程式の定数*/ double D, x1, x2, r1, r2; printf("ax^2 + bx + c = 0 の係数 a, b, c を入力してください---> \n"); scanf_s("%lf %lf %lf", &a, &b, &c); printf("2次方程式を解いた結果は次の通りとなる。\n"); if (a == 0.0) { if (b == 0.0) { printf("係数がおかしい\n"); exit(-1); } { x1 = -c / b; printf("解は%f です。\n", x1); exit(0); } } else { D = b * b - 4 * a * c; if (D >= 0) { x1 = (-b + sqrt(D)) / (2.0 * a); x2 = (-b - sqrt(D)) / (2.0 * a); if (D == 0.0) { printf("解は %f です。\n", x1); } else { printf("解は %f と %f です。¥n", x1, x2); } } else { r1 = -b / (2 * a); r2 = sqrt(-D) / (2 * a); printf("解は%.2f+%.2fi と%.2f-%.2fi \n", r1, r2, r1, r2); } } return 0; }

  • 二分法のプログラムについて

    下の用なプログラムを作ったのですがどうしても正しい答えを導くことができません。自分でもいろいろ調べてみましたがわかりません。誰かご教授宜しくお願いします。 #include<stdio.h> #include<stdlib.h> #define MAX 10 int n , count; double c[MAX+1]; double a,b,e; void nyuuryoku(void) { int i; printf("nの入力>"); scanf("%d",&n); if(n>MAX){printf("最大次数を超えている");exit(1);} else if(n<0){printf("nが負");exit(2);} else{for(i=0;i<=n;i++){printf("係数の値>");scanf("%lf",&c[i]);} }} double f(double x) {double y; int i; y = c[0]; for(i=1;i<=n;i++){ y=y*x+c[i];} return y; } void hani(void){ printf("aの値>");scanf("%lf",&a); printf("bの値>");scanf("%lf",&b); printf("eの値>");scanf("%lf",&e); if(e<=0){printf("eが0または負"); exit(3);} if(f(a)==0){printf("%f",f(a)); exit(4);} if(f(b)==0){printf("%f",f(b)); exit(5);} if(f(a)*f(b)>0){printf("初期値異常"); exit(6);}} double nibun(void) {double c; if(b>a){ while(b-a>e){ count++; c=(a+b)/2; if(f(c)==0){ return c;} if(f(a)*f(c)<0){b=c;} if(f(b)*f(c)<0){a=c;} } return a;} if(a>b){ while(a-b>e){ count++; c=(a+b)/2; if(f(c)==0){ return c;} if(f(b)*f(c)<0){a=c;} if(f(a)*f(c)<0){b=c;} } return a;} } void syutsuryoku(double x){ printf("x=%lf\n",x); printf("f(x)=%lf\n",f(x)); printf("繰り返し回数=%d\n",count); } int main(void){ double ans; count=0; nyuuryoku(); hani(); ans = nibun(); syutsuryoku(ans); }

  • C++の二次方程式のプログラム

    二次方程式の解を求めるプログラムで虚数解の場合、a=0の場合、実数解の場合で求めるようにしているんですが、 #include <iostream> #include <cmath> using namespace std; int main() { double a,b,c; cin >> a >> b >> c; if(a==0) { cout << (-c/b) << '\n'; } else if((b*b-4*a*c)<0) { cout << (-b/2/a) << 'i' << sqrt(4*a*c-b*b)/2/a << '\n'; } else { 次にcout が来るのは分かってるんですが、数学でこういう書き方しなし、ここから先の書き方が分からないんですが、どうやって書けばいいですか? 多分return 0; } のぞいてあと2行か1行だと思うんですが

  • 2分法で方程式の複数の解を自動的に求めるには?

    2分法を授業で習い、アルゴリズムは理解できました。 そして、2分法で方程式の解を求めるプログラムも完成したのですが、 3次方程式などの全ての解を2分法で求める場合、本来ならば、 グラフなどを描き、適切な初期値を自分で変更していく必要があると思います。 しかし、方程式の複数の解を自動的に求めたいのです。 もし、2分法で方程式の複数の解を自動的に求めるアルゴリズムがあれば教えていただけないでしょうか。 よろしくお願いします。 ※ 私が作成したプログラム(C言語)も載せておきます。 #include <stdio.h> #include <math.h> double f(double x) { return ((x-1.0)*(x-2.0)*(x-3.0)); } int main () { int i; double a,b,x; double gosa = 1.0e-14; printf("aの値を入力してください。\n"); scanf("%lf",&a); printf("bの値を入力してください。\n"); scanf("%lf",&b); if(f(a)*f(b) >0) printf("aとbの範囲の中に適切な解が存在しません。\n"); while(1){ x = (a+b)/2.0; printf("%.14f\n",x); if(fabs(b-a)<gosa) break; if(f(a)*f(x)<=0){ b = x; } else{ a = x; } } return(0); }

  • 2次方程式の解

    ax^2+bx+c=0の方程式について abcを自分で入力して、「2次方程式として成り立つか」の判断をし、2次方程式であればその解を求めるプログラムです。 2次方程式の解き方をなんとなく忘れていたので、数IIの教科書やらで確認してみました。 以下のように作成したのですが、解が出るはずの値を入力しても強制的に終了してしまいます。どこがおかしいのでしょうか? 他に気になる点は、メイン関数にて「2次方程式として成り立った場合」にはサブへ移動ができているのでしょうか。 あと、仮に↑が合っていたとして、異なる2つの虚数解の計算方法は以下のやり方でも良いのかどうかもお聞きしたいです。 よろしくお願いします。 #include<stdio.h> #include<math.h> void niji(double a,double b,double c){ double x1,x2,x3,y1,y2,D; D=b*b-(4.0)*a*c; if(D>0){ printf("2つの異なる実数解\n"); x1=(-b+sqrt(D))/(2.0*a); x2=(-b-sqrt(D))/(2.0*a); printf("x= %f , %f \n",x1,x2); } else if(D==0){ printf("重解\n"); x3=(-b)/(2.0*a); printf("x= %f \n",x3); } else{ printf("2つの異なる虚数解\n"); x3=(-b)/(2.0*a); y1=sqrt(D)/(2.0*a); y2=-sqrt(D)/(2.0*a); printf("x= %f + i %f, %f - i %f\n",x3,y1,x3,y2); } return; } int main(void){ double a,b,c; printf("ax^2+bx+c=0の式のabcを入力せよ\n"); while(scanf("%f %f %f",&a,&b,&c)){ if(a==b==c==0){ break; } else if((a==b==0)&&(c!=0)){ printf("不能\n"); } else if((a==0)&&(b!=0)){ printf("1次方程式になる\n"); } else{// 入力されたabcが↑の3つに該当しなければ niji(a,b,c);//←サブ関数に示した2次方程式を解く } } return 0; }

  • 関数のプログラムについて

    任意の二次方程式ax^2+bx+c=0をとくプログラムの作成です 引数をa,b,cとして、解の大きい方を返すというものなのですが、 僕は以下のようにして組んだのですが、うまくいきません。 と、いうより、関数の作り方がいまいちわからないです。 どこが駄目なのか教えてください。 作ってみたやつ↓ #include<math.h> #include<stdio.h> int a,b,c; double d; double x,y,z; int main(void) { a=1; b=2; c=1; printf("ax^2+bx+c=0\n "); d=b^2-4*a*c; if (d<0){printf("kyosuukai\n)} else if(d>=0) { x=(b+sqrt(b^2-4*a*c))/2*a; y=(b-sqrt(b^2-4*a*c))/2*a; if(x>=y){z=x} else if(x<y){z=y} printf("x= %f\n",z); } }

  • このプログラムを添削してください

    #include<stdio.h> #include<math.h> #define n 3 double multi(double a[n],double b[n]); int main(void) { double a[n]; double b[n]; int i; double menseki; for(i=0;i<=n;i++) {scanf("%lf",a[i]); scanf("%lf",b[i]); printf("%dつめの頂点座標は(%d,%d)",i+1,a[i],b[i]); } menseki=multi(a,b); printf("面積は%d",fabs(menseki)); return(0); } double multi(double a[n],double b[n]) { double menseki; menseki=(a[0]*(b[1]-b[2])+a[1]*(b[2]-b[0])+a[2]*(b[0]-b[1]))/2; return(menseki); } 頂点の座標を入力し、ヘロンの公式を用いて三角形の面積を求めるプログラムなのですが、 正常に動作しません。お力添えお願いします。