next up previous
Next: 3 再帰 Up: 計算天文学 II 第2回 C++言語入門 Previous: 1 今日の予定

Subsections


2 配列

Fortran でも C/C++ でも、数値計算をする上で基本になるデータ構造は多く の場合に配列である。

// calculate_sum
#include <iostream>
using namespace std;

int main()
{
    int  n ;
    int  data[100];
    int  i,sum;
    cout << "How many numbers you want to input?";
    cin >> n;
    for (i=0;i<n;i++) cin >>data[i];
    sum = 0;
    for (i=0;i<n;i++){
        sum += data[i];
        cout << "data[" << i <<"]=" << data[i] << " sum=" << sum << endl;
    }
}

これは配列を使う簡単な例である。

int  data[100] ;
と変数宣言で書くと、100個の整数型変数ができ、それぞれが data[0] から data[99] までといった風に、番号で指定して操作 できる。

これはもちろん Fortran で

      integer data(100)
と書くのとほとんど変わらないが、どうでもいいような違いがいくつかある。

括弧はまあなんでもいいが、添字が 0 からになるのは C/C++ の他の多くの言 語とは違った特異な点である。なお、 Fortran の場合は、例えば
      integer data(0:100)
といった具合に「最初と最後」を指定できるので、実は 1 から始めないとい けないというわけではない。しかし、 C/C++ の場合には必ず 0 から始まり、 それを変更する方法はない。

2.1 多次元配列

宣言はFortran では

      integer data(100,100)
とか書いたが、C/C++ では
int data[100][100];
と書く。使い方も同様に Fortran では data(i,j) のところを data[i][j] と書く。次元が増えても同様である。 Fortran では配列の次元 は 7 次元までとかいう制限があるが、 C/C++ では特に制限はない。

なお、他にも細かく違うところはある。実用上で大事なのは、メモリ上にデー タがしまわれる順番である。 1 次元配列ではもちろん配列要素が順にメモリ 上におかれるが、2次元配列では 2 つの可能性がある。つまり、最初の要素 data(1,1) あるいは data[0][0] の次に来るのがなにか?であ る。 Fortran では data(2,1) が来るが、C/C++ではdata[0][1] が来る。この違いは、いろいろな場面で意識する必要がある。

なお、このC の言語仕様で定義された普通の配列に関する限り、 C/C++ の多 次元配列には Fortran のそれに比べた大きな欠点がある。 それは、関数の引数として配列を渡す時に、大きさを変数として渡すことがで きないということである。つまり、 Fortran では

      subroutine sub1(a, n, nn)
      integer n, nn
      real*8 a(nn,nn)
      .....
といった形で、次元ごとの大きさを引数として渡すことができる。このために、 汎用の行列計算や連立一次方程式の解法などのサブルーチンを準備するのが容 易である。ところが、 C/C++ では引数としては渡せないので話が面倒になる。 もっとも、Cの最新の言語仕様ではこれが可能になっているが、現在のところ存在 するほとんどのプログラムはこの機能を使わないで書かれている。

もっとも、これはあくまでも汎用のライブラリを準備し、しかもそれをソース プログラムとしてではなくあらかじめコンパイルしたいわゆるバイナリライブ ラリとして準備する時にだけ問題になることではある。従って、自分でプログ ラムを書く場合にはあまり問題にはならない。

コンパイルしたライブラリを作るにはいろいろな方法がある。 C++ では、ユー ザーが新しいデータ型を定義することができるので、自分で定義してしまえば Fortran の配列よりももっと便利に使えるものができる。また、既に人が作っ た便利なものがいろいろあるので、高度なことをするにはそういったものの利 用を考えるベきである。

2.2 ソート

配列を使う例だが、 計算天文学とはいえ、数値計算ばかりでも芸がないので、ここではちょっと違 うこともやってみよう。

// sort.C

#include <iostream>
using namespace std;

double data[100];

int main()
{
    int n;
    int   i,j;
    cin >> n;
    for (i=0;i<n;i++)cin >> data[i];
    cout << "Input data\n";
    for (i=0;i<n;i++) cout << data[i]<<endl;
    for (i=0;i<n-1;i++){
        for (j=i+1;j<n;j++){
            if (data[i] > data[j]){
                double  work;
                work = data[i];
                data[i] = data[j];
                data[j]= work;
            }
        }
    }
    cout << "Sorted data\n";
    for (i=0;i<n;i++) cout << data[i] << endl;
    return 0;
}

このプログラムは、入力した数を小さい順に並べ変えて出力する。並べ変える 原理は、以下のようなものである。

  1. まず、一番小さい数を先頭に持ってくる。そのために、配列の2番 目の要素から最後の要素までを順に見て、それが先頭の値よりも小さければ2 つを入れ換える。

  2. これで先頭にもっとも小さい数が来た。次に、2番目の位置に残りの 数の中で最小のものを持ってくる。これには、配列の3番 目の要素から最後の要素までを順に見て、それが2番目の値よりも小さければ2 つを入れ換える。

  3. 以下、同様に、 N-1 番目まで順にやっていく。

このプログラムを実行するには、まずデータファイルを別にエディタで作って おく。これは先頭にデータ数を書き、後は一行に一つ数字を書いておけばいい。このデータファイルの名前 を sample.dat、コンパイルしてできた実行ファイルの名前を simple_sort とすれば、

simple_sort < sample.dat
とシェルウインドウで入力すれば結果が得られるはずである。

注意

コンパイルして作るプログラムの名前には sort は使わな いこと。これは、この名前の別のプログラムがあらかじめ準備されていて、そ ちらが実行されてしまうからである。もちろん、 ./sort と指定すれば 自分で作ったものが実行されるが、いずれにしても混乱する可能性がある。

2.3 練習

  1. ソートのプログラムを手続きを使う形に書き直してみよ。メ インプログラムは手続きを呼 ぶだけの形になるようにすること。

  2. ソートのプログラムについて、データの数を非常に大きくした時、実行時間がどうなるかを測定せよ。配列宣言の数字を大き くするのを忘れないこと。また理論上どうなるはずか考えてみよ。

なお、あるプログラムの実行時間を計るには time コマンドが使える。

time simple_sort < sample.dat
という風に入力すると、実行結果のあとにもう一行
0.040u 0.080s 0:00.56 21.4% 0+135k 2+0io 21pf+0w

というような出力が出てくる。この最初の数字が秒単位の実行時間である。な お、実際にかかった時間はもっと長いことがあり得る。これは、ファイルの読 み書きの時間は正確には入らないこと、また何人かで1台の計算機を使ってい るとその分余計に時間がかかることによる。



Jun Makino
平成18年10月15日