PowerPoint プレゼンテーション

コンピュータ基礎実験 第11回
コンピュータープログラミング
(C言語)(8)
1.乱数(復習)
2.配列とその利用
乱数(復習)
ランダムに変化する数を生成する関数「乱数」
C言語の乱数:「rand()」関数
#include <stdlib.h> が必要
srand(s): 乱数系列の初期化関数(sはint型整数)
 srand(s)を実行した時点から、sに応じた乱数の系列が始まる
 sが同じなら同じ乱数の列が得られる
 「srand((unsigned int)time(NULL));」を使うとsをいちいち入力する
手間が省ける(time()関数を使うには「#include <time.h>」が必要)
#include <stdlib.h>
#include <time.h>
Int main(void)
{
‥‥
srand((unsigned int)time(NULL));
‥‥
‥‥
rn=rand();
‥‥
}
前回課題 10-7
0から9までの乱数をN個発生させ,それぞれの数字が出た回数およ
び確率を出力するプログラムを作成せよ。 とくにN=100と10000
の場合について, 確率のバラツキはどちらが小さいか確認せよ。
( ex10-7.c)
0から9までの乱数は、「rand()%10」で発生させる
0~9ごとの発生回数はメンバー数10のint型配列に記録する
int ev[10]={0,0,0,0,0,0,0,0,0,0}; 0で初期化しておく
配列を使わずに、ev0, ev1, ev2‥‥でもよいが、出た乱数毎にswitch,
case文で場合分けすることが必要
回数のカウントアップには、「ev[rn]++;」を実行すればよい
i++; ⇔ i+=1; ⇔ i=i+1; ← 全部同じ結果
3
前回課題 10-7
0から9までの乱数をN個発生させ,それぞれの数字が出た回数およ
び確率を出力するプログラムを作成せよ。 とくにN=100と10000
の場合について, 確率のバラツキはどちらが小さいか確認せよ。
( ex10-7.c)
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(void)
{
int i,n_ev,rn;
int ev[10]={0,0,0,0,0,0,0,0,0,0};
srand((unsigned int)time(NULL));
printf("Input number of events: ");
scanf("%d",&n_ev);
for(i=0; i<n_ev; i++){
rn=rand()%10;
ev[rn]++;
}
for(i=0; i<10; i++){
printf("%dは%d回、確率は%fで
す\n",i,ev[i],(float)ev[i]/n_ev);
}
return 0;
}
4
配列とその利用
乱数発生回数のカウントアップに配列を利用し
ましたが、それ以外に配列を利用できないでしょ
うか?
「確率のバラツキ」をもとめるには、平均と標準偏差
が必要⇒カウントアップに用いた配列を利用して
は!
「数の組」⇒ベクトル、行列の計算に利用できない
か?
ベクトル、行列⇒連立方程式を解くことに利用できな
いか?
配列を使った平均、標準偏差計算
0~9の出現回数がev[0]~ev[9]に記録されてい
る。これを利用して、出現回数と確率の平均値と
バラツキを計算しよう。
平均値:
1 N
1
   xi  x1  x2    xN 
N i 1
N
標準偏差(バラツキの指標):

1 N
2



x


i
N  1 i 1
1 N
2


x




i
N  1 i 1
1  N 2
2
  xi  N 
N  1  i 1

例題 11-1
0から9までの乱数をN個発生させ,それぞれの数字が出た回数の平
均値と標準偏差を計算せよ。 とくにN=100と10000 の場合につい
て平均値と標準偏差(バラツキ)を計算せよ。 ( ex11-1.c)
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
int main(void)
{
int i,n,rn;
float m=0,s=0;
int ev[10]={0,0,0,0,0,0,0,0,0,0};
srand((unsigned int)time(NULL));
printf("Input number of events: ");
scanf("%d",&n);
for(i=0; i<n; i++){
rn=rand()%10;
ev[rn]++;
}
for(i=0; i<10; i++){
m+=(float)ev[i];
s+=(float)ev[i]*ev[i];
}
m/=10; /* m=m/10; と同じ意味 */
s-=(10*m*m);
1  N 2

  xi  N 2 
s/=(10-1);
N  1  i 1

s=sqrt(s);
printf(”"平均値=%f 標準偏差
=%f\n",m,s);
return 0;
}
*sqrt()を使うので、「#include <math.h>」
が必要
7
平均値が毎回ぴったり同じなのははぜ?
課題 11-2
0から9までの乱数をN個発生させ,それぞれの数字がでた確率の平
均値と標準偏差(バラツキ)を計算せよ。 とくにN=100と10000
の場合について標準偏差の大きさを比較せよ。 ( ex11-2.c)
例題 11-3
一様な確率分布の乱数を複数個足した数は、正規分布(ガウス分
布)に従うことが知られている。いま、0~9の間に一様に分布す
る整数の乱数を10個足し5で割った整数をXとする。XをN個発生さ
せ、それぞれの数字が出た回数および確率を出力するプログラムを
作成せよ。 ( ex11-3.c)
結果の確率分布をエクセルでグラフにせよ。
 0~9の乱数は「rand()%10」で生成できそう
 一個のXを発生させるのに、10回ループする「for文」で、「rand()」
で生成した乱数を足していけばよい
 上のループをN回繰り返せばよい
 発生回数のカウントアップはex10-7.cのやり方が利用できそう
 Xの範囲は0~18の整数になる
*画面出力をファイルに保存する方法は、
http://www.tuat.ac.jp/~muroo/computer-tips.html
を参照
$ ./ex11-3.exe | tee result.dat とすると、result.datに保存される
エクセルからは、「開く」から、「すべてのファイル」で読み込める
9
例題 11-3
一様な確率分布の乱数を複数個足した数は、正規分布(ガウス分
布)に従うことが知られている。いま、0~9の間に一様に分布す
る整数の乱数を10個足し5で割った整数をXとする。XをN個発生さ
せ、それぞれの数字が出た回数および確率を出力するプログラムを
作成せよ。 ( ex11-3.c)
結果の確率分布をエクセルでグラフにせよ。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
for(i=0; i<n_ev; i++){
rn=0;
for(j=0; j<10; j++){
rn+=rand()%10;
}
rn/=5;
ev[rn]++;
}
int main(void)
{
int i,j,n_ev,rn;
int
ev[19]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0};
srand((unsigned int)time(NULL));
printf("Input number of events: ");
scanf("%d",&n_ev);
for(i=0; i<19; i++){
printf("%d %d\n",i,ev[i]);
}
return 0;
}
10
配列を使ったベクトル、行列の計算
複数の数の組を一括して扱う数学の概念に、
「ベクトル」と「行列」があります
「ベクトル」は添え字が1変数の配列を、「行列」
は添え字が2変数の配列を用いるとC言語で扱う
ことができます。
 1.2 


v    3.7   v[3] {1.2,-3.7,0.23};
 0.23 


 1 2 3


M   4 5 6   m[3][3] {{1,2,3},{4,5,6},{7,8,9}};
7 8 9


例題 11-4: 3X3行列の配列による表現と画面出力
 1 2 3
行列


M   4 5 6
7 8 9


を添え字が2変数の配列を用いて表現し、画面に出力せよ。
( ex11-4.c)
#include <stdio.h>
i
int main(void)
0
1 2
{
float m[3][3]={{1,2,3},{4,5,6},{7,8,9}};
int i,j;
0 12
j
for(i=0; i<3; i++){
for(j=0; j<3; j++){
printf("%f ",m[i][j]);
}
printf("\n");
}
return 0;
}
i行j列の行列を配列m[i][j]で表す
行iのループの中に、列jのループ(2重)
「for」ループを2重につかって、
内側のループ(j)で列を
外側のループ(i)で行を
繰り返して画面表示
1列目 2列目 3列目
(j=0) (j=1) (j=2)
1行目
1
2
3
(i=0)
2行目
4
5
6
(i=1)
3行目
7
8
9
(i=2)
12
例題 11-5
3次元ベクトルを2個入力し、2個のベクトルの、「和」、「差」、
「内積」を計算して表示せよ。( ex11-5.c)
#include <stdio.h>
void inputvec(float a[3]);
int main(void)
{
float a[3],b[3];
float sum[3],diff[3],inner_p=0;
int i;
inputvec(a);
inputvec(b);
for(i=0; i<3; i++){ /* 成分のためのループ*/
sum[i]=a[i]+b[i]; /* 和の計算 */
diff[i]=a[i]-b[i];
/* 差の計算 */
inner_p+=a[i]*b[i]; /* 内積の計算 */
}
printf("和\t\t差\n");
タブ(一定の文字送り)
for(i=0; i<3; i++){
printf("%f\t%f\n",sum[i],diff[i]);
}
printf("内積: %f\n",inner_p);
return 0;
}
void inputvec(float a[3])
{
float w;
int i;
printf("ベクトルの成分を入力してください: ");
for(i=0; i<3; i++){
scanf("%f",&w); a[i]=w;
}
}
13
例題 11-6
3X3正方行列を2個入力し、2個の行列の和を計算して表示せよ。
また積の1,1成分を計算して表示せよ。( ex11-6.c)
2次元配列なので、「for文」の2重ループで、行(i)
のループの中に列(j)のループを入れて成分ごと
に和を計算すればよい
積の1,1成分は行列Aの1行目と行列Bの一列目
について成分の積の和をとる
 a11

 a21
a
 31
a12
a22
a32
a13  b11 b12

a23  b21 b22
a33  b31 b32
b13 

b23 
b33 
ab11  a11b11  a12b21  a13b31
14
例題 11-6
3X3正方行列を2個入力し、2個の行列の和を計算して表示せよ。
また積の1,1成分を計算して表示せよ。( ex11-6.c)
#include <stdio.h>
void inputmatrix(float a[3][3]);
int main(void)
{
float a[3][3],b[3][3],sum[3][3];
float product[3][3]={{0,0,0},{0,0,0},{0,0,0}};
int i,j,k;
inputmatrix(a);
inputmatrix(b);
for(i=0; i<3; i++){
for(j=0; j<3; j++){
sum[i][j]=a[i][j]+b[i][j];
}
}
printf("和\n");
for(i=0; i<3; i++){
for(j=0; j<3; j++){
printf("%f\t",sum[i][j]);
}
printf("\n");
}
for(k=0; k<3; k++){
product[0][0]+=a[0][k]*b[k][0];
}
printf("積の1,1成分: %f\n",product[0][0]);
return 0;
}
void inputmatrix(float a[3][3])
{
int i,j;
float w;
for(i=0; i<3; i++){
printf("行列の%d行めの成分を入力してくださ
い: ",i+1);
for(j=0; j<3; j++){
scanf("%f",&w); a[i][j]=w;
}
}
}
15
課題 11-7
3X3正方行列を2個入力し、2個の行列の積を計算するプログラ
ムを完成させよ。( ex11-7.c)
ex11-6.cでは1,1成分について計算している
i, j成分を計算するにはどこを変更すればよ
いか for(k=0; k<3; k++){
product[0][0]+=a[0][k]*b[k][0];
}
i
j
「for文」でiとjについてループを回せばよい
(ex11-6.cのinputmatrix()関数を参照)
16
例題 11-8 (発展:任意の次元のベクトル)
「DIM」次元ベクトルを2個入力し、2個のベクトルの、「和」、
「差」、内積を計算して表示せよ。「DIM」の値をいろいろ変えて
コンパイルして結果を比較せよ。( ex11-8.c)
#include <stdio.h>
#define DIM 3
void inputvec(float a[DIM]);
int main(void)
{
float a[DIM],b[DIM];
float sum[DIM],diff[DIM],inner/_p=0;
int i;
inputvec(a);
inputvec(b);
for(i=0; i<DIM; i++){ /* 成分のためのループ*/
sum[i]=a[i]+b[i]; /* 和の計算 */
diff[i]=a[i]-b[i];
/* 差の計算 */
inner_p+=a[i]*b[i]; /* 内積の計算 */
}
printf("和\t\t差\n");
for(i=0; i<DIM; i++){
printf("%f\t%f\n",sum[i],diff[i]);
}
printf("内積: %f\n",inner_p);
return 0;
}
void inputvec(float a[DIM])
{
float w;
int i;
printf("ベクトルの成分を入力してください: ");
for(i=0; i<DIM; i++){
scanf("%f",&w); a[i]=w;
}
}
「#define DIM 3」とすると、以下の「DIM」の
部分が全て「3」に置き換わる。
「#define DIM 3」を「#define DIM 4」とすれ
ば4次元ベクトル用のプログラムになる。
17
配列を使った連立方程式の解法
連立(代数)方程式は行列を用いて表すことが
できます⇒線型代数
係数部分を表す「係数行列」の逆行列が求まれ
ば、逆行列の掛け算によって、解は簡単に求ま
ります
行列、ベクトルを配列で表すことにより、2変数
(x,y)連立方程式を解くプログラムを作ろう
2変数連立方程式と行列
2変数連立方程式の行列による表現
 ax  by  e
 a b  x   e 
    
 

cx  dy  f
 c d  y   f 
係数行列 A
もし逆行列A-1が求まれば
        a b   1 0 
, 

  

A1  
        c d   0 1 
 a b  x   e 
    a b  x      e 

      

   
 
 c d  y   f 
    c d  y      f 

 1 0  x      e 

   
 
 0 1  y      f 
2X2行列の逆行列
2X2行列の逆行列
a b 
1  d  b
1
 のとき A 


A  
ad  bc   c a 
c d 
ただし ad  bc  0
(ad  bc  0の時、逆行列は存在し ない )
例題 11-9
2X2行列を入力し、逆行列を表示するプログラムを作成せよ。
( ex11-9.c)
#include <stdio.h>
#define DIM 2
printf("逆行列:\n");
for(i=0; i<DIM; i++){
for(j=0; j<DIM; j++){
printf("%f\t",inv_a[i][j]);
}
printf("\n");
}
void inputmat(float a[DIM][DIM]);
int main(void)
{
float a[DIM][DIM],inv_a[DIM][DIM],determinant;
int i,j;
}
return 0;
inputmat(a);
}
determinant=a[0][0]*a[1][1]-a[0][1]*a[1][0];
if(determinant==0){
printf("逆行列は存在しない\n");
}
else{
inv_a[0][0]=a[1][1]/determinant;
inv_a[1][1]=a[0][0]/determinant;
inv_a[0][1]=-a[0][1]/determinant;
inv_a[1][0]=-a[1][0]/determinant;
void inputmat(float a[DIM][DIM])
{
float w;
int i,j;
printf("行列の成分を入力してください:\n");
for(i=0; i<DIM; i++){
for(j=0; j<DIM; j++){
scanf("%f",&w); a[i][j]=w;
}
}
}
21
課題 11-10
2X2の係数行列と、2成分定数ベクトルを入力し、連立方程式を解
くプログラムを作成せよ。解が存在しない(不定、不能)場合には
「解なし」と出力すること。( ex11-9.c)
係数行列の逆行列を定数ベクトルに掛けて
得たベクトルの成分が、解x,yになる
逆行列が存在しないとき⇒「不定」、「不能」
の解なしに対応
22
特別課題 11-11
N連の連立方程式を解くプログラムを作成せよ。解が存在しない
(不定、不能)場合には「解なし」と出力すること。( ex1111.c)
N連の連立方程式を解くためにはNXN正方
行列(係数行列)の逆行列が求まればよい
NXN正方行列の逆行列を求めるには、ガウス・
ジョルダン法(Gauss-Jordan elimination)がある
⇒中学でならう消去法と同じもの
ガウス・ジョルダン法の簡略化バージョンとして、
ガウス消去法(Gaussian elimination)がある
WEBでアルゴリズムを調べてみよ(提出に
はおよばない)
23
実習結果のレポート
• 3つのソースファイル「ex11-2.c」、「ex11-7.c」、
「ex11-10.c」を添付ファイルにしてメールを送っ
てください。
• 宛先: [email protected]
• 件名:コンピューター基礎実験11
• 本文:感想および一言
24