課題で・・複素数の四則演算

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
がんも

課題で・・複素数の四則演算

#1

投稿記事 by がんも » 18年前

どうも理解ができなくて困っています(>_<;)

問題はこうです。

いま,複素数を表す構造体 COMPLEX を,次の文によって定義したとします.

typedef struct{ float re, im; } COMPLEX;

例えば COMPLEX c; と宣言したとき,c.re が複素数 c の実数部を表し,c.im が虚数部を表します.このとき,複素数どうしの四則演算を行なう関数 c_cal() を書きなさい.ただし,この関数の最初の行を

COMPLEX c_cal ( COMPLEX a, char c, COMPLEX b )

と定め,プログラム中で c_cal(a,'+',b) と書けば複素数 a とbの和を返し(返却値として出力し),同様に,c_cal(a,'-',b)}, c_cal(a,'*',b), c_cal(a,'/',b) と書くことにより,複素数 a と b の差,積,商をそれぞれ返すものとします.



これはscanfを使って入力するのかなとも思いましたが
複素数の入力なんてできるのですか?・・・

かといってプログラムの中で複素数の計算もどうやればよいのかよくわかりません;;;
ぜひアドバイスをください

box

Re:課題で・・複素数の四則演算

#2

投稿記事 by box » 18年前

> これはscanfを使って入力するのかなとも思いましたが
> 複素数の入力なんてできるのですか?・・・

複素数をa+biの形で表わすとすると、
実部aも虚部bも実数ですので、scanf()で2つの実数を入力して、
構造体のメンバーに割り振ればよいです。

> かといってプログラムの中で複素数の計算もどうやればよいのかよくわかりません;;;

いったんプログラムを離れて、(a+bi)と(c+di)の四則演算を
手で行なってみてください。どうなりますか?

管理人

Re:課題で・・複素数の四則演算

#3

投稿記事 by 管理人 » 18年前

こんにちは。

課題をやる上で苦労した経験を思い出します。
当時は難しく感じていたものも、今思えば何故あの時は順を追って考えなかったのだろうと思います。
こういう課題において、プログラムって実は一つ一つの処理って難しくないんですよね。
沢山しないといけないことがあるので、難しく感じてしまっているだけなんです。
ですので、私が一つ一つ答えに導くような課題を出しますので少しずつ完成にもっていきませんか?

まず、scanfの使い方は理解していらっしゃるようですね。
構造体の利用の仕方はOKですか?
typedef struct{
   int x,y;
}com_t;
 
 

このように定義すると、xとyという人まとまりのケースが出来ます。

com_t com;

このように宣言するとxとyのまとまりであるcom_t型の変数がcomという名前で用意されます。
つまり、comというまとまりのなかに、xとyというint型の変数がそれぞれある状態です。
ですから、comの中にあるxをさしたい時は
com.x
とかけるわけです。
その辺はOKですか?

もし「微妙だ・・」と感じてらっしゃるようでしたらおさらいしておいてくださいね。
http://www9.plala.or.jp/sgwr-t/c/sec15.html

次に、自作関数については大丈夫ですか?
自分で関数を作れますか?
return文の意味は解っていますか?

もしわからなければ過去に自作関数について答えたので、
http://www.play21.jp/board/formz.cgi?ac ... q&rln=7257
こちらのトピの「自作関数について」をお読み下さい。

また、プログラムでわからないことがあればウェブで何でも調べられますから
http://www5c.biglobe.ne.jp/~ecb/c/c00.html
ウェブで勉強してください。

では課題の内容に入っていきます。

課題1-1
xというint型の変数を用意し、scanfで整数を入力させ格納し、xのデータを表示するだけのプログラムを書きましょう。

課題1-2
typedef struct{ float re, im; } COMPLEX; という名前で構造体を定義し、
COMPLEX c;
で宣言した変数のreとimにそれぞれ1.3と2.4を単なるイコールの代入で格納し、表示するプログラムを書きましょう。

課題1-3
先ほどのre,imにそれぞれscanfで実数の値を取り込み表示するプログラムを書きましょう。その際、入力前にprintf文で任意に何の値を入力させるのかを促す説明文を追加しましょう。

課題2-1
main関数から自作関数void calc(int a,int b);へ、メイン関数内で、入力させた2つの整数を渡し、その関数内で、2つの数値の和を表示するプログラムを書きましょう。

課題2-2
main関数から自作関数int calc(int a,int b);へ、メイン関数内で、入力させた2つの整数を渡し、その関数内で和を計算し、計算結果をint型でメイン関数へ返し、その結果をメイン関数で表示するプログラムを書きましょう。

課題2-3
main関数から自作関数double calc(double a,double b);へ、メイン関数内で、入力させた2つの「実数」を渡し、その関数内で「割り算」を計算し、計算結果をdouble型でメイン関数へ返し、その結果をメイン関数で表示するプログラムを書きましょう。
また、割り算をするとき、分母が0の時は計算が出来ないエラーをprintfで表示し、main関数には-1を返します。

課題3-1
main関数で、課題1-3と同じように構造体に2つの実数を格納させる。宣言はCOMPLEX a,b;とし、計4つの値を取り込む。
自作関数COMPLEX calc(COMPLEX a,COMPLEX b);へ、メイン関数内で、入力させた2つのa,bを渡し、その関数内で 新たにCOMPLEX cを宣言。a.im+b.im , a.re+b.re の計算結果をそれぞれcに格納する。計算結果をCOMPLEX型でメイン関数へ返し、その結果をメイン関数で表示するプログラムを書きましょう。

最終課題 は提示された課題です。

管理人

Re:課題で・・複素数の四則演算

#4

投稿記事 by 管理人 » 18年前

とりあえず課題2-3位からこの課題が本格化してきますので、
もし「2-2位までは楽勝でわかる」という感じでしたら、以下回答例を書いておきますので、自分が書いたプログラムと照らし合わせて、合ってたら次々進んでください。

少しでもわからない、あるいは「なんでこうなるの?」みたいな疑問がちょっとでもある課題については、そこで手をとめて、
先ほど紹介したウェブなどの参考サイトをじっくり読んでください。
ちょっとでもわからないところをそのままにしていると後でつけがまわってきます。
着実に全てを理解しながら進んでいった方が身に付きますので、今までの知識を確認しながら先に進んでください。
これらが全部出来たら課題も完成するはずです。

以下回答例

課題1-1

#include <stdio.h>
void main(){
	int x;
	scanf("%d",&x);
	printf("%d",x);
	return ;
}

課題1-2

#include <stdio.h>

typedef struct{
	float re,im;
}COMPLEX;

void main(){
	COMPLEX c;
	c.re=1.3;
	c.im=2.4;
	printf("%f %f",c.re,c.im);
	return ;
}

課題1-3

#include <stdio.h>

typedef struct{
	float re,im;
}COMPLEX;

void main(){
	COMPLEX c;
	printf("実数部->");
	scanf("%f",&c.re);
	printf("虚数部->");
	scanf("%f",&c.im);
	printf("実数部=%f 虚数部=%f",c.re,c.im);
}

課題2-1

#include <stdio.h>

void calc(int a,int b){
	printf("%d",a+b);
}
void main(){
	int x,y;
	scanf("%d",&x);
	scanf("%d",&y);
	calc(x,y);
}

課題2-2

#include <stdio.h>

void calc(int a,int b){
	return a+b;
}
void main(){
	int x,y,z;
	scanf("%d",&x);
	scanf("%d",&y);
	z=calc(x,y);
	printf("%d",z);
}

Re:課題で・・複素数の四則演算

#5

投稿記事 by » 18年前

丁寧に教えていただいて本当にうれしい限りです(;_;)

そしてヒントをもとになんとか自力でプログラムを作ったのですが、

出力が0にしかならないんです;;どこが間違っているか指摘していただければうれしいです


#include <stdio.h>
#include <math.h>

typedef struct{ float re, im; } COMPLEX;

COMPLEX c_cal(COMPLEX a, char c, COMPLEX b)
{
COMPLEX sum;

if(c == '+'){sum.re = a.re + b.re ; sum.im = a.im + b.im ;}
else if(c == '-'){sum.re = a.re - b.re ; sum.im = a.im - b.im ;}
else if(c == '*'){sum.im = a.im * b.im - a.re * b.re ;
sum.re = a.im * b.re + a.re * b.im ;}
else if(c == '/'){sum.im = (a.im * b.im + a.re * b.re)/(pow(b.im,2)+pow(b.re,2)) ;
sum.re = (a.re * b.im - a.im * b.re)/(pow(b.im,2)+pow(b.re,2)) ;}

return sum;

}

int main(void)
{
COMPLEX a, b, sum; char c;

printf("aの虚数部 実数部 四則演算子 bの虚数部 実数部\n");
scanf("%f%f%c%f%f", &a.re, &a.im, &c, &b.re, &b.im);

sum = c_cal(a, c, b);

printf("sum = %f + (%f)i\n", sum.im, sum.re);

}

管理人

Re:課題で・・複素数の四則演算

#6

投稿記事 by 管理人 » 18年前

なんだ、始めて1ヶ月ということなので、まだ手も足も出ないで困ってらっしゃるのかと思いましたが、かなりもう答えまで出来てるじゃないですか^^
これは失礼しました。

途中のプログラムがまちがってなさそうなのに、計算結果がおかしいときは
取り込んだ数値が正常かどうかをまず、疑います。
入力した数値は本当にとりこめていますか?

scanf("%f%f%c%f%f", &a.re, &a.im, &c, &b.re, &b.im);
printf("%f %f %c %f %f\n", a.re, a.im, c, b.re, b.im);

このように書いて確かめてみましょう。

管理人

Re:課題で・・複素数の四則演算

#7

投稿記事 by 管理人 » 18年前

scanf("%f%f%c%f%f", &a.re, &a.im, &c, &b.re, &b.im); 
この記述を

scanf("%f%f %c%f%f", &a.re, &a.im, &c, &b.re, &b.im); 
このように変更して実行すると、printfの結果はどうなりますか?

何故このような事になるか、ウェブで調べて見ましょう!!
 
 

検索をかけるとよいワードのサンプル・・。
http://www.google.co.jp/search?hl=ja&q= ... %AD%97&lr=
http://www.google.co.jp/search?hl=ja&q= ... %99%BA&lr=
http://www.google.co.jp/search?hl=ja&q= ... %82%8B&lr=
http://www.google.co.jp/search?hl=ja&q= ... %82%A1&lr=

管理人

Re:課題で・・複素数の四則演算

#8

投稿記事 by 管理人 » 18年前

大事なエラー処理が一つ出来てないです。
課題2-3を見てエラー処理を追加してみてください。
例えば全部0の値をいれて計算するとどうなります?

それから計算できる演算子じゃない文字が入力された時のエラー処理も行うと良いでしょう。
そこまでしろと書いてないので、しなくてもいいかもしれませんが、よいいいと思います。
scanfはなかなかエラー処理のしにくい関数ですが、ある程度オプションがあります。例えば
#include <stdio.h>

void main() {
	char c=NULL;
	printf("a,b,c,dのどれかを入力してください。->");
	scanf("%[abcd]" , &c);
	if(c==NULL)
		printf("正常に入力されていません。\n");
	else
		printf("入力された値 = %c" , c);
}

実行結果1

a,b,c,dのどれかを入力してください。->b
入力された値 = b

実行結果2

a,b,c,dのどれかを入力してください。->z
正常に入力されていません。
 
このように「abcd」のどれかの文字だけしか入力を受け付けたくない時は%[abcd]と書く事が出来ます。
また、「abcd以外」を受け付けたい時は%[^abcd]とかけます。
便利なので知っておくと得ですよ♪

管理人

Re:課題で・・複素数の四則演算

#9

投稿記事 by 管理人 » 18年前

ん・・・。
掛け算間違ってないですか?

sum.im = a.im * b.im - a.re * b.re ;
sum.re = a.im * b.re + a.re * b.im ;

ここ。

計算式は


(a+bi) * (c+di)
=a*c + a*di + c*bi + bi*di
=(a*c - b*d) + (a*di + c*bi)
 ↑↑ ↑↑
  rere  imim
      ↑
   re
つまりリアルパートにリアルパートとイマジナリーパートの混合の掛け算があってはおかしいですよね。
ですから上記計算式をそのままあてはめると

z.re = z1.re * z2.re - z1.im * z2.im;
z.im = z1.re * z2.im + z1.im * z2.re;

こんな感じになりませんか?
 
 

管理人

Re:課題で・・複素数の四則演算

#10

投稿記事 by 管理人 » 18年前

あえてscanfを使って厳密に書くならこんな感じでしょうかねぇ。
#include <stdio.h>
#include <stdlib.h>
#include <math.h> 

typedef struct{ 
	float re,im;
}COMPLEX; 

COMPLEX c_cal(COMPLEX a, char c, COMPLEX b){ 
	COMPLEX sum; 
	if(c == '+'){
		sum.re = a.re + b.re ;
		sum.im = a.im + b.im ;
	} 
	else if(c == '-'){
		sum.re = a.re - b.re ;
		sum.im = a.im - b.im ;
	} 
	else if(c == '*'){
		sum.re = a.re * b.re - a.im * b.im ;
		sum.im = a.im * b.re + a.re * b.im ;
	} 
	else if(c == '/'){
		if(b.im==0 && b.re==0){
			printf("0割禁止\n");
			exit(99);
		}
		sum.im = (a.im * b.im + a.re * b.re)/(pow(b.im,2)+pow(b.re,2));
		sum.re = (a.re * b.im - a.im * b.re)/(pow(b.im,2)+pow(b.re,2)) ;
	}
	else{
		printf("対応した演算子ではない。\n");
		exit(99);
	}
	return sum; 
} 

void main(void){ 
	int x=0,z;
	COMPLEX a, b, sum; char c=NULL; 

	while(x<5){
		switch(x){
			case 0:
				printf("aの\n実数部 -> ");	z=scanf("%f",&a.re);	break;
			case 1:
				printf("虚数部 -> ");		z=scanf("%f",&a.im);	break;
			case 2:
				printf("四則演算子 -> ");	scanf(" %[-+/*]c",&c);	break;
			case 3:
				printf("bの\n実数部 -> ");	z=scanf("%f",&b.re);	break;
			case 4:
				printf("虚数部 -> ");		z=scanf("%f",&b.im);	break;
		}
		switch(x){
			case 0:case 1:case 3:case 4:
				if(z==0)	printf("数値以外の入力。再入力してください。\n");
				else		x++;
				break;
			case 2:
				if(c!=NULL)	x++;
				else		printf("四則演算子ではありません。再入力してください。\n");
				break;
		}
		fflush(stdin);
	}

	sum = c_cal(a, c, b); 

	printf("(%.2f + %.2fi) %c (%.2f + %.2fi) =",a.re,a.im,c,b.re,b.im);
	printf(" %.2f + (%.2f)i\n", sum.re, sum.im); 
} 

実行結果1

aの
実数部 -> 2.3
虚数部 -> 3.1
四則演算子 -> /
bの
実数部 -> 0.2
虚数部 -> 2.1
(2.30 + 3.10i) / (0.20 + 2.10i) = 0.95 + (1.57)i


実行結果2

aの
実数部 -> 2.1
虚数部 -> s.8ds2
数値以外の入力。再入力してください。
虚数部 -> 1.2
四則演算子 -> 2d1c/
四則演算子ではありません。再入力してください。
四則演算子 -> *
bの
実数部 -> 1.0
虚数部 -> 0.9
(2.10 + 1.20i) * (1.00 + 0.90i) = 1.02 + (3.09)i
 
 
半分寝ながら書いたんでどこか違うかもしれませんけど、とりあえず、エラー処理はしておいたほうがよいのではないでしょうか。

ではおやすみなさいm(_ _)MzzZ

しっぽ

Re:課題で・・複素数の四則演算

#11

投稿記事 by しっぽ » 18年前

else if(c == '/'){
		if(b.im==0 && b.re==0){
			printf("0割禁止\n");
これはORの間違えでしょう。
if(b.im==0 || b.re==0){

box

Re:課題で・・複素数の四則演算

#12

投稿記事 by box » 18年前

&& でいいんじゃないでしょうか。
3+0i(つまり、ただの3)で割ったり、0+7i(つまり、ただの7i)で割ったり
することは十分に考えられます。

管理人

Re:課題で・・複素数の四則演算

#13

投稿記事 by 管理人 » 18年前

&&でいいと思います。
というのも、分母に値が2種類あるので、分母が0になるにはその2つの値が両方0でないといけません。
それぞれの値は2乗しているので、2-2のように2つの値が相殺して0になることもありませんから、
やはり分母が0になるパターンは2つの値が両方0の時のみに成ると思います。

sum.im = (a.im * b.im + a.re * b.re)/(pow(b.im,2)+pow(b.re,2));

この式ですから、b.imとb.reどちらも0の時のみ分母が0になると考えましたm(_ _)m

管理人

Re:課題で・・複素数の四則演算

#14

投稿記事 by 管理人 » 18年前

一つおまけを言いますと、

scanf(" %[-+/*]c",&c);	break;

この記述。

scanf(" %[+-*[/url]c",&c);	break;

と書くと、うまくいきません。[+-*[/url]の方が順番的にすんなりいくように思うのに、
わざと順番を変えているのには理由があります。
実は[/url]の中は正規表現に似た書き方が出来、
[0-9]とかけば、0~9の値ということになり、2でも5でもマッチします。
ですから、[+-*[/url]の前半の[+-*]は、「"+"~"*"」の文字コード内でマッチさせろと言う意味に
なってしまい、"-"マイナスの文字としての意味を失ってしまいます。

http://e-words.jp/p/r-ascii.html
アスキーコード表によると、"+"は43、"*"は42ですから、43~42で、間違ってマッチする文字は
無いにしても、マイナスがマッチしなくなります。

この○~○という範囲を表すマイナスは一番最初に書くか、最後に書けばただの文字と解釈されますので、
マイナスを一番最初に書く必要があったということです。
ちょっと発展的なお話でしたが、「なぜこの順番じゃないとうまくいかないんだ?」と
疑問でしたらその疑問が解決すればと思い書きました。
 
 
 

しっぽ

Re:課題で・・複素数の四則演算

#15

投稿記事 by しっぽ » 18年前

> && でいいんじゃないでしょうか。
> 3+0i(つまり、ただの3)で割ったり、0+7i(つまり、ただの7i)で割ったり
> することは十分に考えられます。

了解です。

がんも

Re:課題で・・複素数の四則演算

#16

投稿記事 by がんも » 18年前

本当にいろいろありがとうございます!

%fと%cの間にスペースを入れたらとりあえず足し算はできました。
ご指摘どおり割り算や、+-/*以外の記号が入れられた場合のときを作りたいと思います。

掛け算なのですが、
sum.im = a.im * b.im - a.re * b.re ではあっていませんか??

それと、書いていただいたプログラムですが、自分にはまだ理解できないものがいっぱいありまして・・(汗

NULLとexit(99)は初めて見ました。どういう意味なのか教えていただけたらうれしいです

がんも

Re:課題で・・複素数の四則演算

#17

投稿記事 by がんも » 18年前

またまたすみません;;

%fと%cの間にスペースを入れたのは、cがスペースも文字として読み込んでしまうからなのですか??

がんも

Re:課題で・・複素数の四則演算

#18

投稿記事 by がんも » 18年前

わたしreのほうを虚部と勘違いしてました!掛け算間違ってましたね;;

がんも

Re:課題で・・複素数の四則演算

#19

投稿記事 by がんも » 18年前

結構できました。ひとつ気になるのが、
scanf("%f",&a.re);
scanf("%f",&a.im);
scanf("%c",&c); if(c == '\n') scanf("%c",&c);
scanf("%f",&b.re);
scanf("%f",&b.im);
という感じにかいて、記号が+-*/以外だったらプログラムを終了するようにしましたのですが
(exit(99)というのを使いました、だけど、意味はなんとなくでしかわかってません;;)
入力のときに
たとえば
1 1 + 1 1
と入力するとスペースが文字と判断されるらしくて、終了してしまいます。
これはどう回避すればよいでしょうか?

管理人

Re:課題で・・複素数の四則演算

#20

投稿記事 by 管理人 » 18年前

NULLというのは「何もない」ということです。ヌル文字とか終端記号の別名で呼ばれるのご存知でしょうか。
何も無い、つまり条件にマッチせず、何も格納されていないと言う状態を示します。
exit関数はそこで処理を強制終了する関数です。

また、%cの前にスペースをあけると、改行コードを含め、そこまでの空白などを読み飛ばしてくれるのです。

>たとえば
>1 1 + 1 1
>と入力するとスペースが文字と判断されるらしくて、終了してしまいます。

スペースが文字と認識されているわけではありません、scanfはスペースか改行で区切られた区間を一区間としているのです。
もしこれを避けるにはgetchar関数を使うべきでしょう。
getchar関数は標準入力からのデータを1文字ずつ取得する関数ですから、スペースがあったら読み飛ばすようにプログラムを組めばスペースを無視できます。
記号がはいってくるまでを数値データとすればいいのです。
ただ文字データを数値データに変更しなければなりませんし、文字列処理が配列とカラムなど、結構複雑になってしまいますので、ちょっと入門したての方法としてはわかりずらいかもしれません。
スペースを無視する仕様にしなければならないのでしょうか?
もしそうならgetcharについてお調べ下さい。

また、exit(99)の「99」は別にこの数字でなくてもいいです。
このプログラム以外の他のプロセスに与える数値であり、どこでエラーが発生して終了したかを示すものですので、この中の値はなんでもいいです。
とりあえずexit関数を呼べば処理が終了します。

YuO

Re:課題で・・複素数の四則演算

#21

投稿記事 by YuO » 18年前

えーっと……。管理人さんのコード/発言に突っ込みどころが……。
あんまりだというところがあるので,書いておきます。

----引用ここから----
scanf("%[abcd]" , &c);
    if(c==NULL)
        printf("正常に入力されていません。\n");
    else
        printf("入力された値 = %c" , c);
----引用ここまで----
cはcharですから,不適切です。
[は,スキャンセットでない文字を見つけると自動的に終端のナル文字を追加します。
よって,a, b, c, dどれか一文字でも入力されると,*(&c + 1)以降にナル文字を書き込むため,未定義の動作となります。
# cへの代入の有無はscanfの戻り値を使った方がわかりやすいかと。


> fflush(stdin);

よくある間違い:http://www.kouno.jp/home/c_faq/c12.html#26
fflushを入力ストリームに使った場合の結果は未定義とされています。

ちなみに,昔http://m--takahashi.com/bbs/pastlog/01200/01131.htmlがありまして,リダイレクトを行った場合に動作が変化するなど,別の影響もあったりします。


基本的にはscanfとそれ以外の入力関数を混ぜないというhttp://www.kouno.jp/home/c_faq/c12.html#18に従うのがよいでしょう。


> NULLというのは「何もない」ということです。ヌル文字とか終端記号の別名で呼ばれるのご存知でしょうか。

NULLは空ポインタ定数 (null pointer constant) であって,ヌル文字 (JISではナル文字,null character)や終端記号とは異なります。

管理人

Re:課題で・・複素数の四則演算

#22

投稿記事 by 管理人 » 18年前

ご指摘ありがとうございます。

>scanf("%[abcd]" , &c);

こちら記載ミスでした。サンプルや、他の説明の中では

scanf("%[abcd]c" , &c);

こんな感じでcをつけてあります。
これで大丈夫だと思っていたんですが・・。

調べてみるとこれでもcのアドレス以外にも値が書き込まれるようですね。

fflushについては使用の仕方に問題があるようですね。それも含め少し調べてみます。

またscanfとgetcharを同じプログラムに使ってはいけないという意味がはっきりよくわからないので、こちらも明日調べて見ます。

>NULLは空ポインタ定数 (null pointer constant) であって,ヌル文字 (JISではナル文字,null character)や終端記号とは異なります。

昔この記事を読んだ事がありました。
http://www.cmagazine.jp/src/kinjite/c/null.html
昔から両方とも0と思っても不具合が無く、ずっと両方0を表すと思っていたので抜けきれていませんでした。
ご指摘ありがとうございました。
間違っていた部分についてお詫びします。

がんも

Re:課題で・・複素数の四則演算

#23

投稿記事 by がんも » 18年前

できました!本当にありがとうございました。
またお世話になると思いますがよろしくお願いします。

閉鎖

“C言語何でも質問掲示板” へ戻る