第 9 回 配列、文字列

本日の内容


9-1. Mupad

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

基本操作

Mupad では求めたい式を記述してそのまま Enter を押すだけです。 % (あるいは %1 )は直前の結果を指します。%2 は 2 個前の結果を、 %3 は 3 個前の結果を指します。

整数、有理数

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

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

演習9-1

次の計算をしなさい

  1. 1/3+1/6
  2. 100!

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

例
isprime(7)
ifactor(18)

演習9-2

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


実数

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

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

演習9-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))

演習9-4

次の計算をしなさい。

  1. (√2 + √3)2
  2. (1+√2)10

演習9-5

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

  √3 + √2
-------------
  √3 - √2

複素数

虚数単位 i=√(-1) は Mupad では「I」で表します。 実部と虚部に分離する関数は rectform です。

例
rectform((-1)^(1/3))
(これは x2+x+1=0 の解の一つ)

演習9-6

次の値を求めなさい。

  1. √(-1) の平方根
  2. (2+I)10
  3. e

多項式、方程式

多項式を因数分解するには 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})

演習9-7

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

  1. a3+b3+c3-3abc

演習9-8

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

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

演習9-9

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

ヒント

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)))

演習9-10

次の関数を微分しなさい

  1. sin x
  2. 1/x
  3. exsin x

演習9-11

次の関数を積分しなさい

  1. sin x
  2. 1/x
  3. exsin x

演習9-12

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

  1. y'=x, y(0)=1
  2. y'=xy, y(0)=1
  3. y'=1/y, y(0)=1

グラフ

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

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

演習9-13

次のグラフを書きなさい

  1. y=tan(x) (gnuplot と出力を比較しなさい)
  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)

演習9-14

次の値を求めなさい。

  1. 1 + 3 + ... + (2n-1)
  2. 12 + 22 + ... + n2
  3.  1     1     1
    --- + --- + --- + ...
     12    22    32
    
  4. 次の式の n を無限大にした時の極限
          x
    (1 + ---)n
          n
    
  5.      1      1      1
    1 + ---- + ---- + ---- + ...
         1!     2!     3!
    
  6. xxの x を 0 に近付けた時の(右)極限

ベクトル、行列

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

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)

演習9-15

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

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

演習9-16

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

ヒント

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

 1
--- * (aの長さ) * (bの長さ) * sin(abのなす角)
 2

9-2. 配列

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

演習9-17

下のプログラムを a0+a1x+a2x2=0 (a2≠0)を解くように完成させなさい。 また、次の方程式を解きなさい。

  1. 2x2-5x-3=0
  2. x2+2x+1=0
  3. x2-2x+5=0
#include <stdio.h>
#include <math.h>
main(){
  float a[]={2,-3,1};
  float d;
  float x0,x1,re,im;

  /* 判別式を計算 */

  printf("方程式 (%f) x^2 + (%f) x + (%f) = 0 の解は",a[2],a[1],a[0]);
  if(/* 判別式が正 */){
    x0=(-a[1]+sqrt(d))/(2*a[2]);
    /* x1 を計算 */
    printf("%f, %f\n",x0,x1);
  }else{
    if(/*判別式が 0 */){
      /* x0 を計算 */
      printf("%f\n",x0);
    }else{
       if( /* 判別式が負 */ ){
         re=-a[1]/(2*a[2]);
	 /* 虚部 im を計算 */
	 printf("%f ± %f i\n",re,im);
       }
    }
  }
}

演習9-18

次のプログラムを完成させなさい。また、改造して平均値も出力するようにし なさい(#define X Y はプログラム中で Y を X という 名前で使うことを意味する)。

#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=i+1;
  }
  printf("合計: %f\n",s);
}

演習9-19

以下の数の標準偏差を求めなさい。 但し標準偏差は次の式で与えられる。

標準偏差 = √分散

       Σ(素点 - 平均)2
分散 = ----------------
               n
3.5, 2.0, 5.5, 2.3
ヒント

分散は次の式を使うと while 文一つで求めることができる。


       Σ(素点 - 平均)2
分散 = ----------------
               n

        Σ((素点)2 - 2×素点×平均 + (平均)2)
     = -----------------------------------
                      n

        Σ(素点)2            Σ素点             Σ 1	
     = --------- - 2×平均×-------- +(平均)2×------
           n                   n                n

     = ((素点)2 の平均) - (平均)2

演習9-20

以下の数の偏差値を求めなさい。 但し偏差値は次の式で与えられる。

          (素点 - 平均)
偏差値 = --------------- × 10 + 50
             標準偏差
3.5, 2.0, 5.5, 2.3

演習9-21

以下の数のペア(xi, yi)が、方程式 y= ax+b をほぼ満たす関係がある時、この a, b の値を最小二乗法を 使って求めなさい。

(2.3, 4.5), (1.1, 2.4), (5.5, 10.9), (4.9, 10.0)
ヒント

最小二乗法によると、 a と b は次の連立方程式を満たす。

xi2) a + (Σxi) b = Σ(xiyi)
(Σxi)  a +     n b = Σyi

9-3. 番兵

データの列の中に、列の終りを意味する値を付加してプログラムの流れを変え るというテクニックがあります。 そのようなデータのことを番兵と呼びます。

演習9-22

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

#include <stdio.h>
main(){
  int a[]={5,3,10,2,0};
  int i=0;
  /* a[i] の値が 0 でないとき以下を繰り返す */
  {
    printf("%d\n",a[i]);
    i=i+1;
  }
}

ループ変数の値をチェックする繰り返しと比較して、 番兵を使用する場合、 データの個数を知らなくても良いので、プログラムを書く自由度が増えます。

演習9-23

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

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

演習9-24

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

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

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


9-4. 文字列

C 言語で文字列を扱うには、文字型の配列を使います。 ただし、配列の終りには配列の終りを示す(番兵)特別な文字\0を入 れます。従って、配列にあらかじめ文字列を入れるには次のようにします。

char x[]={'a','b','c','\0'};

ただし、文字列の扱いを簡単にするために、これを次のように書くことが可能 です。

char x[]="abc";

このように書くと配列変数 x の値は次のようになります。

x[0]='a', x[1]='b', x[2]='c', x[3]='\0'

二重引用符で括られた文字列を並べると一つの文字列として扱われます。 長い文字列を記述する場合、つぎのようにします。

char x[]="abc"
         "def"
         "ghi";

この宣言は char x[]="abcdefghi"; と同じになります。 なお、このような文字列の一括の代入はあくまでも宣言文の中でのみ有効で、 プログラムの実行中はできません。 プログラム中はひと文字ずつしか取り扱えません。 文字配列 x, y に対し、 x=y はできません(エラーにならず誤動作します)。

文字を画面に表示するには printf("%c",x[0]); などと書きますが、文字配列 を一括して画面に表示するには %s を使用します。 printf("%s",x); と %s に対して文字配列名を指定します。 これは、次のプログラムと同じ処理が行なわれます。

int i=0;
while(x[i]!='\0'){
  printf("%c",x[i]);
}

演習9-25

次のプログラムを完成させなさい。 また、文字列を "ABCDEF" に替えて実行しなさい。

#include <stdio.h>
main(){
  char x[]="abcdefgh";
  int y=0;
  while(x[y]!='\0'){
    printf("文字列 %s の %d 文字目は %c\n",?,?,?);
    y=y+1;
  }
  printf("全部で %d 文字\n",?);
}

演習9-26

文字列の長さを出力するプログラムを書きなさい。 文字列 "abcdefg" などで試しなさい。


演習9-27

次のプログラムを完成させなさい。 また、文字列を "ABC" と "DEF" に替えて実行しなさい。

#include <stdio.h>
main(){
  char x[]="abcdef", y[]="ghijkl", z[100];
  int i,j;
  printf("文字列 %s と文字列 %s をくっつけると",x,y);
  /* z に x をまずコピーする */
  j=0;
  while(y[j]!='\0'){
    z[i]=y[j];
    i=i+1;
    j=j+1;
  }
  z[i]='\0';
  printf("%s\n",z);
}

演習9-28

文字列が与えられた時、先頭の 3 文字だけを表示するプログラムを書きなさ い。 作ったプログラムを文字列 "abcde", "ABC", "X" などで試しなさい。


演習9-29

文字列が与えられた時、先頭の空白の文字数を数えるプログラムを書きなさい。 "a○b○c" なら 0, "○○def" なら 2 が出力されるようにしなさい(○は空白 を表す)。


演習9-30

文字配列 x 中に含まれている文字変数 y の文字の使用頻度を求めるプログラ ムを書きなさい。 例えば、 x[]="This is a pen.", y='i' ならば、 2 を表示しなさい。


演習9-31

文字列が与えられた時、縦書きに表示するプログラムを書きなさい。 "abcdefg" を縦書きにしなさい。


演習9-32

文字列が与えられた時、逆向きに表示するプログラムを書きなさい。 "abcdefg" が与えられた時、「gfedcba」と表示されるようにしなさい。


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