ソフトウェアとは

公開: 2021年7月17日

更新: 2021年8月8日

あらまし

ケンブリッジ大学で数学を教えていたバッベージが、歯車式の計算機械を作った時、細かな計算手順を機械に指示するために、バッベージは、当時、木綿糸を複雑に縫い合わせて織る、レース織機に模様の織り方を指示する方法として採用されていたパンチカードを使うことにしました。このジャガード織機に模様を支指示方法は、日本でも京都の西陣織や、福岡県の博多織で使われています。

計算の途中結果を蓄えている記憶装置から、その値を読出し、それを次の計算をするための基となる値として使い、それをどのように処理して次の途中結果にするのかの計算過程を機械に指示する必要があります。この計算の手順を、パンチカードに開けられた穴の模様で指示するのです。開けられた穴の並び方で、どの記憶装置の値を読みだして、それをどのように計算して、その結果をどの記憶装置に格納させるかなどを指定します。

この計算のステップを長く重ねることで、複雑な計算も、単純な機械で行うことができると、バッベージは考えたようです。その仕事を引き受けたのが、貴族で、有名な詩人だった、バイロン卿の娘で、ラブレス公の夫人だった、エイダ・アウグスタ・バイロンでした。当時、エイダは、10代だったとされています。競馬が趣味であった、このラブレス公婦人は、新しく作られる計算機械を使って、競馬の賭に勝つための計算をしようとしていたのかも知れません。

現代社会で、エイダが「世界最初のプログラマ」と呼ばれているのは、このためです。しかし、実際にエイダが世界最初のプログラムを作成し、それを動かすことはできませんでした。それは、バッベージが計算機械の試作に失敗したからでした。バッベージが考えたような計算機械を世界で最初に作り上げたのは、バッベージの失敗から百数十年後のアメリカで、ハーバード大学のエイケンが作った電気式計算機械でした。

エイケンが電気式計算機械を作った後、数学者たちは、「抽象機械」と名付けられた仮想の計算機械を定義して、その機械を使って、どのように計算ができるのかを考えるようになりました。例えば、「与えられた数の平方根」をどう計算するのか、などの問題です。このような問題は、500年以上前から、アラビアの数学者たちによって研究されていた、「方程式の解を求めるやり方」などでした。

コンピュータを使った計算とその方法

第2次世界大戦が終わって、ペンシルベニア大学で世界最初のコンピュータが開発された当時、コンピュータに計算手順を指示するための方法は、次のようなものでした。例えば、足し算器に足すべき2つの数値を入力し、足し算した結果を取り出し、次の計算を行うために他の計算装置の入力にしたり、記憶装置に蓄えさせるためには、それらの装置(実際には真空管で動く回路)や装置間を直接的に電線で結びつける方法でした。このやり方は、必要に応じて異なる計算ができると言う意味では、便利な方法でしたが、長い計算手順を指定するための方法としては、複雑で、計算の指定を行うために時間がかかり、間違いも起こりやすい方法でした。

コンピュータの普及のためには、このような計算手順の指定仕方として、バッベージは、産業革命当時、イギリスで複雑なレース編みができるようにするために考案された、厚いボール紙に穴をあけたパンチカードを糸で結んでつなげた、ジャカード編み機の織模様指定法のやり方を採用しました。この方法にならって、紙のテープに穴をあけて計算手順を指定し、それをテープの読み取り装置に読み込ませる方法が普及しました。テープの最初と最後をつなげて、輪にすると、同じ計算をするプログラムを必要な回数だけ、繰り返し実行することもできます。

この紙テープ上に記録されるプログラムでは、コンピュータのどの装置を使うか、例えば、レジスタと呼ばれる計算の途中結果を保持させる高速な記憶装置のどれを使うかを指定するか、レジスタに蓄えられている値をどの記憶装置に記憶させるか、どの記憶装置からどのレジスタに値を呼び出すのかなどを指定する、機械語命令と言う形式が採用されていました。このやり方でプログラムを記述しようとすると、簡単な計算でもかなりの長さのプログラムになります。例えば、二つの値を足し算する場合、(1)指定した記憶装置からレジスタに値を呼び出し、(2)別のレジスタに別の記憶装置から別の値を読出し、(3)これら2つのレジスタに蓄えられている値を足して、その結果を最初のレジスタの値として蓄えさせ、(4)そのレジスタに蓄えられている足し算の結果を、指定した記憶装置に蓄える、と言う計算手順になります。つまり、a + b = cの簡単な計算でも、4ステップの手順から成るプログラムになってしまいます。

機械語によるプログラムは、この例から分かるように、簡単な計算でもとても長い記述になります。この問題を解決するために、1950年代の終り頃、数学や物理などの計算向けに、FORTRANと名付けられた高級言語が開発されました。この例の場合、FORTRANであれば、『c=a+b』と書くだけで、コンパイラと呼ばれる翻訳プログラムが、それを機械語に翻訳し、上で説明したような機械語のプログラムに変換してくれます。これによって、プログラムを書くことが、容易になり、様々な人がプログラミングの仕事に就けるようになりました。このような方法は、それ以後のC言語などにも踏襲され、C言語であれば、『c=a+b;』と書けば良いのです。最後の『; (コロン)』記号は、C言語などで、1つの単位の計算の終わりを、コンパイラに伝える特殊な記号です。日本語の文の最期を示す『。』記号に似たものです。

プログラムを書く時、記憶域は、長いプログラム自身をコンピュータに読み込むためにも使われます。それだけではなく、計算の途中結果を蓄えるための、算数や数学で言えば、「変数」に相当するもののためにも使われます。メモリとして使える記憶域が大きくなることは、大きなブログラムが使う、大量の変数のための記憶域を利用できることになります。このため、プログラミングを考えるとき、それを行う人(プログラマ)は、記憶域の制限など、あまり余計なことに気を使わなくて良くなり、プログラム作成時に人が犯してしまう間違いを減らすことができるようにもなりました。。

大規模集積回路の利用が可能になったため、現在では、数百ギガ・バイト(ギガ・バイトはメガ・バイトの約千倍)にまで拡大しています。このため、大規模なプログラムでは、数千万行にもなるプログラムも出てきています。コンピュータは、それほど大規模なプログラムでもコンピュータ上で動かすことができるようになっています。このことは、これまで電子回路でやっていたことや機械でやっていたことを、同じことをプログラムの計算に置き換えられる場合があることを意味しています。ただし、これは人間が間違えることなく、そのような大きなプログラムを書くことができることが前提です。数十万行を超えるプログラムを、最初から誤りなく書くことは、現実的には不可能です。

例えば、与えられた実数xの平方根yを計算することを考えましょう。ある数、xの平方根yは、x=y×yが成り立つ数yを見つけることと同じです。つまり、x−y×y=0の方程式を解くことと同じことになります。コンピュータで計算する場合は、適当なyを与えて、xy×yを計算し、そのyの値を少し変化させて、同じ計算を行い、xy×yの答えが最初のyの時よりも小さくなるかどうかを調べる。小さくなっていれば、yの値を同じような方法でさらに小さくする。そうでない場合は、逆にyの値を最初の計算よりも大きくする。このような計算を繰り返してx−y×yの答えが、ほぼゼロになるまで繰り返します。例えば、yの値が大きすぎると、y−x×xの値は、ゼロより大きくなります。そのときは、yを1回前の計算の時より1/2ずつ小さくしてゆきます。逆に、yの値を大きくする場合には、1回前の値の2倍にしてゆく方法が考えられます。

最初の計算で使うyをxに等しい値とすると、プログラムは、次のようになります。(1)yの最大値をxと同じ値にする。(2)yの最小値をゼロとする。(3)yの値をyの最大値と最小値の中間の値に設定する。そのyの値で(4) z=x−y×yを計算する。(5)zがほぼゼロであれば計算を終わらせる。(6)zがゼロより小さければ、次の計算のためのyの最大値を現在のyの値とする。もし、(7)zがゼロより大きければ、次の計算のためのyの最小値を現在のyの値とする。そして、(3)へ戻って、(3)から(7)までの計算を繰り返す。以上が簡単な平方根計算プログラムのあらましです。

例えば、xが2.0の場合、最初の計算のためのyは、2.0とゼロの中間の1.0となります。すると、zは、2.0-1.0=1.0で、1.0となり、yの最小値は計算に使ったyの値であった1.0となります。したがって、次の計算のyの値は、最大値2.0と新しい最小値である1.0の中間の値である2.0+1.0=3.0を2で割った1.5となり、このときzは2.0−1.5×1.5= -0.25となる。この結果から、zがゼロより小さいので、次の計算で使うyの最大値を1.5に変更する。このことから、次の計算で使うyの値は、最大値の1.5と最小値1.0の中間の値である1.25となる。このyの値を使ってzを計算すると、2.0-1.5625=0.4375となる。今度は、zがゼロより大きい値なので、次の計算で使うyの最小値を1.25として、最大値1.5と最小値1.25の中間の値、すなわち、1.5-1.25=1.375が次の計算のyとなり、z=2.0-1.890625=0.109375となる。

この計算を続けると、次の計算では、yの最小値は1.375で、新しいyの値は1.4375となる。このとき、zは、-0.66406…となり、今度はzがゼロより大きな値となる。そのため、今度はyの最大値を1.4375として、新しいyを求めると、1.4375と1.375の中間の値である1.40625が得られる。これを新しいyの値としてzを計算しなおすと、zは0.022460..となり、ゼロよりも大きな値となる。この結果に基づいて次のyを求めると、1.421875が次のyの値とな、その時のzの値は、-0.021728...となり、ゼロよりちいさな値となる。このような計算を繰り返すと、yの値は、1.4140625、1.41796875と変化し、zの値は、0.00042725、-0.01063538となってゆく。この計算を繰り返せば、zの値はゼロに近づいてゆく。ところで、2.0の平方根の値は、1.414213…である。

上に示した計算法は、yの値を、最大値と最小値の中間の値にする方法であるが、計算の途中でyの値が、答えの1.414に近づいたにも関わらず、次の計算でzの絶対値が大きくなっている。これは、ここで使った方法が十分に優れた方法とは言えないことを示している。万有引力を発見したニュートンは、平方根の計算などで、この方法よりも速く正しい答えに近づく方法を考え出している。それは、今日のコンピュータを使った計算や、電卓の計算などで使われている、もう少し複雑なやり方である。

この平方根の計算からも理解できるように、コンピュータを使って、高速に計算を行うためには、その計算の仕方を考え、プログラミング言語でそのやり方を書き、そのプログラムを翻訳プログラムを使って、コンピュータがそのやり方で計算を行える機械語のプログラムにしなければ、コンピュータを使って計算を行うことはできない。翻訳プログラムも上に示した平方根を計算するのと同じように、その翻訳のやり方を考えて、機械語で書き直した巨大なプログラムである。

自分が考えた計算を、自分が考えた計算方法で、コンピュータが正しく計算できるようにするためには、まず、自分の考えた計算そのものが、正しくなければなりません。さらに、自分が考えたその計算を行うための計算の手順も、正しい計算の仕方に沿ったものでなければなりません。その正しい計算の手順に従った計算を、プログラミング言語で正確に書き、その自分がプログラミング言語で書かいたプログラムを、翻訳プログラムが自分の思った通りに機械語に翻訳して、間違いのない機械語のプログラムを作り出すことが前提となります。その正しい機械語のプログラムを、コンピュータが自分が思ったように計算しなければ、正しい計算結果は得られません。

自分が書いたプログラムは、もちろんのこと、翻訳プログラムにも間違いがありえることは簡単に理解できます。さらに、コンピュータ自身の設計やその組み立ての過程で、人間が起こす間違いが入り込むことを、完全に防ぐことはできません。コンピュータの設計をするのも、コンピュータを作るのも人間だからです。人間は、時々、間違えるからです。長い経験を積んだ技術者は、自分が間違いをしないようにするために、何に注意をすべきか、何をしっかりと確認すべきかを知っています。とは言え、人間が起こす間違いをゼロにすることはできないのです。

(つづく)