] > gnuplot, String

第 11 回 Gnuplot, 文字列

本日の内容


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

11-1. Gnuplot

gnuplot はグラフを作成するためのツールです。 名前は gnu とついていますが、GNU とは配布ライセンスは異なる形態です。

Mupad や Scilab もグラフを書くことはできました。しかし、Mupad ver. 2.5.3 では対数グラフのプロットにバグがあり正確なグラフが書けません。 また SCILAB では tanx のような不連続点のあるグラフは書けません。 そのため、周波数特性などのグラフを書く際に、 gnuplot が必要になります。

基本操作

二次元のグラフを書くには plot を使用します。 変数は x を使用して、関数をそのまま書きます。 複数のグラフを重ねるには関数を「,(カンマ)」で区切ります。 また、 x 軸の範囲、 y 軸の範囲を設定するには [ ] の中に範囲を「:(コロ ン)」で区切って書きます。

演習11-1

次のグラフを書きなさい

  1. plot sin(x)
  2. plot sin(x),cos(x)
  3. plot [0:2*pi] sin(x)
  4. plot [0:2*pi][0:1] sin(x)
  5. plot [][:0] sin(x)

応用

gnuplot は複素数なども扱うことができます。 ここでは電気回路、電子回路で重要となる周波数特性のグラフを例題を通して 学びます。

交流回路

コンデンサは電圧に比例した電気を蓄えることができます。

Q=CV

蓄えた電気は電流を積分したものになるため、電圧は電流を積分した値に比例 します。

V=1C It

一方、コイルでは流れる電流が作る磁束を維持するように、電流の変化に逆ら うように電圧が発生します。 つまり、逆向きに発生する電圧は電流の微分に比例します。

V= L I t

従って、抵抗の他にコンデンサやコイルを含む回路において、電圧と電流の関 係は、一般には微分方程式によって表されます。

しかし、次のようなことを考えると電圧と電流の間にオームの法則と似た法則 が成り立つことになり、従来の抵抗のみの回路と同様にして回路の性質を調べ ることができるようになります。

  1. 回路に流れる電流を単一の周波数に固定する。 周波数 f に対して角周波数 ω = 2 π f が良く使われる。
  2. このとき回路に流れる電流は正弦波として I = I0 cos ω t として表されるが、これを複素数に拡張して次のように表す ( j= -1 )。

    I = I0 j ω t

    このようにすると、微分積分に関して関数の形が変わらず、単に j ω の係数の乗除だけになる。

  3. 従って、電圧 V と電流 I の関係はコンデンサに対しては静電容量を C とす ると 次の関係になる。

    V = 1 j ω C I

    一方、コイルに対してはインダクタンスを L とすると次の関係になる。

    V = j ω L I
  4. ここで抵抗に関しては R , コンデンサに関しては 1 j ω C , コイルに関しては j ω L インピーダンス と呼びます。 インピーダンスは周波数をパラメータに持ち、複素数になりますが、従来のオー ムの法則と同様の法則が成り立ちますので、従来と同じ手法で回路の解析が使 えます。

直列回路 例えばインピーダンスが Z1 , Z2 の部品からなる直列回路があったとします。 すると、キルヒホッフの原理は成り立ちますので、二つの部品を流れる電流は 等しくなります。従ってどちらの部品も流れる電流を I とします。 するとそれぞれの部品で発生する電圧(電圧降下)は Z1 I , Z2 I となります。 Z1 , Z2 間の電圧と Z2 間の電圧の比は単純に次のようになります。

V12 : V2 = Z1 I + Z2 I : Z2 I = Z1 + Z2 : Z2

例11-1

例えば次の回路の周波数特性を考えます。

low pass filter

抵抗値を R, コンデンサの容量を C とした時、入力電圧に対する出力電圧の 比は、それぞれのインピーダンス R, 1/jωC の比に電圧を分けたものになり ますから次式で得られます。

1j ωC R+ 1j ωC = 1 1+ j ω C R

ここで、 CR=1 の時、ωに関する周波数特性を示す式をグラフにしてみましょ う。 つまり、 1/(1+ j x) というグラフを書くということです。 gnuplot では j = -1 を {0,1} で表します( 0+ 1*j の意味です)。 従って、周波数特性のうち電圧の大きさの比は abs(1/(1+{0,1}*x)) と書けます。 一般に、周波数特性を調べる時は横軸、縦軸とも対数目盛を使用します。 また、求める周波数の範囲ですが、今回は 10-5 から 105 まで求めましょう。 gnuplot で巾乗は ** で表します。 求める周波数特性のグラフは次のようになります。

set logscale xy
plot [10**-5:10**5] abs(1/(1+{0,1}*x))

一方、偏角を求めるには arg 関数を使用します。 偏角の場合縦軸は対数目盛ではないので、 logscale を解除します。

set nologscale y
plot [10**-5:10**5] arg(1/(1+{0,1}*x))

対数目盛を全て解除するには set nologscale xy として下さい。

演習11-2

上記の回路において、C=10μF, R=1kΩ の場合のグラフと、CR=1 の場合のグ ラフを重ねて書きなさい。 但し、 μは 10-6 を、 k は 103 を表します。

演習11-3

次の回路において入出力電圧の比と偏角の周波数特性をグラフに表しなさい。 なお、複素数で表した場合の入出力電圧比は R R + 1 j ω C = j ω C R 1 + j ω C R になります。 但し、グラフを書く際、コンデンサの容量と抵抗値の積は 1 とします。 また、更に全問同様、上記の回路において、C=10μF, R=1kΩ の場合のグラフと、CR=1 の場合のグラフを重ねて書きなさい。

low pass filter

演習11-4

次の回路においてインピーダンスの大きさと偏角の周波数特性をグラフに表し なさい。 なお、複素数で表した場合のインピーダンスは次のようになります。

1 1R + j ω C + 1 j ω L = R 1+ j Q ω ω0 - ω0 ω
ω0 = 1 L C , Q = R C L

但し、グラフを書く際、LC = 1, R = 1 として、 Q= 0.1, 1, 10 のそれぞれ に関してグラフの形を調べなさい。

LCR

数値入力

演習11-5

次のような値を入れたファイルを作り、c:\work\testdata という名前で 保存しなさい。

0 3
4 5
10 7

そして、 gnuplot で次のようにしてグラフを描きなさい。

  1. cd 'c:\work'
  2. plot "testdata"

ファイル出力

なお、TeX のドキュメントに入れる画像を作るには、 EPS ファイルで出力し ます。次のようにすると detafile というファイルからデータを読んでプロッ トしたグラフを graph.eps というファイル名で白黒の EPS ファイルに保存し ます(カラーの出力にする場合は monochrome を color に変更します)。

set terminal postscript eps monochrome
set output "graph.eps"
cd 'c:\work'
plot [0:][0:] "datafile", 0.5*x+1

パラメータ

set parametric とすると、媒介表示モードになります。 set noparametric とすると元に戻ります 媒介表示モードでは、t の関数を二つ書くことにより、 t の値により座標を 定めグラフを書きます。

演習11-6

次の媒介変数による関数のグラフを書きなさい。

  1. t,t
  2. cost , sint
  3. cos6t , sin5t

三次元グラフィック

gnuplot では三次元グラフィックを表示させるのに、 splot 命令を使用しま す。

演習11-7

次の関数のグラフを書きなさい。

  1. x2+ y2
  2. sin x2+ y2

また set parametric とすると、 u, v を媒介変数とした関数のグラフを書く ことができます。

  1. cosu , sinu cosv , sinu sinv
  2. 3+ cosu cosv , 3+ cosu sinv , sinu

11-2. 番兵(復習)

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

演習11-8

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

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

例11-2

配列の要素数を数えるプログラムは次のようにします。 まず、数を数える変数(カウンタ)を 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);
}

演習11-9

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

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

演習11-10

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

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

演習11-11

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

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

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


11-3. 文字列

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 と書いても x に y の値をコピーすることには なりません(エラーにならず誤動作します)。

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

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

演習11-12

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


演習11-13

? マークの部分を書き換えて、次のプログラムを完成させなさい。 また、文字列を "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",?);
}

演習11-14

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


演習11-15

次のプログラムを完成させなさい。 また、文字列を "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);
}

演習11-16

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

ヒント

「文字が \0 かまたは 3 文字を越えた時」終了させるには次のように論理演算を 使います。

  while((文字が '\0' でない)&&(3 文字以内))

演習11-17

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


演習11-18

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


演習11-19

文字列が与えられた時、二文字出力しては改行するように表示するプログラム を書きなさい。 そして、次の文字列で試しなさい。

  1. "a"
  2. "abcd"
  3. "abcde"

演習11-20

文字列が与えられた時、最初の文字から一文字おきに表示するプログラムを書 きなさい。 "abcde" なら出力は ace になります。 そして、次の文字列で試しなさい。

  1. "a"
  2. "abcd"
  3. "abcde"

演習11-21

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


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