ページ 11

saya

Posted: 2011年11月22日(火) 15:06
by 関数を使って小さい順に並び替える
3行3列の行列を関数を使って小さい順に並び替えるプログラムなんですけど
よくわからないです

授業で習ったところは
for文 if文 scanf printf dowhile
などです

ポインターも独自に使ってやらないとダメみたいです


コード:

#include<stdio.h>
void swap(int *x,int *y);
int main(void)
{
  int a[3];
  int i,s;
  printf("数値を入力してください\n");
  for(i=0;i<3;i++)
    {
      scanf("%d",&a[i]);
  
    }

    { for(i=0;i<3;i++)
{
  for(i=0;i<2;i++){
    for(i=0;i<2;i++){  
      swap(*a[i],*a[i+1]);
    }
    }
for(i=0;i<3;i++)
    {
      printf("%d   ",a[i]);
    }
  printf("\n");
  return 0;
}

void swap(int *x,int *y)
{
  double tmp;
  int i,flag;
  do
    {
      flag=0;
      for(i=0;i<1;i++)
{
  if(*x>*y)
    {
      tmp=*x;
      *x=*y;
      *y=tmp;
    }
      
}
    }
  while(flag==1);
}

動きもしません
ポインターとかいろいろ足りないと思うんですけど
どうしたらいいでしょうか?

Re: saya

Posted: 2011年11月22日(火) 15:24
by beatle
{と}の数がそもそも対応しておらず、プログラムとして成り立っていません。
動かないというのではなく、コンパイルさえできません。

「ポインターとかいろいろ足りない」という話をする前に、とりあえずコンパイルができる
ようなソースコードにしてください。
このような変なソースコードになってしまう要因としては、インデント(字下げ)ができていない
ということがあると思います。
インデントをしっかり直したソースコードを提示します。

コード:

#include<stdio.h>
void swap(int *x,int *y);
int main(void)
{
    int a[3];
    int i,s;
    printf("数値を入力してください\n");
    for(i=0; i<3; i++)
    {
        scanf("%d",&a[i]);
    }

    {
        for(i=0; i<3; i++)
        {
            for(i=0; i<2; i++)
            {
                for(i=0; i<2; i++)
                {
                    swap(*a[i],*a[i+1]);
                }
            }
            for(i=0; i<3; i++)
            {
                printf("%d   ",a[i]);
            }
            printf("\n");
            return 0;
        }

void swap(int *x,int *y)
{
    double tmp;
    int i,flag;
    do
    {
        flag=0;
        for(i=0; i<1; i++)
        {
            if(*x>*y)
            {
                tmp=*x;
                *x=*y;
                *y=tmp;
            }

        }
    }
    while(flag==1);
}
swap関数はとりあえずどの{に対しても対応する}があり、関数定義になってますが、main関数に至っては
尻切れトンボのようになっています。

Re: saya

Posted: 2011年11月22日(火) 15:32
by saya

コード:

#include<stdio.h>
void swap(int *x,int *y);
int main(void)
{
    int a[3];
    int i,s;
    printf("数値を入力してください\n");
    for(i=0; i<3; i++)
    {
        scanf("%d",&a[i]);
    }
 
            for(i=0; i<2; i++)
            {
                for(i=0; i<2; i++)
                {
                    swap(*a[i],*a[i+1]);
                }
            }
            for(i=0; i<3; i++)
            {
                printf("%d   ",a[i]);
            }
            printf("\n");
            return 0;
        }
 
void swap(int *x,int *y)
{
    double tmp;
    int i,flag;
    do
    {
        flag=0;
        for(i=0; i<1; i++)
        {
            if(*x>*y)
            {
                tmp=*x;
                *x=*y;
                *y=tmp;
            }
 
        }
    }
    while(flag==1);
}
直してみました

Re: saya

Posted: 2011年11月22日(火) 15:53
by beatle
パッと見同じだったので驚きましたが、{と}の対応がバッチリ直ってますね。
ついでにインデントも直せば完璧でした。

さて、ポイントを説明するために3行のソースコードを書いてみました。

コード:

int a[3];
int b;
b = a[0];
3つの要素を持つint型の配列aと、int型の変数bです。bにaの0番目を代入しています。
aと書いた時、これは

コード:

*(a + i);
と同じ意味になります。
まず「a」とだけ書くと、それは「配列の先頭の位置」を表します。
そこに「i番目」を意味する整数「i」を足すと、(a + i)全体では「配列のi番目の位置」を表します。

一般に「HOGE」が「FOOの位置」を表すときに「*HOGE」と書くと、「FOOの位置にある」を表します。
そこで、「HOGE」=「(a + i)」、「FOOの位置」=「配列のi番目の位置」と考えれば、
「*(a + i)」は「配列のi番目の位置にある値」を表すことが分かります。

まとめると、aは「配列のi番目の位置にある値」を表します。

「*」という記号はこのように、「何かの位置(=アドレス)」から「その位置にある値」を
取り出す演算子です。
「*何かの位置」のように使うわけです。

以上を踏まえた上で

コード:

swap(*a[i],*a[i+1]);
を見ると、これはおかしいことに気がつくわけです。
a
=> *(a + i)
=> *(配列aのi番目の位置)
=> 配列aのi番目にある値
ですから、
*a
=> *( *(a + i) )
=> *( *(配列aのi番目の位置) )
=> *( 配列aのi番目にある値 )
=> コンパイルエラー
となってしまうのです。

Re: saya

Posted: 2011年11月22日(火) 23:15
by box
saya さんが書きました: 3行3列の行列
これはどこに出てきているんでしょうか?
また、入力例と出力例とを挙げてください。

配列の中に入っている3つの数字を配び替えるプログラム

Posted: 2011年11月23日(水) 10:53
by saya
配列の中に入っている3つの数字を配び替えるプログラムでした
とりあえず
ポインターを消してみました


コード:

#include<stdio.h>
void swap(int *x,int *y);
int main(void)
{
    int a[3];
    int i,s;
    printf("数値を入力してください\n");
    for(i=0; i<3; i++)
    {
        scanf("%d",&a[i]);
    }
 
            for(i=0; i<2; i++)
            {
                for(i=0; i<2; i++)
                {
                    swap(a[i],a[i+1]);
                }
            }
            for(i=0; i<3; i++)
            {
                printf("%d   ",a[i]);
            }
            printf("\n");
            return 0;
 }
 
void swap(int *x,int *y)
{
    double tmp;
    int i,flag;
    do
    {
        flag=0;
        for(i=0; i<1; i++)
        {
            if(*x>*y)
            {
                tmp=*x;
                *x=*y;
                *y=tmp;
            }
 
        }
    }
    while(flag==1);
}

Re: saya

Posted: 2011年11月23日(水) 11:05
by beatle
アスタリスク「*」を削除したのは分かりましたが、結局直ったのですか?
それとも、まだ不具合がありますか?
saya さんが書きました:とりあえず
ポインターを消してみました
と言われても、「消したのは分かりました。それで?」っていう感じですね。

Re: saya

Posted: 2011年11月23日(水) 11:10
by saya
ソートされないです

Re: saya

Posted: 2011年11月23日(水) 11:38
by beatle
恐らくコンパイルするとワーニングまたはエラーが出ると思います。
具体的には、17行目の

コード:

swap(a[i],a[i+1]);
がダメなのです。swapは

コード:

void swap(int *x,int *y);
と宣言されています。この意味するところは
「swap関数は2つの引数x,yを取ります。xはint型変数の位置を受け取り、yはint型変数の位置を受け取ります」
x, yともに、int型変数の「位置」を受け取るわけです。
「int x」だったらint型の値そのものを受け取るのですが、「int *x」ですから、「あるint型変数の位置」を受け取るのです。

前回の話を思い出すと、「aは配列の先頭位置」を表し、「aは配列のi番目の位置にある値」を表すのでした。
swapは「位置」を受け取りたいのに「その位置にある値」を渡してしまっているのです。

例えるとこんな感じでしょうか。
----
あるデパートに行きました。ジーンズ売り場に行きたかったので、ジーンズ売り場の場所を店員に聞きました。
私「ジーンズ売り場の場所はどこですか?場所を書いたメモをくれませんか?」
店員「はい、これがジーンズです。」
店員はなんと、ジーンズそのものを渡してきました。
----
店員は、場所を書いたメモが欲しいと言われているのに、ジーンズそのものを渡しました。
これではダメです。(人間社会だったら許されるかも?でも、コンピュータの世界ではダメです)

swap関数には、何かの位置を渡す必要がありますから、

コード:

swap(a + i, a + (i + j));
と書くのが正解なのです。
(前回の復習で、a + iは「配列aのi番目の位置」を表すのでした)

Re: saya

Posted: 2011年11月23日(水) 12:25
by saya

コード:

#include<stdio.h>
void swap(int *x,int *y);
int main(void)
{
    int a[3];
    int i,s,j;
    printf("数値を入力してください\n");
    for(i=0; i<3; i++)
    {
        scanf("%d",&a[i]);
    }
 
            for(i=0; i<2; i++)
            {
                for(j=0; j<2; j++)
                {
                    swap(a+i,a+(i+j));
                }
            }
            for(i=0; i<3; i++)
            {
                printf("%d   ",a[i]);
            }
            printf("\n");
            return 0;
 }
 
void swap(int *x,int *y)
{
    double tmp;
    int i,flag;
    do
    {
        flag=0;
        for(i=0; i<1; i++)
        {
            if(*x>*y)
            {
                tmp=*x;
                *x=*y;
                *y=tmp;
            }
 
        }
    }
    while(flag==1);
}
よくわかったんですけど
まだ並び変わらなかったです
一番目と二番目の配列がの中身が入れ替わったんですけど
二番目と三番目が入れ替わらなかったです

バブルソートのところは繰り返す必要がないようにも思われてるんですが
直しても変わらなかったので下に戻しました

Re: saya

Posted: 2011年11月23日(水) 12:37
by みけCAT
swap関数の中で絶対にflag==1にはなりませんが、正常ですか?
for(i=0;i<1;i++)も実質1回しか実行されず、意味がないと思います。

あ。
saya さんが書きました:バブルソートのところは繰り返す必要がないようにも思われてるんですが
直しても変わらなかったので下に戻しました
ということか。

Re: saya

Posted: 2011年11月23日(水) 13:02
by beatle
まだインデントがよく分かっていらっしゃらないようですから、一度mixcpp/投稿前チェックリストの「チェック3 : インデントを揃えよう」を御覧ください。

コード:

            for(i=0; i<2; i++)
            {
                for(j=0; j<2; j++)
                {
                    swap(a+i,a+(i+j));
                }
            }
さて、このコードの繰り返しを展開してみましょうか。展開っていうのは、繰り返しを使わないで同じ処理をするプログラムに変換することです。
いざ展開してみると

コード:

swap(a+0,a+(0+0));
swap(a+0,a+(0+1));
swap(a+1,a+(1+0));
swap(a+1,a+(1+1));
こうなります。これを整理すると

コード:

swap(a+0,a+0);
swap(a+0,a+1);
swap(a+1,a+1);
swap(a+1,a+2);
となります。swapに同じものを渡しても入れ替わりませんから、4つのうち2つのswap呼び出しは無駄ですね(まあ、無駄なだけで害はありませんが)。
ですから実質的には、2重for文は以下の2行と同じ動作です。

コード:

swap(a+0,a+1);
swap(a+1,a+2);
試しに、a = { 3, 2, 1 } の場合を考えてみましょう。希望する出力は恐らく { 1, 2, 3 } ですよね?
swap(a+0, a+1); => { 2, 3, 1 }
swap(a+1, a+2); => { 2, 1, 3 }
となって、最終的に { 2, 1, 3 } になってしまいます。

Re: saya

Posted: 2011年11月23日(水) 13:17
by non
flagは何のために使うのか、先生の指示なら、説明があったのではないですか?
まずは、flagを使わず、swapの中の繰り返しを止めた方がいいですね。

それに、いつのまにかバブルソート(隣接交換法)でなくなってますよ。

Re: saya

Posted: 2011年11月23日(水) 16:02
by box
saya さんが書きました:

コード:

                for(j=0; j<2; j++)
                {
                    swap(a+i,a+(i+j));
                }
ここのあたりをもっと吟味される方がよいと思います。
コードを書く前に、数字を書いた3枚の紙を用意するなどして、
手で並べ替えるときにどういう手順を踏むか、よく考えられた方がよいと思います。
saya さんが書きました:

コード:

void swap(int *x,int *y)
{
    double tmp;
int型の数値を入れ替える際に用いるワークがどうしてdouble型なのか、
このあたりもよく吟味される方がよいと思います。

Re: saya

Posted: 2011年11月23日(水) 19:39
by saya

コード:

#include<stdio.h>
void swap(int *x,int *y);
int main(void)
{
    int a[3];
    int i,s,j,k;
    printf("数値を入力してください\n");
    for(i=0; i<3; i++)
    {
        scanf("%d",&a[i]);
    }
	for(k=0;k<2;k++)
	{
            for(i=0; i<2; i++)
            {
                for(j=0; j<2; j++)
                {
                    swap(a+i,a+(i+j));
                }
            }
	}
            for(i=0; i<3; i++)
            {
                printf("%d   ",a[i]);
            }
            printf("\n");
            return 0;
 }
 
void swap(int *x,int *y)
{
    double tmp;
    int i,flag;
    do
    {
        flag=0;
        for(i=0; i<1; i++)
        {
            if(*x>*y)
            {
                tmp=*x;
                *x=*y;
                *y=tmp;
           flag=1;
			}
 
        }
    }
    while(flag==1);
}

やっとできました
理解したつもりです

Re: saya

Posted: 2011年11月23日(水) 20:48
by non
とても理解できているとは思えないのですが。
私が、先生なら返します。
例えばデータが5個になった時、どこの数を変えますか?

Re: saya

Posted: 2011年11月23日(水) 23:42
by saya

コード:

#include<stdio.h>
void swap(int *x,int *y);
int main(void)
{
    int a[5];
    int i,j,k;
    printf("数値を入力してください\n");
    for(i=0; i<5; i++)
    {
        scanf("%d",&a[i]);
    }
	for(k=0;k<4;k++)
	{

                for(j=0; j<4; j++)
                {
                    swap(a+j,a+(j+1));
                }
	}
            for(i=0; i<5; i++)
            {
                printf("%d   ",a[i]);
            }
            printf("\n");
            return 0;
 }
 
void swap(int *x,int *y)
{
    double tmp;
    int i,flag;
    do
    {
        flag=0;
        for(i=0; i<1; i++)
        {
            if(*x>*y)
            {
                tmp=*x;
                *x=*y;
                *y=tmp;
           flag=1;
			}
 
        }
    }
    while(flag==1);
}


たぶん5個のときはこれでいいです

Re: saya

Posted: 2011年11月24日(木) 09:29
by non
12行から19行の部分が3重ループから2重ループになりましたし、隣接交換法の形になったようです。
しかし、既に指摘されているように、
32行目のdouble tempの件、
swap関数の中の2つのループの必要性とflagの意味がわかりません。

まず、すっきりした無駄のない形に書くと、次のようになります。

コード:

#include<stdio.h>
#define N 5
void swap(int *x,int *y);
int main(void)
{
	int a[N];
	int i,j,k;
	printf("数値を入力してください\n");
	for(i=0; i<N; i++)
	{
		scanf("%d",&a[i]);
	}
	for(k=0;k<N-1;k++)
	{
		for(j=0; j<N-1; j++)
		{
			swap(a+j,a+j+1);
		}
	}
	for(i=0; i<N; i++)
	{
		printf("%d   ",a[i]);
	}
	printf("\n");
	return 0;
}

void swap(int *x,int *y)
{
	int tmp;
	if(*x>*y){
		tmp=*x;
		*x=*y;
		*y=tmp;
	}
}
次にflagですが、内側のループで一度も交換しなかったとき、すでに整列していることを示し、以降の処理をやめるために使うのが一般的です。先生からそのような話はなかったのでしょうか?

Re: saya

Posted: 2011年11月24日(木) 09:50
by saya
その通りですね
プログラミングの授業自体がずっと演習なので
説明はほとんど無い状態で進めています