ページ 11

独習Cの練習問題5_1_1のバブルソートプログラムについて

Posted: 2013年1月01日(火) 16:57
by パクパク
[1] 質問文

 [1.1] 自分が今行いたい事は何か

明けましておめでとうございます。
また、いつも返信して頂き、皆さんありがとうございます。
さっそく質問ですが、

1.独習Cの練習問題5_1_1のバブルソートプログラムについて。
2.わたくしがコードを書く際に考えている事をコメントにしてみました。
 出来れば、もっとこういう事を考えて書いたら良い、というアドバイスを頂けないでしょうか。


 [1.2] どのように取り組んだか(プログラムコードがある場合記載)

コード:

/*

(2013/1/1)Lesson5_1_3.c

<問題>

1.ユーザーの指定した数、float型の数値を受け取り、それらをソートする。
2.ソートのアルゴリズムにはバブルソートを使用する。

<考え方>

1.ユーザーの指定した数、float型の数値を受け取る
→ユーザーの指定した数を格納するため、int型整数countを定義する。
→float型の配列を定義する。変数をnum_fとし配列要素を[100]とする。
→式には整数値か定数のみが使えるので、float型配列格納用の変数tを定義する。

2.ソートのアルゴリズムにはバブルソートを使用する。
→比較用にint型の変数aとbを定義する(上記と同様の理由により、配列要素用の変数の為、int型とする)。

*/

#include <stdio.h>//scanf()関数を使用する為、<conio.h>は使用しない。

int main(void)
{
	float num_f[100], t;//float型の配列と変数を同時に定義する。
	int a, b, count;//int型変数a,b,countを定義する
		
	printf("float型の数値を整列します。\n");//どのような目的のプログラムかユーザーに知らせる。
	printf("入力する浮動小数点数を100までの数で指定してください: ");//ユーザーに条件を知らせる。
	scanf("%d" , &count);

	/*初期設定部:配列要素は0から始まるので、aに0を代入する。
	  条件設定部:上記と同様の理由により、count未満とする
	  インクリメント部:変数countが使われた後、インクリメントする。*/
	for(a = 0; a < count; a++){
		printf("%d 番目の浮動小数点数を入力して下さい: " , a + 1);
		scanf("%f" , &num_f[a]);//配列num_fの配列要素[a]番目に数値を格納する。
	}

	/*バブルソートを使用する。
	  初期設定部:
	  条件判定部:
	  インクリメント部:インクリメントした後、変数が使われる。*/
	for(a = 1; a < count; ++a)
		
		/*初期設定部:
	      条件判定部:
	      インクリメント部:デクリメントした後、変数が使われる。*/
		for(b = count - 1; b >= a; --b){//0番目の配列要素を呼び出す都合、count - 1とする?
			//隣接する要素を比較する
			if(num_f[b - 1] > num_f[b]){
				//要素を交換する
				t = num_f[b - 1];//float型の変数fに配列num_fの配列要素[b - 1]番を代入
				num_f[b -1] = num_f[b];
				num_f[b] = t;
			}
		}

		for(a = 0; a < count; a++)
		printf("%f\n" , num_f[a]);

	return 0;
}
 
 [1.4] 今何がわからないのか、知りたいのか

<疑問点>
1.45行目と50行目がわかりません。
特に45行目の初期設定部でaに1を代入する理由がわかりません。0では駄目なのでしょうか?
2.配列と変数は同時に定義しない方が良いでしょうか?
3.定義と同時に全ての変数を初期化した方が良いでしょうか?

[2] 環境  
 [2.1] OS : Windows
 [2.2] コンパイラ名 : VC++ 2010 Express

Re: 独習Cの練習問題5_1_1のバブルソートプログラムについて

Posted: 2013年1月01日(火) 17:21
by softya(ソフト屋)
>1.45行目と50行目がわかりません。
>特に45行目の初期設定部でaに1を代入する理由がわかりません。0では駄目なのでしょうか?

1に関しては自分で絵や図を書くと良いと思います。
例えば、5個の要素をバブルソートするなら四角を5個書きます。
これが配列を意味しますので、それぞれに0から番号をつけてやります。
次に別の小さな紙に5つの適当な値を書いてそれぞれの四角の上に置きます。
これで擬似的に配列に値を代入したという状況ですね。
あとはaやbやcountの箱を書いた紙を用意します。※ これには値を書き入れるので消しやすいように余裕を空けましょう。
countはこの場合5で固定ですけどね。
さて、これで準備は完了です。

では、この状況で45行から58行のプログラムを擬似的に紙の上で実行してみましょう。
とりあえず、a=0から始めてみましょうか。
やってみた結果をお待ちしております。

>2.配列と変数は同時に定義しない方が良いでしょうか?

分かりやすい方法取れるのならばそれを選択べきです。

>3.定義と同時に全ての変数を初期化した方が良いでしょうか?

初期化しておくほうが無難です。

Re: 独習Cの練習問題5_1_1のバブルソートプログラムについて

Posted: 2013年1月01日(火) 18:08
by パクパク
いつも返信ありがとうございます。

1.所用があるので、この後、紙かgimp2上でやってみます。理解出来たら解決とさせて下さい。
2.なんとなく、別々に定義する方がわかりやすいような気がするので、そう致します。
3.初期化する癖をつけようと思います。

わかりやすい説明ありがとうございました。

Re: 独習Cの練習問題5_1_1のバブルソートプログラムについて

Posted: 2013年1月01日(火) 20:56
by box
パクパク さんが書きました: 2.配列と変数は同時に定義しない方が良いでしょうか?
パクパク さんが書きました:

コード:

	float num_f[100], t;//float型の配列と変数を同時に定義する。
ここの話ですか?だとすれば、どちらでもいいです。

コード:

    float num_f[100], t;
でも

コード:

    float num_f[100];
    float t;
でも、お好きなように書いてください。
少なくとも、「どちらかであるべきだ」というような話ではありません。
パクパク さんが書きました: 3.定義と同時に全ての変数を初期化した方が良いでしょうか?
初期化しておかねばならない変数があるとして、必ずしも定義と同時である必要はありません。
例えば、何かの合計値が入る変数として、float型のsumを考えます。
この変数には合計値が入るのですから、どこかで必ずゼロで初期化しておく必要がありますね。
その方法は

コード:

    float sum = 0;
でもいいし、

コード:

    float sum;
    /* 何か他の処理 */
    sum = 0;
でもかまいません。要するに、「使うよりも前に、どこかのタイミングで」初期化してあればいいです。
「絶対に定義と同時でなければならない」ということはありません。

なお、今回のコードで使っている変数については、定義と同時に初期化する必要性を特に感じません。
どうせ、for文の中とかで初期化していたり、代入文を使うことで値を入れたりしていますので、
定義と同時にゼロか何かに初期化することに意味があるとは思いません。

Re: 独習Cの練習問題5_1_1のバブルソートプログラムについて

Posted: 2013年1月02日(水) 11:50
by パクパク
画像

バブルソートを自分なりに表にしてみました。
しかしaとbの関係がわかりません。いつのまにbはaの数値を受け取ったのでしょうか?

すいません。混乱しています。

Re: 独習Cの練習問題5_1_1のバブルソートプログラムについて

Posted: 2013年1月02日(水) 12:04
by softya(ソフト屋)
画像をうまく参照できませんので、zipファイルにしてもらってよいでしょうか?
※ 私の環境のウィルス対策ソフトのカスペルスキーが原因かと思います。

Re: 独習Cの練習問題5_1_1のバブルソートプログラムについて

Posted: 2013年1月02日(水) 12:07
by softya(ソフト屋)
それと値の変化を調べるために a,b,countなどの変化直後にprintf表示するのも勉強のために必要なことですよ。
これはデバッグのテクニックとしても使えます。

Re: 独習Cの練習問題5_1_1のバブルソートプログラムについて

Posted: 2013年1月02日(水) 13:12
by パクパク
http://up.shinetworks.net/cgi-bin/snup/ ... 9.zip.html
上記の画像をzipにした物です。passは「4021」です。

Re: 独習Cの練習問題5_1_1のバブルソートプログラムについて

Posted: 2013年1月02日(水) 13:49
by softya(ソフト屋)
出かけるので他の方お願いします。
画像見ましたが、1回しか回してないので続けないと意味無いですね。
ループを全部回して書いてみてください。そうすればbの意味がわかります。
大変だったら、配列の要素数はまず3個でやってみましょう。
値は、11,6,3 あたりで。

Re: 独習Cの練習問題5_1_1のバブルソートプログラムについて

Posted: 2013年1月02日(水) 14:43
by へにっくす
横から失礼。
パクパク さんが書きました:しかしaとbの関係がわかりません。いつのまにbはaの数値を受け取ったのでしょうか?
配列の添え字にaは使ってないよね?
そこに気づけば、aの役割が分かりそうなものだけど。。

Re: 独習Cの練習問題5_1_1のバブルソートプログラムについて

Posted: 2013年1月02日(水) 16:06
by パクパク
もしかして上記の表の場合、配列要素自体は固定された、ただの入れ物で、そこに格納した数値をcountの定数5を利用してa番目とかb番目、[b-1]番目のように呼び出して比較したり、変数tを使って交換したりしているのでしょうか?

Re: 独習Cの練習問題5_1_1のバブルソートプログラムについて

Posted: 2013年1月02日(水) 16:23
by box
パクパク さんが書きました:もしかして上記の表の場合、配列要素自体は固定された、ただの入れ物で、そこに格納した数値をcountの定数5を利用してa番目とかb番目、[b-1]番目のように呼び出して比較したり、変数tを使って交換したりしているのでしょうか?
ご自分がコンピューターになったつもりで、1行ずつ実行していきながら、
各変数の値がどのように変化していくかを追いかけてみてはどうでしょうか。

Re: 独習Cの練習問題5_1_1のバブルソートプログラムについて

Posted: 2013年1月03日(木) 08:01
by パクパク
<1.>
45行目から58行目までのコードを一行ごとに追ってみました。
見づらいですが、コメントは都合上、コードの下に書いています。
aは、0と1どちらでも良いという事がわかったような気がしました。

紙でもやってみましたが、視覚的に理解出来たように感じました。

<2.>と<3.>についてはわかりやすく書くことを念頭において、臨機応変に書いてみようと思います。

しばらく待って、ツッコミが無ければ(あった方がうれしいのですが……)解決とさせてください。

皆さん、私の疑問に付き合って頂いてありがとうございます。

コード:

	

int num_f[100];
int count, a, b;

for(a = 0; a < count; ++a){
		for(b = count - 1; b >= a; --b){
			if(num_f[b - 1] > num_f[b]){
			t = num_f[b - 1];
			num_f[b - 1] = num_f[b];
			num_f[b] = t;
			}
		}
	}
	//a = 0の場合 
	
	//count = 3
	//num_f[0] = 7
	//num_f[1] = 5
	//num_f[2] = 3

	a = 0;
	//(6)a = 0
	a < count;
	//(6)真(0 < 3)
	b = count - 1;
	//(7)b = 2
	b >= a;
	//(7)真(2 >= 0)
	num_f[b - 1] > num_f[b];
	//(8)真(5 > 3)
	t = num_f[b - 1];
	//(9)t = 5
	num_f[b - 1] = num_f[b];
	//(10)num_f[1] = 3
	num_f[b] = t;
	//(11)num_f[2] = 5

	num_f[0] = 7, num_f[1] = 3, num_f[2] = 5;	

	--b;
	//(7)b = 1
	b >= a;
	//(7)真(1 >= 0)
	num_f[b - 1] > num_f[b];
	//(8)真(7 > 3)
	t = num_f[b - 1];
	//(9)t = 7
	num_f[b - 1] = num_f[b];
	//(10)num_f[0] = 3
	num_f[b] = t;
	//(11)num_f[1] = 7

	num_f[0] = 3, num_f[1] = 7, num_f[2] = 5;	
	
	--b;
	//(7)b = 0
	b >= a;
	//(7)偽(0 > 0)
	++a;
	//(6)a = 1
	a < count;
	//(6)真(1 < 3) 
	b = count - 1;
	//(7)b = 2
	b >= a;
	//(7)真(2 >= 1)
	num_f[b - 1] > num_f[b];
	//(8)真(7 > 5)
	t = num_f[b - 1];
	//(9)t = 7
	num_f[b - 1] = num_f[b];
	//(10)num_f[1] = 5
	num_f[b] = t;
	//(11)num_f[2] = 7

	num_f[0] = 3, num_f[1] = 5, num_f[2] = 7;	

	--b;
	//(7)b = 1
	b >= a;
	//(7)真(1 >= 1)
	num_f[b - 1] > num_f[b];
	//(8)偽(3 > 5)
	--b;
	//(7)b = 0
	b >= a;
	//(7)偽(0 >= 1)
	++a;
	//(6)a = 2
	a < count;
	//(6)真(2 < 3)
	b = count - 1;
	//(7)b = 2
	b >= a;
	//(7)真(2 >= 2)
	num_f[b - 1] > num_f[b];
	//(8)偽(5 > 7)
	--b;
	//(7)b = -1
	b >= a;
	//(7)偽(-1 >= 2)
	++a;
	//(6)a = 3
	a < count;
	//(6)偽(3 < 3)
	//表示へ

	//a = 1の場合

	//count = 3
	//num_f[0] = 7
	//num_f[1] = 5
	//num_f[2] = 3

	a = 1;
	//(6)a = 1 
	a < count;
	//(6)真(1 < 3)  
	b = count - 1;
	//(7)b = 2 
	b >= a;
	//(7)真(2 >= 1)
	num_f[b - 1] > num_f[b];
	//(8)真(5 > 3)
	t = num_f[b - 1];
	//(9)t = 5
	num_f[b - 1] = num_f[b];
	//(10)num_f[1] = 3
	num_f[b] = t;
	//(11)num_f[2] = 5

	num_f[0] = 7, num_f[1] = 3, num_f[2] = 5;

	--b;
	//(7)b = 1
	b >= a;
	//(7)真(1 >= 1)
	num_f[b - 1] = num_f[b];
	//(8)真(7 > 3) 
	t = num_f[b - 1];
	//(9)t = 7
	num_f[b] = t;
	//(10)num_f[0] = 3
	num_f[b] = t;
	//(11)num_f[1] = 7

	num_f[0] = 3, num_f[1] = 7, num_f[2] = 5;

	--b;
	//(7)b = 0
	b >= a;
	//(7)偽(0 >= 1)
	++a;
	//(6)a = 2
	a < count;
	//(6)真(2 < 3)
	b = count - 1;
	//(7)b = 2
	b >= a;
	//(7)真(2 >= 2)
	num_f[b - 1] = num_f[b];
	//(8)真(7 > 5)
	t = num_f[b - 1];
	//(9)t = 7
	num_f[b - 1] = num_f[b];
	//(10)num_f[1] = 5
	num_f[b] = t;
	//(11)num_f[2] = 7

	num_f[0] = 3, num_f[1] = 5, num_f[2] = 7;

	--b;
	//(7)b = -1
	b >= a;
	//(7)偽(-1 >= 1)
	++a;
	//(6)a = 3
	a < count;
	//(6)偽(3 < 3)
	//表示へ

Re: 独習Cの練習問題5_1_1のバブルソートプログラムについて

Posted: 2013年1月03日(木) 08:22
by box
パクパク さんが書きました: aは、0と1どちらでも良いという事がわかったような気がしました。
本当ですか?
パクパク さんが書きました:

コード:

for(a = 0; a < count; ++a){
		for(b = count - 1; b >= a; --b){
			if(num_f[b - 1] > num_f[b]){
aが0から始まるとします。
bはa(つまり0)まで減っていく可能性があります。
そうすると、上のif文で、num_f[-1]という、アクセスしてはいけない領域を
参照することにならないでしょうか。
パクパク さんが書きました:

コード:

	//(7)b = 0
	b >= a;
	//(7)偽(0 > 0)
このとき、aは0のままのはずです。
0 >= 0 は真ですから、偽と判断するのはまずくないでしょうか。

Re: 独習Cの練習問題5_1_1のバブルソートプログラムについて

Posted: 2013年1月03日(木) 08:48
by パクパク
ご教示ありがとうございます。

num_f[100]から、範囲は0~100なので-1にはアクセス出来きませんね。本にも、
「Cは添え字の範囲がチェックされません。範囲を超えた場合プログラムがクラッシュする可能性があります」、
と記載がありました。

コード:

    --b;
    //(7)b = 0
    b >= a;
    //(7)真(0 >= 0)
    t = num_f[b - 1];
    //(8)t = num_f[-1]となり、範囲を超えた添え字を呼び出す。 
(恥ずかしい……)

Re: 独習Cの練習問題5_1_1のバブルソートプログラムについて

Posted: 2013年1月03日(木) 09:06
by かずま
パクパク さんが書きました:num_f[100]から、範囲は0~100なので-1にはアクセス出来きませんね。
アクセス可能な範囲は 0~99 です。

さて、a を 0から始めることもできることを説明してみましょう。

たとえば、count が 5 のとき、比較は次の 4回
  num_f[3] と num_f[4] を比較
  num_f[2] と num_f[3] を比較
  num_f[1] と num_f[2] を比較
  num_f[0] と num_f[1] を比較

同じような処理を何度も書くのは大変なので、
ループ変数 b を用意して num_f[b-1] と num_f の比較とすれば
比較処理を書くのは 1度ですみます。

コード:

    for (b = 4; b >= 1; --b)
        if (num_f[b-1] > num_f[b]) {
すなわち、

コード:

    for (b = count - 1; b >= a; --b)
        if (num_f[b-1] > num_f[b]) {
ほら a は 1 ですよね。a が 0 だと、
num_f[-1] と num_f[0] を比較しようとしてしまいますよ。

この 4回の比較を行うと num_f[0] には最小値が入っているので、今度は、
  num_f[3] と num_f[4] を比較
  num_f[2] と num_f[3] を比較
  num_f[1] と num_f[2] を比較
の比較を行って、次に小さい値を num_f[1] に入れたい。
num_f[b-1] > num_f の b は、4 から 2 まで。
ほら a は 2 です。

このように a は、1 から count-1 まで繰り返して、ソートが終了します。

a の役割は、b の forループをどこで終わらせるかを示すことです。
隣り合う 2つの数値 num_f[b-1] と num_f の比較の繰り返しの最後の比較
num_f[a-1] > num_f[a] で、num_f[a-1] に最小値がはいるのですから、
a は 1以上でないといけません。

ところが、a を 0 から始めることもできます。それは

コード:

    for (b = count - 1; b > a; --b)
        if (num_f[b-1] > num_f[b]) {
とすることです。b >= a ではなく、b > a となっているところがミソです。
a を 0 から始めると、a が 1 から count-1 までのときよりループ回数が
1つ多くなりますが、b の forループの終了条件が b > a なので、最後の
a (= count-1) のときにループに入らなくなって、ソート結果は同じになります。

Re: 独習Cの練習問題5_1_1のバブルソートプログラムについて

Posted: 2013年1月03日(木) 09:29
by パクパク
アクセス可能な範囲は 0~99 です。
(恥ずかしい……)

返信ありがとうございます。
a の役割は、b の forループをどこで終わらせるかを示すことです。
疑問点も答えて頂きましたし、解決!とさせて下さい。
返信してくださった方、ツッコミを入れてくださった方、皆さん、ありがとうございました。