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

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

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

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

アルゴリズム

アルゴリズムとは、定められた目的を達成するために、プログラムの理論的な動作を定義するものです。

Q&A

解決済

5回答

3658閲覧

二つのプログラムに関してなぜ結果が異なるのでしょうか?

carnage0216

総合スコア194

C

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

アルゴリズム

アルゴリズムとは、定められた目的を達成するために、プログラムの理論的な動作を定義するものです。

0グッド

2クリップ

投稿2018/08/01 14:23

編集2018/08/05 04:31

<環境>

  • windows10
  • visual studio 2017
/********************************************* * 連立方程式の解法 ( ガウスの消去法 ) *********************************************/ #include <iostream> // for cout #include <stdio.h> // for printf() // 元の数定義 #define N 4 // 3 using namespace std; /* * 計算クラス */ class Calc { double a[N][N + 1]; // 各種変数 double d; // ダミー int i, j, k; // LOOP インデックス public: // 連立方程式を解く(ガウスの消去法) void calcGaussElimination(); }; /* * 連立方程式を解く(ガウスの消去法) */ void Calc::calcGaussElimination() { // 係数 static double a[N][N + 1] = { //{ 2.0, -3.0, 1.0, 5.0}, //{ 1.0, 1.0, -1.0, 2.0}, //{ 3.0, 5.0, -7.0, 0.0} { 1.0, -2.0, 3.0, -4.0, 5.0}, {-2.0, 5.0, 8.0, -3.0, 9.0}, { 5.0, 4.0, 7.0, 1.0, -1.0}, { 9.0, 7.0, 3.0, 5.0, 4.0} }; // 元の連立方程式をコンソール出力 for (i = 0; i < N; i++) { for (j = 0; j < N; j++) printf("%+fx%d ", a[i][j], j + 1); printf("= %+f\n", a[i][N]); } // 前進消去 for (k = 0; k < N -1; k++) { for (i = k + 1; i < N; i++) { d = a[i][k] / a[k][k]; for (j = k + 1; j <= N; j++) a[i][j] -= a[k][j] * d; } } // 後退代入 for (i = N - 1; i >= 0; i--) { d = a[i][N]; for (j = i + 1; j < N; j++) d -= a[i][j] * a[j][N]; a[i][N] = d / a[i][i]; } // 結果出力 for (k = 0; k < N; k++) printf("x%d = %f\n", k + 1, a[k][N]); } /* * メイン処理 */ int main() { try { // 計算クラスインスタンス化 Calc objCalc; // 連立方程式を解く(ガウスの消去法) objCalc.calcGaussElimination(); } catch (...) { cout << "例外発生!" << endl; return -1; } // 正常終了 return 0; }

以上のプログラムの後退代入の部分に関して、

for (i = N - 1; i >= 0; i--) { d = a[i][N]; for (j = i + 1; j < N; j++) d -= a[i][j] * a[j][N]; a[i][N] = d / a[i][i]; }

の部分を

for (i = N - 1; i >= 0; i--) { d = a[i][N]; for (j = i + 1; j < N; j++) a[i][N] = d - a[i][j] * a[j][N] / a[i][i]; }

と置いてビルドするといじっていない元のプログラムと同じ結果が出ると思ったのですが全く異なった結果が出ました。
いじる前の計算結果はこちらです。

+1.000000x1 -2.000000x2 +3.000000x3 -4.000000x4 = +5.000000 -2.000000x1 +5.000000x2 +8.000000x3 -3.000000x4 = +9.000000 +5.000000x1 +4.000000x2 +7.000000x3 +1.000000x4 = -1.000000 +9.000000x1 +7.000000x2 +3.000000x3 +5.000000x4 = +4.000000 x1 = 1.000000 x2 = 3.000000 x3 = -2.000000 x4 = -4.000000

いじった後の結果はこちらです。

+1.000000x1 -2.000000x2 +3.000000x3 -4.000000x4 = +5.000000 -2.000000x1 +5.000000x2 +8.000000x3 -3.000000x4 = +9.000000 +5.000000x1 +4.000000x2 +7.000000x3 +1.000000x4 = -1.000000 +9.000000x1 +7.000000x2 +3.000000x3 +5.000000x4 = +4.000000 x1 = 82.333333 x2 = 231.666667 x3 = -275.415033 x4 = 19.333333

正直なところなぜ結果が違うのかよくわかりませんでした。別に{}を付いてないので式を一つにまとめても同じ結果が出ると思っていました。

8/5 編集
dを使ってのプログラム

for (i = N - 2; i >= 0; i--) { d = a[i][N]/ a[i][i]; a[i][N] =d*a[i][i]; for (j = N-1; j < N; j++) a[i][N]=a[i][N] - a[i][j] * a[j][N] / a[i][i]; }

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

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

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

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

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

guest

回答5

0

forとprintf()あたりを使って,

1
12
123
1234
12345
...

みたいなのを出力するプログラム書けますか?
おそらく,この辺からやっていった方が良いと思います.
(煽りとかじゃなく,きっと,その方がかえって近道だろうという意味で)

投稿2018/08/02 01:34

fana

総合スコア11656

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

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

0

連立方程式を解くプログラムを多元線形連立方程式にするには

え、なにやってんの
別サイトでわからなくってこのサイトに投稿
で、このサイトでわからなくって別サイトに投稿

人を振り回すのが好きなんですね。
編集前

C

1for (int i = N - 1; i >= 0; i--) { 2 d = a[i][N]; 3 for (int j = i + 1; j < N; j++) { 4 d -= a[i][j] * a[j][N]; 5 } 6 printf("(i)%d, %d\n", i, d); 7 a[i][N] = d / a[i][i]; 8}

のループ処理で変数dがどのような値に変化しているか
理解できてないんじゃないですか。

投稿2018/08/01 20:18

YomogiKOBO

総合スコア187

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

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

carnage0216

2018/08/02 13:32 編集

計算が違ったのですね。 はい、dがどのように変化するか理解できていないので計算を一から行います。
guest

0

ベストアンサー

やるとしたらこう

c

1 for (i = N - 1; i >= 0; i--) { 2 a[i][N] /= a[i][i]; 3 for (j = i + 1; j < N; j++) 4 a[i][N] -= a[i][j] * a[j][N] / a[i][i]; 5 }

ただし、CPUの演算の中で割り算というのは一番遅い演算と言っても過言ではないので
(x+y+z) / w = x/w + y/w + z/w
という展開は無駄でしかない


追記

とりあえず、前回の計算式
a[i][N] = (a[i][N]-なにか) / a[i][i]を思い出す。
分配する
a[i][N] = a[i][N]/a[i][i] - なにか/a[i][i]
あとはそれに従ってプログラムに変換するだけ


for (i = N - 1; i >= 0; i--) {
d = a[i][N]/ a[i][i];
for (j = i + 1; j < N; j++)
a[i][N] = d- a[i][j] * a[j][N] / a[i][i];
}

このfor文をi=0,N=3で展開すると

c

1i = 0; 2d = a[i][3] / a[i][i]; 3j = 1; a[i][N] = d - a[i][j]*a[j][N]/a[i][i]; 4j = 2; a[i][N] = d - a[i][j]*a[j][N]/a[i][i];

みたいな感じ
j=1の時の計算が丸ごと捨てられています。

投稿2018/08/01 15:20

編集2018/08/01 21:33
asm

総合スコア15147

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

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

carnage0216

2018/08/01 15:26

やはり私のようなプログラミング思考のない人間は、うまい人のプログラムの書き方を真似るのが一番ですね。 どうもありがとうございます。ただなぜ、割り算は遅いのでしょうか? CPUの演算では掛け算は足し算で再現、割り算は引き算で再現していると思いますが。
carnage0216

2018/08/01 15:41 編集

ちなみに、プログラム変化前は +1.000000x1 -2.000000x2 +3.000000x3 -4.000000x4 = +5.000000 -2.000000x1 +5.000000x2 +8.000000x3 -3.000000x4 = +9.000000 +5.000000x1 +4.000000x2 +7.000000x3 +1.000000x4 = -1.000000 +9.000000x1 +7.000000x2 +3.000000x3 +5.000000x4 = +4.000000 x1 = 1.000000 x2 = 3.000000 x3 = -2.000000 x4 = -4.000000 0000.007秒かかりました プログラム変化後は +1.000000x1 -2.000000x2 +3.000000x3 -4.000000x4 = +5.000000 -2.000000x1 +5.000000x2 +8.000000x3 -3.000000x4 = +9.000000 +5.000000x1 +4.000000x2 +7.000000x3 +1.000000x4 = -1.000000 +9.000000x1 +7.000000x2 +3.000000x3 +5.000000x4 = +4.000000 x1 = 1.000000 x2 = 3.000000 x3 = -2.000000 x4 = -4.000000 0000.019秒かかりました とでました。やはり後者は遅いですね。
asm

2018/08/01 15:48

掛け算も遅いと言えば遅いんですが、 人間が筆算で答えを出す時を思い出してもらうと分かるかと思いますが 掛け算は桁ごとに並列に演算して最後に加算すりゃいいんですが 割り算は上位桁の答えが正確に分からないと下位桁の答えがでないので一桁ずつ答えを出す必要があるんです
asm

2018/08/01 15:53

なお、最近のCPUが使ってるような最新のアルゴリズムまでは知らんので ベンチマークによる計測が根拠だったりする程度の知識です
carnage0216

2018/08/01 16:24

解答ありがとうございます。確かにそう考えると納得できます。 質問に戻るのですが、asmさんはどうのような過程を経て元のプログラムから載せていただいたプログラムを作ったのでしょうか? 過程が知りたいです。そして参考にしたいです。 どうかよろしくお願いいたします。
carnage0216

2018/08/01 16:41 編集

ヘンテコなことばかりしていしまい申し訳ないのですが、 for (i = N - 1; i >= 0; i--) { a[i][N] /= a[i][i]; for (j = i + 1; j < N; j++) a[i][N] -= a[i][j] * a[j][N] / a[i][i]; } のa[i][N]をdに変えて以下のようにしたら結果が異なるものが得られました。なぜ置き換えただけで間違った結果が出たのか疑問です。 for (i = N - 1; i >= 0; i--) { d = a[i][N]/ a[i][i]; for (j = i + 1; j < N; j++) a[i][N] = d- a[i][j] * a[j][N] / a[i][i]; } どうかよろしくお願いいたします。置き換えるのはあまりよくないのかもしれません。正しい値が入らないかもしれないので...。
rubato6809

2018/08/02 00:06 編集

呆 ・インデントを正しくつけよう。 ・{}を省略するな。 と言われたばかりではないか。元プログラムが{}を省略していたなら、まず{}を補うことから始めたらどうか。 for文をフローチャートに描き直せるかどうか。 つまり、計算の順序を理解できるか、それによって変数・配列の値がいつどこで変化するか、認識できるか。この間、おもちゃプログラムを紹介したけど、あれもひとつの手。 「-=」「*=」「/=」などを複合代入演算子とか呼ぶけど、ただの代入演算子「=」だけに書き直したらどうなるか・・・等など基本から見直す必要があるね。 随分前にも言ったけど、身近に相談できる相手を見つけるのが先決。そういう人がいればヘンテコな方向に暴走せずに済みそうなものだが。普通より理解が遅い、それでも諦められないのなら、金を払ってでもそういう人を見つけることだね。ここで応えてくれる人はタダ働き、ネット経由でかゆい所に手が届かないだろうし、時間はかかるしで、君にとって効率が良いとは思えない。
carnage0216

2018/08/02 06:07

本当に申し訳ありません。二度とこのような質問はしません。 そうですね。プログラミングの家庭教師の見つけたほうが良いかもしれません。 本当に申し訳ありませんでした。
carnage0216

2018/08/02 08:56

なるほど、j=1の時の計算がないため正しい計算が出ないのですね。
carnage0216

2018/08/02 09:20

for (i = N - 1; i >= 0; i--) { d= a[i][N] / a[i][i]; for (j = i + 1; j < N; j++) a[i][N]=d - a[i][j] * a[j][N] / a[i][i]; } のプログラムの計算の過程を考えると 1週目、 d=a[2][3] / a[2][2] a[2][3]=a[2][3] / a[2][2] - a[2][3] * a[3][3] / a[2][2] 2週目、 d= a[1][3] / a[1][1] a[1][3]= a[1][3] / a[1][1] - a[1][3] * a[3][3] / a[1][1] 3週目、 d= a[0][3] / a[0][0] a[0][3]= a[0][3] / a[0][0] - a[0][3] * a[3][3] / a[0][0] となると思うのですがあっているでしょうか?
asm

2018/08/02 09:29

誤っている式を深く考察する意味はあるのでしょうか? j<Nから、jはN以上になるとforを脱出しますので N=3のとき それぞれ a[2][3] = a[2][3] /* 変化なし */ a[1][3] = a[1][3]/a[1][1] - a[1][2]*a[2][3]/a[1][1] a[0][3] = a[1][3]/a[0][0] - a[0][2] *a[2][3]/a[0][0] ですね
rubato6809

2018/08/02 09:48

その二重ループの一番最初の割り算、 一方は a[3][N] /= a[3][3]; を実行するので、a[3][N]の値は変化する。 ところがもう一方は d = a[3][N] / a[3][3]; を計算した後、dの値を使わず捨ててしまい、d = a[2][N] / a[2][2]; を計算するので、a[3][N] は変化しない。 もうこれだけで違うことがわかる。 プログラムの字面だけで書き換えようとすると判断を誤る、故に地道に・具体的にトレースすることが大事、という教訓でしょう。
carnage0216

2018/08/02 10:28

解答ありがとうございます。 私は二重ループを完璧に理解できていませんでした。 for (i = N - 1; i >= 0; i--) { d= a[i][N] / a[i][i]; for (j = i + 1; j < N; j++) a[i][N]=d - a[i][j] * a[j][N] / a[i][i]; } のi=1の時、d= a[1][3] / a[1][1]; の後、このdがa[i][N]=d - a[i][j] * a[j][N] / a[i][i]に代入されると思っていました。
carnage0216

2018/08/02 12:35

dに置き換えての計算を一から作ります。
carnage0216

2018/08/05 07:03

ちなみに、どうにかして、 d = a[i][N]/ a[i][i]のままで正しい答えを出すプログラムを書くにはどうしたら良いでしょうか? for (i = N - 1; i >= 0; i--) { d = a[i][N]/ a[i][i]; a[i][N]=d ;    for (j = i + 1; j < N; j++)      a[i][N] =     a[i][N] - a[i][j] * a[j][N] / a[i][i]; }
asm

2018/08/05 07:41

まず、d = a[i][N] / a[i][i]と一時的に置くことによって なんらかの恩恵を受けるというのが間違った考察です
rubato6809

2018/08/05 08:09

> どうにかして、d = a[i][N]/ a[i][i]のままで正しい答えを出すプログラムを書くにはどうしたら良い? むしろ、なぜそんな変更をしなければならないのか?変更する目的はどこにあるのか? そちらの方が問題です。特段コードが読みやすくなるわけでもなければ、性能向上が見込めるわけでもないのに、正しく動くコードをぶち壊すリスクを負ってまで書き直す必要性は無いと思います。
rubato6809

2018/08/05 08:27 編集

どうにか書き直せないか、の前に、結果が異なる理由は理解できたのでしょうか? それとも、理由は理解できないけど、結果が異なるという事実は受け入れざるをえない(ので、どう書き換えれば良いのか?)ということですか? まずそこをコメントしてほしいですねえ。
carnage0216

2018/08/05 09:16

理解はできました。(もしかしたらちゃんとした理解はできていないかもしれません。自信はないです。) ただ、自分なりのこだわりで皆様を振り回してしまっていることは事実です。 そこまでしてd = a[i][N]/ a[i][i]にしたいわけではありません。 >>まず、d = a[i][N] / a[i][i]と一時的に置くことによって なんらかの恩恵を受けるというのが間違った考察です おっしゃる通りだと思いますのでこのまま載せて頂いたコードを真似させて頂きます。 どうもありがとうございます。
rubato6809

2018/08/05 15:04

2つの結果が違うのは当然です。プログラムの動作に明確な違いが見つかりますから。配列とforループを学んだ人なら理解すると思います。ところがこれだけ手間をかけて、理解できていないかもしれない・自信はない、、、というか理解できた様子が窺えません。未だに「d=...」で計算できないのかという未練を押しとどめようとしているようです。 いつだったか、向いてないかもしれないと自問してたことがありましたね。君は向いてません。
carnage0216

2018/08/08 14:39 編集

rubato6809 rubato6809さん、解答ありがとうございます。 >>君は向いてません。 はっきり言っていただき本当にありがとうございます。僕は曖昧なものが理解できないので大変助かります。自分なりにやっと理解できてきましたが、やはり時間をこれだけかけてやっと理解できたところを考えると向いてないですね。追々このサイトからいなくなるかもしれませんが気を悪くしないでください。 >>未だに「d=...」で計算できないのかという未練を押しとどめようとしているようです。 それに関しては自分なりに完結させました。 >>ところがこれだけ手間をかけて、理解できていないかもしれない・自信はない、、、というか理解できた様子が窺えません。 やっと理解は出来ました。しかし、自信は持てません。ここまで助けてもらって何を自信にしていいかわかりません。一人じゃ何もできない僕に自信を持つこと自体無理な話です。
carnage0216

2018/08/08 15:44 編集

d = a[i][N]/ a[i][i];でプログラムを作っているわけではありませんが、 #include <iostream> #include <stdio.h> // 元の数定義 #define N 3 // 3 using namespace std; //double a[N][N+1]; // 係数 static double a[N][N + 1] = { { 1.0, 2.0, 8.0, 4.0}, { 4.0, 3.0, 2.0, 12.0}, { 1.0, 4.0, 2.0, 4.0}, }; int main() { double d; int i=2; d = a[i][N] / a[i][i]; //d = a[2][3] / a[2][2]   a[i][N] = d - a[i][i] * a[i][N] / a[i][i]; printf("%f\n", a[i][N]); a[i][N] = a[i][N] / a[i][i]; //d = a[2][3] / a[2][2]      a[i][N] = a[i][N] - a[i][i] * a[i][N] / a[i][i]; printf("%f\n", a[i][N]); return 0; } 実行結果 -2.000000 0.000000 より何が理解できていなかったのかを知りました。置き換えることで計算する過程が異なりことを知りました。 私はプログラミングに向いていないかもしれませんが、食いついて学習していきrubato6809さんに認めてもらえるよう努力します。私は頭は悪い落ちこぼれですが、粘着質で諦めは悪い方なので今後ともよろしくお願いいたします。
guest

0

計算順序が違うから。
4数をp,q,r,sとすると、
前者は(p-qr)/sを、
後者はp-(q
r/s)を計算している。
後者はq*r/sが減算より先に処理されるため、前者と違う計算を行うことになる。

投稿2018/08/01 14:30

swordone

総合スコア20651

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

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

carnage0216

2018/08/01 15:04

わかりやすい回答どうもありがとうございます。 なるほど、ではp/s-q*r/sと出来るように組んでみます。
guest

0

別に{}を付いてないので式を一つにまとめても同じ結果が出ると思っていました。

{ } が付いていないので、1つにまとめたら違う結果が出るに決まっているじゃないですか。

d -= a[i][j] * a[j][N];jのループの中で実行されて、a[i][N] = d / a[i][i];jのループが終わってから実行されます。

投稿2018/08/01 14:30

otn

総合スコア84538

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

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

carnage0216

2018/08/01 15:04

解答どうもありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問