] > Mupad, Scilab, array

第 10 回 Mupad, Scilab, 配列

本日の内容


このドキュメントは http://edu.net.c.dendai.ac.jp/ 上で公開されています。

10-1. Mupad

この資料は平田 浩 一さん / 愛媛大学教育学部 の「数式処理 MuPAD 入門 」 Tomonori Kouya さんの「Documents on MuPAD」を参考にして作 りました。 また、Mupad の教科書として 赤間 世紀「はじめてのMuPAD」Springer-Verlag Tokyo が出版されています。

基本操作

Mupad は数式を処理するアプリケーションソフトウェアです。 Mupad では求めたい式を記述してそのまま Enter を押すだけです。 % (あるいは %1 )は直前の結果を指します。%2 は 2 個前の結果を、 %3 は 3 個前の結果を指します。

整数、有理数

整数や有理数の四則演算は +, -, *, / を使用します。割算は有理数として扱 われ、分数で計算されます。 なお、整数の割算で商と余りを求めるには div, mod を使います。 巾乗は ^ を使用します。 階乗は ! を使用します。

例
1+1
5/2
5 div 2
5 mod 2
2^10
6!

演習10-1

次の計算をしなさい

  1. 13 + 16
  2. 100!

ある数が素数かどうかを判断するには isprime 関数を使います。 また、素因数分解するには ifactor 関数を使います。

例
isprime(7)
ifactor(18)

演習10-2

2105 -1 , 2106 -1 , 2107 -1 は素数ですか? もし素数でなければ素因数分解しなさい。


実数

ルートの処理は基本的には開平されず、そのまま取り扱われます。 ルートは sqrt(2) でも 2^(1/2)でもどちらでも可能です。 また、円周率は PI、自然対数の底は E で表します。 一方、求めた値を数値で得たい場合は float 関数を使用します。必要な精度 は DIGITS 変数に桁数を代入することで与えます。

例
2^(1/2)
DIGITS:=30
float(PI)

演習10-3

2 を 20 桁求めなさい。

変数

Mupad では任意の文字を変数として使うことができます。x, y x1 など。 代入は「:=」という記号を使います。

例
x:=1

なお、代入された変数は方程式には使えません。 代入を取り消すには、delete を使います。

例
delete x

式の計算

expand は式を展開します。 simplify は式を単純化します。 radsimp は根号を整理します。

例
expand((x+y)^2)
simplify(sqrt(2)*sqrt(8))
radsimp(1/sqrt(2))

演習10-4

次の計算をしなさい。

  1. 2 + 3 2
  2. 1 + 3 10

演習10-5

次の式において分母にルートがない形にしなさい。

3 + 2 3 - 2

複素数

虚数単位 = -1 は Mupad では「I(英字大文字)」で表します。 実部と虚部に分離する関数は rectform です。また絶対値は abs です。

例
rectform((-1)^(1/3))

(これは x2 +x +1 =0 の解の一つ)

演習10-6

次の値を求めなさい。 また大きさ(絶対値)も求めなさい。

  1. -1 の平方根
  2. 2 + 10
  3. π

多項式、方程式

多項式を因数分解するには factor を使います。 式をある変数に関して解くには collect を使います。 式に値を代入するには subs を使います。 方程式を解くには solve を使います。 なお、変数には任意の式を代入できます。

例
factor(x^2+2*x+1)
collect((x+y+z)^3,[x])
subs(x^2,[x=(y+1)])
solve(x^2+3*x+2,x)
solve({x+y=1,x-y=-1},{x,y})

演習10-7

次の多項式を因数分解しなさい

  1. a3 + b3 + c3 - 3 a b c

演習10-8

次の多項式を x についてまとめなさい。

(x+y) 3 + (x+y) 2 + (x+y)+1

演習10-9

X= x+2 , Y= x-2 のとき、 X3 = Y2 を解きなさい。

ヒント

式に別の式を代入するには、 subs(X^3~Y^2,[X=x+2,Y=x-2]) とする。 Rootof という表記に関しては、 float(%) を指定すると、数値解が得られる。


微積分

関数を微分するには diff を使います。 不定積分を求めるには int を使用します(積分定数が付加されないことに注意)。 定積分も同様に int を使用しますが、第二引数で積分区間を指定します。 微分方程式を定義するには ode という関数を使います。 初期値を与えることもできます。

diff(x^2,x);
int(x,x);
int(x^2,x=1..2);
solve(ode(y'(x)=y(x),y(x)))
solve(ode({y'(x)=y(x),y(0)=1},y(x)))

演習10-10

次の関数を微分しなさい

  1. sinx
  2. 1x
  3. x sinx

演習10-11

次の関数を積分しなさい

  1. sinx
  2. 1x
  3. x sinx

演習10-12

次の常微分方程式を解きなさい。

  1. y x = x , y 0 = 1
  2. y x = x y , y 0 = 1
  3. y x = 1y , y 0 = 1

グラフ

二次元グラフを書くには plotfunc2d、 三次元グラフを書くには plotfunc3d を使います。

例
plotfunc2d(sin(x),cos(x),x=-PI..PI)
plotfunc3d(x^2+y^2)

演習10-13

次のグラフを書きなさい

  1. y=tanx
  2. z = sin x2 + y2

数列

数列の和を求めるには sum を使います。 また極限を求めるには limit を使います。 なお無限大は infinity と表します。 右極限、左極限はそれぞれ最後に Right, Left のオプションを付けます。

例
sum(i,i=1..10)
sum(i,i=1..n)
sum(2^(-i),i=1..infinity)
limit(1/n, n=infinity)
limit(x^x,x=0,Right)
limit(x/abs(x),x=0,Right)
limit(x/abs(x),x=0,Left)

演習10-14

次の値を求めなさい。

  1. 1+ 3+ ...+ 2n -1
  2. 12+ 22+ ...+ n2
  3. 1 12 + 1 22 + 1 32 + ...
  4. lim n 1+ xn n
  5. 1 + 1 1! + 1 2! + 1 3! + ...

ベクトル、行列

行列は次のようにして定義します。

matrix([[1,2],[3,4]])

行ベクトル、列ベクトルは次のように定義します。

matrix([[1,2,3]])
matrix([1,2,3])

行列の演算は +, -, * が使用できます。 また逆行列は (1/行列) あるいは -1 乗で求めます。

例
A:=matrix([[1,2],[3,4]])
B:=matrix([[5,6],[7,8]])
A+B
A-B
3*A
A*B
1/A
A^(-1)

転置は linalg::transpose を使用します。 行列式は linalg::det を使用します。 Rank は linalg::rank を使用します。 ベクトルの大きさは norm(ベクトル,2) で求めます。 内積は linalg::scalarProduct を使用します。 二つのベクトルのなす角は linalg::angle を使用します。 外積は linalg::crossProduct を使用します。 n×n の正方行列 A と n 次元列ベクトル b に対して、方程式「Ax=b」を解く には linalg::linearSolve(A,b) を使います。

なお、「lialg::」を省略したい場合は始めに「export(linalg)」を実行しま す。

例
export(linalg)
A:=matrix([[1,2,3],[4,5,6],[7,8,9]])
traspose(A)
det(A)
rank(A)
rank(matrix([[1,1],[1,1]]))
x:=matrix([1,2,3])
y:=matrix([4,5,6])
norm(x,2)
scalarProduct(x,y)
transpose(x)*y
angle(x,y)
crossProduct(x,y)
linearSolve(A,x)

演習10-15

ベクトル x=(1,2), y=(3,4) に対して次の問いに答えなさい。

  1. x, y の大きさを求めなさい。
  2. x, y のなす角を求めなさい。

演習10-16

頂点座標が (1,2,3),(4,5,6),(7,8,9) の三角形の面積を求めなさい。

ヒント

三角形の二辺を表すベクトルを a, b とすると、面積は次の式 で求まる。

1 2 a の長さ b の長さ sin a b のなす角

10-2. SciLab

MATLAB という数値計算ソフトウェアがあります。 これはアメリカで行われた LINPACK と EISOACK という行列演算アルゴリズム を作るプロジェクトの成果を元に作られました。 このソフトウェアは有償です。

一方、 SCILAB(ホームページ)は、 INRIA (フランス国立コンピュータ科学・制御研究所)で作成された高機能な行 列演算パッケージで、 MATLAB と高い互換性があります。 こちらは無償で入手できる配布自由なソフトウェアです

SCILAB は一言で言えば、変数に行列を代入できる C 言語のようなものです。

なお、Mupad の有償版では行列計算のパッケージとして SCILAB を組み込んだ ものもあります。

基本操作

変数は宣言せずに使用できます。 = で代入します。 変数には数(スカラー)や行列を代入できます。 行列の書式は[](角カッコ)で数を列挙します。 空白またはカンマ(,)で区切ると行ベクトル、改行または ;(セミコロン)で区 切ると列ベクトルになります。 基本的には代入した結果を表示しますが、行の最後に ; を置くと結果を表示 しません。 また式を入れるとそのまま計算します。変数名のみを入れると、変数の中身を 入れます。

例10-1

--> a=1
 a =

    1.

--> a=1;

--> a
 a =

    1.

-->

例10-2

--> b=[1 2]
 b  =
 
!   1.    2. !
 
--> b=[1,2]
 b  =
 
!   1.    2. !
 
-->c=[1
-->2]
 c  =
 
!   1. !
!   2. !
-->c=[1;2]
 c  =
 
!   1. !
!   2. !
 
-->d=[1 2
-->3 4]
 d  =
 
!   1.    2. !
!   3.    4. !
 
-->d=[1,2;3,4]
 d  =
 
!   1.    2. !
!   3.    4. !
 
-->

行の作成

1:10 のようにコロン(:)で数を区切ると、コロンの左を最初の値、右を最後 の値として 1 刻みで増やした行ベクトルを作ります。 つまり、 1:10 = [1 2 3 4 5 6 7 8 9 10] となります。 また、 1:2:9 と三つの値をコロン 2 つで区切ると、中央の値は増分として扱 われます。 つまり、 1:2:9=[1 3 5 7 9] となります。 一方、全て 0 の行列は zeros(行数, 列数) で作れます。 また全て 1 の行列は ones(行数, 列数) で作れます。

例10-3

-->a=1:10

 a  =
 
!   1.    2.    3.    4.    5.    6.    7.    8.    9.    10. !
 
-->b=1:2:9
 b  =
 
!   1.    3.    5.    7.    9. !
 
-->zeros(2,3)
 ans  =
 
!   0.    0.    0. !
!   0.    0.    0. !
 
-->ones(3,2)
 ans  =
 
!   1.    1. !
!   1.    1. !
!   1.    1. !
 
-->

またベクトルの一部を取り出すには、ベクトル名の後に()(丸括弧)を付け、丸 括弧の中に取り出したい要素の番号を記入します。 但し、 MATLAB, SCILAB では要素の番号は 1 から始まります。 丸括弧の中には値を複数、つまりベクトルを書くことも可能です。

例10-4

-->b=1:2:9
 b  =
 
!   1.    3.    5.    7.    9. !
 
-->b(3)
 ans  =
 
    5.  
 
-->b([2 5 1])
 ans  =
 
!   3.    9.    1. !
 
-->b(2:4)
 ans  =
 
!   3.    5.    7. !

-->d=[1,2;3,4]
 d  =
 
!   1.    2. !
!   3.    4. !
 
-->d(2,2) 
 ans  =
 
    4.  
 
-->d([1 2],2)
 ans  =
 
!   2. !
!   4. !
 
-->d(2,:)    
 ans  =
 
!   3.    4. !
 
-->

演算

a+b と a-b は要素間の足し算引き算になります。 a*b は行列のかけ算を示し、 a' は転置を表します。 a/b は a*(b の逆行列)、 a\b は (a の逆行列)*b を表します。 a.*b は要素毎のかけ算、 a./b、 a.\b は要素毎の割算、 a.^b は要素毎の巾乗を意味します。

例10-5

-->a=[1 2;3 4]
 a  =
 
!   1.    2. !
!   3.    4. !
 
-->b=[1 2;-2 -1]
 b  =
 
!   1.    2. !
! - 2.  - 1. !
 
-->a+b
 ans  =
 
!   2.    4. !
!   1.    3. !
 
-->a-b
 ans  =
 
!   0.    0. !
!   5.    5. !
 
-->a*b
 ans  =
 
! - 3.    0. !
! - 5.    2. !
 
-->a/b
 ans  =
 
!   1.           0.        !
!   1.6666667  - 0.6666667 !
 
-->a\b
 ans  =
 
! - 4.   - 5.  !
!   2.5    3.5 !
 
-->a.*b
 ans  =
 
!   1.    4. !
! - 6.  - 4. !
 
-->a./b
 ans  =
 
!   1.     1. !
! - 1.5  - 4. !
 
-->a.\b
 ans  =
 
!   1.           1.   !
! - 0.6666667  - 0.25 !

関数

関数は行列が与えられると、通常スカラーしか与えられてない関数でも、各要 素に対して値を求めます。 また自由に関数を定義することもできます(省略)。

例10-6

-->sin(a)
 ans  =
 
!   0.8414710    0.9092974 !
!   0.1411200  - 0.7568025 !
 

プログラムの制御 for

for はループ変数に行ベクトルを代入する式を指定すると、end までの処理に おいて、ベクトルの各要素を順にループ変数に代入して処理を行います。

プログラムの制御 if

if は式を指定し、その式が 0 でない時、 end までを実行します。 else も指定できます。

プログラムの制御 while

while は式を指定し、その式が 0 でない場合 end までの処理を繰り返します。

演習10-17

協力 音響信号処理研究室 金田先生

メモリ領域(スタック)を多めに取ります。
stacksize(10^8);
サンプリング周波数は 48000 にします。 そして、1 秒間を表す系列を作成します。
Fs = 48000;
tt = 0: 1/Fs: 1;
880Hz の sin 波 ss、雑音 nn 、sin 波と雑音の合成 ssnn を作ります。
ss = 0.6 * sin( 880 * 2 * %pi * tt);
nn = rand( 1, length(tt),'norm')* 0.1;
ssnn = ss + nn;
ss, nn, ssnn の最初の 400 点でグラフを書いてみます。
plot2d( tt(1:400), ss(1:400))
xbasc()
plot2d( tt(1:400), nn(1:400))
xbasc()
plot2d( tt(1:400), ssnn(1:400))
それぞれ音を出してみます。
playsnd(ss, Fs,32)
playsnd(nn, Fs,32)
playsnd(ssnn, Fs,32)
畳み込み演算により、ノイズを除去します。 インパルス応答として 1/20 が 20 個あるベクトルを作り、それに対して畳み 込みの計算をします。
hh = ones(20,1) /20;
yy = convol(ssnn,hh);
作った yy に対して、グラフを作成し、音を再生します。
xbasc()
plot2d( tt(1:400), yy(1:400))
playsnd(yy, Fs,32)

hh をいろいろ(ones(3,1)/3 など)変化させると、ノイズの取れ方が変わります。

参考

また、次のようにssnn の数個の点を平均することでも雑音が取れます。

zz = zeros(1,  length(ssnn)-20)
for ii = 1:length(ssnn)-N
  zz(ii) = mean(ssnn(ii:ii+20-1));
end

10-3. 配列

宣言文での初期化

C 言語では宣言文で変数の値を代入(初期化)できます。

int i=0;
float a=2.3;
char d='F';

配列

C 言語で数列を取り扱うために配列という概念が用意されています。 添字は角括弧「[ ]」で括ります。 つまり、a0, a1, a2 はそれぞれ a[0], a[1], a[2] と表します。 aiは a[i] と表します。 ただし、添字に使用できる変数は整数型(int)だけです。 さらに添字には a[i+1] や b[j*2] など、整数を値に持つ式を書くことができます。

配列を使う時、宣言は次のようにします。

int a[5];
char b[3];
float c[4];

するとそれぞれ、次の変数が使えるようになります。

a[0], a[1], a[2], a[3], a[4]
b[0], b[1], b[2]
c[0], c[1], c[2], c[3]

プログラムでは、配列はそれぞれ普通の変数として使用することができます。 但し、実行中に配列の値をまとめて代入することはできません。 一方、初期化する際には次のようにまとめて値をセットできます。

int a[5]={ 3, 4, 6, 0, -1 };

このようにすると 次のように値が代入されます。

a[0]= 3,
a[1]= 4,
a[2]= 6,
a[3]= 0,
a[4]=-1

さらに値をまとめてセットする場合、要素数の指定を省略することができます。

float b[]={ 1.3, -3e-2, -5.0 };

このようにすると次のように値が代入されます。

b[0]= 1.3,
b[1]=-3e-2,
b[2]=-5.0

演習10-18

以下の方程式 a0 + a1x = 0 ( a1≠0 ) の解を求めるプログラムを完成させなさい。 そして、以下の方程式をこのプログラムで解きなさい。

  1. 2 + x = 0
  2. 2 - 4 x = 0
  3. 0 - 3 x = 0
#include <stdio.h>
main(){
  float a[]={2,1};
  float x;
  /* x の計算 */
  /* printf("方程式 %f + %f x = 0 の解は %f\n", ... );*/
}

例10-7

配列変数の内容を表示するプログラムは次のように作ります。 まず、整数型のループ変数を用意し、値を 0 にセットします。 (ここではループ変数の名前を i とします)。 そして、 while 文の中に入ります。 ループ変数 i が配列の要素数以下の時に繰り返します。 繰返し行う処理は printf 文で配列の i 番目の要素を出力することで、 出力が終ったらループ変数 i を一つ増やします。

このようにして作成したのが以下のプログラムです。 但し、#define X Y はプログラム中で Y を X という 名前で使うことを意味します。

#include <stdio.h>
#define N 6
main(){
  float a[N]={2, 1, 5, 6, 9, 2};
  int i;
  i=0;
  while(i<N){
    printf("a[%d] の値は %f\n",i,a[i]);
    i++;
  }
}

演習10-19

以下の入力された配列変数を表示するプログラムを完成させなさい。 但し、繰返しは while ではなく for を使用しなさい。

#include <stdio.h>
#define N 6
main(){
  float a[N]={2, 1, 5, 6, 9, 2};
  int i;
  /* for( ; ; ){ */
  /* printf("a[%d] の値は %f\n",...); */
  /* } */
}

例10-8

配列変数の値の合計を得るには次のようにします。 まず、ループ変数と集計用の変数を用意します。 集計用の変数を 0 にし、ループ変数も 0 にします。 そして while 文を使い、ループ変数が配列変数の最大添字を越えない限り繰 り返します。 ここで繰返し行う処理は数列の和を求めるための漸化式 Sn = Sn-1 + an を利用して配列の各要素の値から合計値を計算します。 具体的には代入演算子 += を使用し、集計用の変数を配列変数の要素分だけ増 やします。 while 文を抜けたら、集計用の変数に合計が求まっていますので、それを出力 します。 このようにして作成したのが次のプログラムです。

#include <stdio.h>
#define N 5
main(){
  int i;
  float a[N]={2.3, 4.4, 3.9, -1.2, 0.3};
  float s;
  i=0;
  s=0;
  while(i<N){
    printf("a[%1d]=%f\n",i,a[i]);
    s+=a[i];
    i++;
  }
  printf("合計: %f\n",s);
}

なお、結果が予想と異なる場合、 float の代わりに double を使用して結果 を比較しなさい。


演習10-20

配列の平均値を出力するプログラムを作りなさい。但し、繰返しは while を 使わず、 for を使用しなさい。


10-4. 番兵

while や for の繰返しをコントロールするため、ループ変数という専用の変 数を用意し、何回繰り返したかを数えてコントロールする手法を学びました。 ここでは数を数える代わりにデータの列の中に列の終りを意味する値を付加し て、プログラムの流れを変えるというテクニックを学びます。 そのようなデータのことを番兵と呼びます。 ループ変数ではあらかじめ全体のデータの数がわからないとだめでしたが、番 兵を用いるとデータの最後をデータ自身が教えてくれるため、データの数をあ らかじめ知る必要もなく、またプログラムが簡潔になります。

演習10-21

次のプログラムを完成させ、正の値が出力されるようにしなさい。

#include <stdio.h>
main(){
  int a[]={5,3,10,2,0};
  int i=0;
  /* a[i] の値が 0 でないとき以下を繰り返す */
  {
    /* i 番目の配列の要素を表示する */
    /* i を 1 増やす */
  }
}

例10-9

配列の要素数を数えるプログラムは次のようにします。 まず、数を数える変数(カウンタ)を 0 にし、配列をアクセスする変数を 0 に セットします。 そして、while 文を使って配列の要素が番兵になるまで繰り返します。 各配列要素に対してアクセスするたびにカウンタを一ずつ増やします。 すると、全配列要素にアクセスし終るとカウンタが要素数を示すことになりま す。 最後にカウンタの値を出力します。 このようにして作成したプログラムを次に示します。

#include <stdio.h>
main(){
  int a[]={5,3,10,2,0};
  int i=0;
  int counter=0;
  while(a[i]!=0){
    counter++;
    i++;
  }
  printf("配列の要素数は %d\n",counter);
}

演習10-22

次のプログラムを完成させ、偶数がいくつあるか表示しなさい。 なお、特定の条件を満たすものを数えるには繰返し文の中に if 文を入れ、条 件が成立した要素だけカウンタを増やすようにします。

#include <stdio.h>
main(){
  int a[]={5,3,10,2,0};
  int n=0;
  int i;
  /* i を 0 にする */
  /* a[i] の値が 0 でないとき以下を繰り返す */
  {
    if(/* a[i] が偶数の時*/){
      /* 数を数える */
    }
    /* i を 1 増やす */
  }
  printf("与えられた入力中に偶数は %d 個\n",n);
}

演習10-23

番兵を使って次の正の値の列の合計が出力されるプログラムを書きなさい。

{2, 7, 1, 3, 9, 0}

演習10-24

番兵を使って次の正の値の列の平均が出力されるプログラムを書きなさい。

{2.1, 7.5, 1.2, 3.4, 9.9, 0}
ヒント

列の長さはあらかじめ決められないので、番兵が出てくるまで要素をひとつひ とつ数えて配列のサイズを求める。



10-5. 宿題

gnuplot をダウンロードしておいて下さい。


坂本直志 <sakamoto@c.dendai.ac.jp>
東京電機大学工学部情報通信工学科