ページ 11

ポインタについて

Posted: 2007年2月02日(金) 12:54
by にゃんこ
また大学の課題なんですけど、

行列A=
a[1][1]=1 a[1][2]=1 a[1][3]=1 a[1][4]=1
a[2][1]=2 a[2][2]=4 a[2][3]=5 a[2][4]=3
a[3][1]=-1 a[3][2]=-3 a[3][3]=-6 a[3][4]=2
a[4][1]=5 a[4][2]=7 a[4][3]=8 a[4][4]=7
ベクトルb=
b[1]=4
b[2]=6
b[3]=2
b[4]=16

のとき、行列Aとベクトルbを配列とポインタを用いて作成し、さらにAx=bの拡大行列を表示して表示するプログラムを作成せよ。


上の課題、配列を用いて作成し、拡大行列を表示することは出来たんですがポインタをどのように使っていいのかが分かりません。ちなみに作成したプログラムは↓↓です。

#include <stdio.h>

#define MYMATRIX_SIZE 4


double matrix[MYMATRIX_SIZE][MYMATRIX_SIZE];
double vector[MYMATRIX_SIZE];

main()
{
int iRow,iCol;

/* A */
matrix[0][0] = 1;
matrix[0][1] = 1;
matrix[0][2] = 1;
matrix[0][3] = 1;
matrix[1][0] = 2;
matrix[1][1] = 4;
matrix[1][2] = 5;
matrix[1][3] = 3;
matrix[2][0] = -1;
matrix[2][1] = -3;
matrix[2][2] = -6;
matrix[2][3] = 2;
matrix[3][0] = 5;
matrix[3][1] = 7;
matrix[3][2] = 8;
matrix[3][3] = 7;

/* b */
vector[0] = 4;
vector[1] = 6;
vector[2] = 2;
vector[3] = 16;

/* 表示 */
printf("拡大行列 = \n");
for(iRow=0; iRow<MYMATRIX_SIZE; iRow++){
for(iCol=0; iCol<MYMATRIX_SIZE; iCol++){
printf("%8.4g",matrix[iRow][iCo[/url]);
if(iCol==3){
printf("%8.4g\n",vector[iRow]);
}
}
}
}


ポインタはすごく苦手でまったく分からないのでよろしくお願いします!!

Re:ポインタについて

Posted: 2007年2月02日(金) 16:20
by 管理人
ポインタはネットでもいいですので、勉強しましょう。

まず普通のポインタを勉強しましょう。

http://www5c.biglobe.ne.jp/~ecb/c/c00.html

ここの6章から7.4章まで勉強してください。

ポインタは難しいですから、概念を一通り学んだ後、自分で色々関数を作って実行してみてください。
其の中でわかってくるはずです。

一通りわかったら以下のプログラムを見てください。

2次元配列の掛け算を行うプログラムです。
#include <stdio.h>

void calc(int arr1[/url][2],int arr2[/url][2],int arr3[/url][2]){
	arr3[0][0]=arr1[0][0]*arr2[0][0]+arr1[0][1]*arr2[1][0];
	arr3[0][1]=arr1[0][0]*arr2[0][1]+arr1[0][1]*arr2[1][1];
	arr3[1][0]=arr1[1][0]*arr2[0][0]+arr1[1][1]*arr2[1][0];
	arr3[1][1]=arr1[1][0]*arr2[0][1]+arr1[1][1]*arr2[1][1];
	return ;
}

int main() {
	int arr1[2][2]={{1,2},{3,4}},arr2[2][2]={{5,6},{7,8}},i,j,arr3[2][2];
	calc(arr1,arr2,arr3);
	printf("%d %d\n",arr3[0][0],arr3[0][1]);
	printf("%d %d\n",arr3[1][0],arr3[1][1]);
	return 0;
}

例えば上記のプログラムでしたらarr1という2次元配列の先頭アドレスをcalcに渡します。
受け取ったアドレスをarr1[/url][2]で受け取ります。
最上位の要素のみ省略する事が出来ます。
受け取り方は
void calc(int (*arr1)[2],int (*arr2)[2],int (*arr3)[2]){
このように書いてもかまいません。

掛け算はうまくスマートにかけるように工夫してみてください。

頑張って☆

Re:ポインタについて

Posted: 2007年2月02日(金) 16:45
by box
> 行列Aとベクトルbを配列とポインタを用いて作成し

ポインタをどう使えばいいか、ですけど、
こんなことを想定されてるんでしょうか…。よくわからないです。

#include <stdio.h>

#define SIZE (4)

int main (void)
{
	double *mat_a[SIZE];
	double vec_b[/url] = { 4, 6, 2, 16 };
	double m[/url] = { 1, 1, 1, 1, 2, 4, 5, 3, -1, -3, -6, 2, 5, 7, 8, 7 };
	int i, j;
	
	for (i = j = 0; i < SIZE; i++, j += SIZE)
		mat_a = &m[j];
	for (i = 0; i < SIZE; i++) {
		for (j = 0; j < SIZE; j++) {
			printf("%8.4g", mat_a[j]);
			if (j == SIZE - 1)
				printf("%8.4g\n", vec_b);
		}
	}
	return 0;
}

Re:ポインタについて

Posted: 2007年2月02日(金) 16:55
by 管理人
ポインタを用いて配列を作製するという意味は確かによくわかりませんが、
計算をするのに単に関数へアドレスを渡して計算するということなのではないでしょうか?



アドレスと配列の関係は


int a[5];
と宣言したとし、aの先頭アドレスが10000番地だったとすると、int型変数は1つ4バイトですから

a[0]のアドレスは10000番地
a[1]のアドレスは10004番地
a[2]のアドレスは10008番地
a[3]のアドレスは10012番地
a[4]のアドレスは10016番地

ということに成ります。

問題をもう少し明確にしたほうがいいかもしれません。

Re:ポインタについて

Posted: 2007年2月02日(金) 17:10
by むつ
++ 質問の意図とは違うと判断した為消去します ++

Re:ポインタについて

Posted: 2007年2月03日(土) 03:51
by にゃんこ
分かりにくい質問で申し訳ありませんでした。。
私自身あまりポインタの使い方が分かっていないので、質問自体も分かりにくくなってしまったようです(*_*)
皆さんの意見を参考にもう少し自分で勉強してみようと思います。
本当にありがとうございました。

あと、ポインタとは離れますがこの問題の続きで

問2 問1で作成した拡大行列に前進消去を適用し、三角形式の行列にし、その結果を表示するプログラムを作成せよ。

問3 問2で作成した三角形式の行列に後退代入を適用して与えられた連立方程式を解き、その結果を表示するプログラムを作成せよ。

問2は自分でやってみたところ実行結果が以下のようになりました。
1   1    1    1    4
0   2    3    1    -2
0   0     -2    4       4
0      0        0     1        -2
それぞれの要素を
α[1][1] = 1  α[1][2] = 1  α[1][3] = 1  α[1][4] = 1  β[1] = 4
   ・      ・      ・     ・      ・
  ・      ・      ・     ・      ・
  ・      ・      ・     ・      ・
と表すと、連立方程式は
α[1][1]x[1] + α[1][2]x[2] + α[1][3]x[3] + α[1][4]x[4] = β[1]
        α[2][2]x[2] + α[2][3]x[3] + α[2][4]x[4] = β[2]
                α[3][3]x[3] + α[3][4]x[4] = β[3]
                        α[4][4]x[4] = β[4]

したがって

x[4] = β[4]/α[4][4]
x[3] = (β[3]-α[3][4]x[4])/α[3][3]
x[2] = (β[2]-α[2][3]x[3]-α[2][4]x[4])/α[2][2]
x[1] = (β[1]-α[1][2]x[2]-α[1][3]x[3]-α[1][4]x[4])/α[1][1]
と表せる。

上のxの値をそれぞれ求めていくプログラムなんですが、さすがに一つずつ求めて行ったのでは課題になりませんので……。↓↓↓のような感じのプログラムを作ってみたんですが、x[1]=3、x[2]=9、x[3]=-6、x[4]=-2となるはずの答えが合わなくてm(__)m
どこが間違っているんでしょうか??

Re:ポインタについて

Posted: 2007年2月03日(土) 03:55
by にゃんこ
すいません。プログラムは以下です。。

#include <stdio.h>

#define MYMATRIX_SIZE 5

double matrix[MYMATRIX_SIZE-1][MYMATRIX_SIZE];
double func(int i);

main()
{
int i;
double matrix[MYMATRIX_SIZE+3];

matrix[0][0] = 1;
matrix[0][1] = 1;
matrix[0][2] = 1;
matrix[0][3] = 1;
matrix[0][4] = 4;
matrix[1][0] = 0;
matrix[1][1] = 2;
matrix[1][2] = 3;
matrix[1][3] = 1;
matrix[1][4] = -2;
matrix[2][0] = 0;
matrix[2][1] = 0;
matrix[2][2] = -2;
matrix[2][3] = 4;
matrix[2][4] = 4;
matrix[3][0] = 0;
matrix[3][1] = 0;
matrix[3][2] = 0;
matrix[3][3] = 1;
matrix[3][4] = -2;

for(i=MYMATRIX_SIZE-1; i>=0; i--){
x = func(i) / matrix;
}

for(i=0; i<MYMATRIX_SIZE-1; i++){
printf("%15.8g\n", x);
}
}

double func(int i)
{
double result;

if(i>MYMATRIX_SIZE-1){
result = 0;
}else{
result = matrix[MYMATRIX_SIZE]
- matrix[MYMATRIX_SIZE-1] * func(i+1)
- matrix[MYMATRIX_SIZE-2] * func(i+2)
- matrix[MYMATRIX_SIZE-3] * func(i+3);
}
return result;
}

Re:ポインタについて

Posted: 2007年2月03日(土) 17:32
by しっぽ
完全に間違っているところ

main関数内の
double matrix[MYMATRIX_SIZE+3];
はサイズが合っていない。

double func(int i) 内ではグローバルな
double matrix[MYMATRIX_SIZE-1][MYMATRIX_SIZE];
が使用されてしまうため、main関数内での matrix
への代入が無意味になっている。

Re:ポインタについて

Posted: 2007年2月03日(土) 18:35
by box
> x[4] = β[4]/α[4][4]
> x[3] = (β[3]-α[3][4]x[4])/α[3][3]
> x[2] = (β[2]-α[2][3]x[3]-α[2][4]x[4])/α[2][2]
> x[1] = (β[1]-α[1][2]x[2]-α[1][3]x[3]-α[1][4]x[4])/α[1][1]

このとおりコードを書いたら、次のようになりました。

#include <stdio.h>

#define SIZE (4)

int main(void)
{
	double x[SIZE];
	double a[SIZE][SIZE] = {
		{ 1, 1,  1, 1 },
		{ 0, 2,  3, 1 },
		{ 0, 0, -2, 4 },
		{ 0, 0,  0, 1 },
	};
	double b[SIZE] = { 4, -2, 4, -2 };
	int i;
	
	for (i = SIZE - 1; i >= 0; i--) {
		double t;
		int j;
		for (t = 0, j = i + 1; j < SIZE; j++)
			t += a[j] * x[j];
		x = (b - t) / a;
	}
	for (i = 0; i < SIZE; i++)
		printf("x[%d]=%f\n", i, x);
	return 0;
}

Re:ポインタについて

Posted: 2007年2月03日(土) 19:00
by しっぽ
私もコードを投稿してみよ。
ただし前進消去とか後退代入は知らないので、目的に適合するかは分かりません。
#include <stdio.h> 

#define SIZE 4 

void print(double **matrix, int ny, int nx);
void lower_triangle(double **matrix, int ny, int nx);
void normalize(double **matrix, int ny, int nx);
void kronecker_delta(double **matrix, int ny, int nx);

int main(void) 
{ 
	int y;

	double buff[SIZE][SIZE + 1] = 
	{
		{ 1, 1, 1, 1, 4}, 
		{ 2, 4, 5, 3, 6},
		{-1,-3,-6, 2, 2}, 
		{ 5, 7, 8, 7,16}
	};

	double *matrix[SIZE];
	double *vector[SIZE];

	printf("初期状態\n");
	for(y = 0; y < SIZE; ++y){
		matrix[y] = &buff[y][0];
		vector[y] = &buff[y][SIZE];
	}
	print(matrix, SIZE, SIZE + 1);

	printf("小行列Aが下三角行列になるよう変形する\n");
	lower_triangle(matrix, SIZE, SIZE + 1);
	print(matrix, SIZE, SIZE + 1);

	printf("小行列Aの主対角が1になるよう変形する\n");
	normalize(matrix, SIZE, SIZE + 1);
	print(matrix, SIZE, SIZE + 1);

	printf("小行列Aがクロネッカー・デルタになるよう変形する\n");
	kronecker_delta(matrix, SIZE, SIZE + 1);
	print(matrix, SIZE, SIZE + 1);

	printf("解ベクトルb\n");
	print(vector, SIZE, 1);

	return 0;
} 

void print(double **matrix, int ny, int nx)
{
	int x, y;
	for(y = 0; y < ny; ++y){
		for(x = 0; x < nx; ++x){
			printf("%6.4g ", matrix[y][x]);
		}
		printf("\n");
	}
	printf("\n");
}

void lower_triangle(double **matrix, int ny, int nx)
{
	int x, y, j;
	for(y = 0; y < ny - 1; ++y){
		for(j = y + 1; j < ny; ++j){
			double d = matrix[j][y] / matrix[y][y];
			for(x = y; x < nx; ++x){
				matrix[j][x] -= matrix[y][x] * d;
			}
		}
	} 
}

void normalize(double **matrix, int ny, int nx)
{
	int x, y;
	for(y = 0; y < ny; ++y){
		double d = matrix[y][y];
		for(x = 0; x < nx; ++x){
			matrix[y][x] /= d;
		}
	}
}

void kronecker_delta(double **matrix, int ny, int nx)
{
	int y;
	for(y = ny - 1; y > 0; --y){
		double d = matrix[y][nx - 1];
		int j;
		for(j = 0; j < y; ++j){
			matrix[j][nx - 1] -= matrix[j][y] * d;
			matrix[j][y] = 0.0;
		}
	} 
}

Re:ポインタについて

Posted: 2007年2月04日(日) 01:28
by にゃんこ
boxさん、しっぽさん、ありがとうございます。
えぇと、たぶんboxさんのプログラムが一番課題の内容に近いと思うんですけど、
行列をaとbに分けていらっしゃいますが、それを分けずに一つの配列として扱い答えを出すことは可能でしょうか??

Re:ポインタについて

Posted: 2007年2月04日(日) 08:15
by box
> 行列をaとbに分けていらっしゃいますが、それを分けずに一つの配列として扱い答えを出すことは可能でしょうか??

もちろんできます。
・aの定義を1列増やす
・bの定義を削除する
・計算式でbを使っている箇所を、aの増やした列を使うように修正する

Re:ポインタについて

Posted: 2007年2月07日(水) 02:39
by にゃんこ
わかりました。では、そのように変更して作ってみます(*^_^*)
本当にありがとうございました。。