wiki:algorithm:素数を表示してみる

素数を表示する(前章)

プログラムと言えば数学です。数学の中で法則性が無いと言われている素数の出し方について考えてみましょう。
たとえば1000万までの数の中から素数であるものを見つけて、素数一覧を出力するプログラムを考えます。

  • 1以外の自然数
  • 約数は1と自身の数しかない

素数の定義は簡単です。ですが、機械的に探す(プログラムで探す)というのはとっても難しいですね。まずは、100までの数でうまくできるかを考えていきます。素数の定義に従えば、他の数で割り切ることができれば、素数ではないとなります。
ですから、100までの数を並べて他の数で割り切るれた数を消していけば残った数が素数となります。

プログラムを書くときに一番大事なのはアルゴリズムです。アルゴリズムというのは、実現するための方法論です。先ほどのように「100までの数を並べて他の数で割り切るれた数を消していけば残った数が素数」というのを、どのように実現するかを考える必要があります。
プログラムを作るには、いろいろな制約があります。少なくとも人間の感覚論で「ぬぬぬ、これは素数である」という経験則をプログラムする1)ことはできません。
プログラムの制約を以下に示します。

  • 順番に1つずつしか処理できない。2)
  • 処理を枝分かれさせるためには、枝分かれの判断基準を明記しないといけない。
  • 何度も同じ処理を実行するには、何回実行するかを明記しないといけない。

ここでいう「処理」というのは、日本語でいうところの動詞です。例えば、先ほどの「100までの数を並べて他の数で割り切るれた数を消していけば残った数が素数」を動詞ごとに1文に分けると下のようになります。

  1. 100までの数を並べる
  2. 他の数でわる
    1. 割り切れたら消す
    2. 割り切れなかったら残す
  3. 残った数を表示する

このように、処理したいことを動詞ごとに分けると、必要な処理が見えてきます。
ただ、ここで問題なのが、プログラムで処理できる動詞というのは限られているということです。一般的なプログラム言語では以下の動詞しか用意されていません。

  • 足す
  • 引く
  • かける
  • わる
  • 一時保存できる変数を用意する3)
  • 一時保存できる変数に代入する

これを見ると、ほぼ電卓ですね。また、答えるという動詞は用意されていません。答えるためのプロセス(アルゴリズム)は自分で考えて下さいまっせというのが、プログラム言語のスタンスです。で、アルゴリスムを考えるというのは、上の例で言うと、
「100までの数を並べる」という動詞を「足す、ひく、かける、わる、一時保存できる変数を用意する、一時保存できる変数に代入する」を使って表現し直すということなのです。
これが慣れないうちはめちゃんこ難しい。
「100までの数を並べる」を

  • 100までの数を、一時保存できる変数を用意する

置き換えるとしましょう。
このとき、変数には「名前」を付けるようにしましょう。名前は一意の名前を付けることが望ましいです。また、目的がわかるような名前を付けましょう。ここでは、変数の名前をNaturalNumberとしましょう。

  • 100までの数を、一時保存できる変数(NaturalNumber)を用意する
  • 変数(NaturalNumber)に1~100までの数を代入する

といった感じで「100までの数を並べる」処理をプログラム的な表現に落とし込むことができました。
とはいえ、これではまだ不十分です。順次処理の制約(順番に1つずつしか処理できない。)を思い出して下さい。あたかもできるかのように、1~100までの数を代入すると書いていますが、これは、代入という行為を100回行っていることと一緒です。つまり、プログラムの制約上できないことなのです。つまり、プログラム的な表現に直すとすると、以下のようになります。

  • 100までの数を、一時保存できる変数(NaturalNumber)を用意する
  • 変数(NaturalNumber)に1を代入する
  • 変数(NaturalNumber)に2を代入する
  • 変数(NaturalNumber)に3を代入する
  • 変数(NaturalNumber)に4を代入する
  •  ・ 
  •  ・
  •  ・
  • 変数(NaturalNumber)に100を代入する

となります。マジです。面倒ですね。ただ、プログラムには反復処理というのがあります。プログラムの制約に書いた「何度も同じ処理を実行するには、何回実行するかを明記しないといけない。」ですが、裏を返せば何度も同じ処理を実行することができるということです。どういうことでしょうか。上のように、モノだけ違うけど、「代入する」という動詞は一緒というときに「何度も同じ処理」を実行するといいます。また、何度も同じ処理をすることを反復処理と言います。反復処理を用いて上の例を書き換えてみましょう。反復処理を実行するには「繰り返す」という動詞を使います。

  1. 100までの数を、一時保存できる変数(NaturalNumber)を用意する
  2. 反復処理の回数を、一時保存できる変数(Counter)を用意する
  3. 変数(Counter)に1を代入する
  4. 変数(Counter)が100になるまで、以下の処理を繰り返す
    1. 変数(NaturalNumber)に、変数(Counter)の値を代入する
    2. 変数(Counter)の値に1を足す
    3. 変数(Counter)に変数(Counter)の値を代入する

謎が増えましたね。1つずつ見ていきましょう。
「100までの数を、一時保存できる変数(NaturalNumber)を用意する」はもういいですね。次からが問題ですね。
「反復処理の回数を、一時保存できる変数(Counter)を用意する」はすごく大事な処理です。反復処理を行うためには、何回実行するかを明記しないといけないので、そのための変数というのを用意します。ここでいう「何回実行するかを明記」というのは、反復処理を行うための条件とも言えるでしょう。例えば、「お母さんに何度もゲームをしたいと伝えるが、宿題が終わるまでダメ」と言われるとしましょう。これは、お母さん目線で言うと「宿題を終わらせるまで、ゲームをしてはダメと何度も言う」ことになります。このとき、宿題を終わらせるまで、というのが、反復処理を行うための条件と言えます。
正直、たとえの話はどうでも良いのですが、反復処理を行うための条件というのは、状態が変化したことを見定めることが多いので、状態を保存しておく変数を用意しておくことが一般的と言えるでしょう。
「変数(Counter)に1を代入する」では、反復処理の条件の初期状態を宣言しています。このように、変数の初期値をわざわざ代入しておくことを変数の初期化と言います。ちなみに、変数を用意しただけで、初期化されていない状態では、どんな値が入っているかは保証されません。反復処理の条件では、反復処理最初の一発目は変数(Counter)の値は1が良いので、変数(Counter)を1で初期化しているわけです。
「変数(Counter)が100になるまで、以下の処理を繰り返す」では、これから反復処理を行うということを宣言しています。反復処理の内容はa~cの内容になります。このように、インデント4)で表しています。実際のプログラムでは反復処理の内容は「ブロック」と言います。ここではa~cの内容をブロックと呼びます。
ブロックの内容は変数(Counter)が100になるまで、繰り返されます。裏を返せば変数(Counter)が100にならないと永遠に反復処理を繰り返すことになります。この状態を無限ループと言います。プログラムが無限ループに陥ると、終了しません。なので、反復処理を行う場合は必ず、無限ループしないことをチェックしないといけません。
「変数(NaturalNumber)に、変数(Counter)の値を代入する」では、変数(Counter)の値を代入します。プログラム上で代入というのは、変数の値を書き換えることを指します。ここで、勘の良い人は、こう考えるでしょう。

  • 反復処理1回目のときに変数(NaturalNumber)には1が代入されます。
  • 反復処理2回目のときに変数(NaturalNumber)には2が代入されます。ここで、変数(NaturalNumber)の値が書き換わってしまうのでは?

ごもっともです。ですが、変数(NaturalNumber)は100までの数を、全て一時保存できるのです。それは、100までの数を一時保存できるように用意したからですね。このように、1つの変数で複数の値を一時保存できる変数の形式を「配列」といいます。配列型の変数と言ったりします。
次の「変数(Counter)の値に1を足す」では、無限ループ回避のために絶対に行わないといけない処理です。変数(Counter)は状態を保存しておくための変数ですので、反復処理中にその状態を変化させないといけません。1回目の反復処理では1、2回目の反復処理では2・・・、というように状態が変化出来れば良いので、今の変数(Counter)の値に1を足せばよいことになります。
「変数(Counter)に変数(Counter)の値を代入する」は少しひっかかりますか?でも、これも重要なことです。前行で、変数(Counter)に1を足していますが、1を足した結果をどこにも一時保存していません。一時保存しておかないと、他の処理から呼び出すこと5)ができません。なので、1を足した結果を代入する変数が必要なのですが、新しく用意する必要はありません。変数(Counter)に代入すれば良いのです。そうすれば、反復処理の条件ではずっと変数(Counter)を参照すれば良いことになります。このようになんらかの計算結果を自身の変数に代入するという手法はすごくよく使われます。

上の例で示した通り、プログラムにはいくつか制約があって、可能な処理も限られています。なので、変数の扱い方がとっても重要になるのです。変数はプログラムの途中経過を一時保存することができます。その変数の中身をどのタイミングで更新し、どのように参照するかがプログラムの極意です。
とはいえ、文章だけ読んでもイメージが付かないと思いますので一度、pythonのコードを見てみましょう。

  1. #変数の定義(宣言)を行う
  2. number = 0
  3.  
  4. print(number)
  5.  
  6. number = number + 1
  7.  
  8. print(number)

実はpythonでは変数の宣言と代入は1行で済まします。新しい変数(プログラム上でそれまで登場しなかった変数)になんらかの値を代入すると、変数の宣言となります。言語によっては、変数の宣言と、代入は別の行に書かないといけないこともあります。このような書き方の違いをプログラムの言語仕様と言います。 2行目の「number = 0」ではnumberという変数に0という値を代入しています。
一般的なプログラムでは、「=」は代入を表します6)。これを代入演算子といいます。
4行目の「print(number)」はnumberを表示するという処理です。おかしいですね。表示するという動詞はプログラム言語には用意されていないはずでした。


コンピュータ上の処理というのは、どんな処理でも2進数の足し算引き算(かけ算割り算)で行います。ですが、パソコンには画面やキーボード、マウスなどのハードウェアが繋がっています。プログラム上でも画面の操作(表示など)や、キーボードやマウスからの入力を検知しないといけません。コンピュータ上では四則演算しかできないのに、パソコン上では四則演算だけでは到底追いつかない処理を行っていることになります。
実は、この「到底追いつかない処理」というのも中身を見ると「数万行の四則演算」で実現しています。この数万行の四則演算を、プログラマに書かせるわけにもいかないので、「数万行の四則演算」も「1行書けば済むようにした命令文」にしたものがあります。この命令文のことを「API」と言います。
APIをプログラム上で呼び出すと、裏では「数万行の四則演算」が展開・実行されることになります。APIは、WindwosなどのOS7)が持っています。OSはプログラマたちにAPIを提供することによって、便利なアプリケーションを開発させようという魂胆なのですね。ただし、OSが提供するAPIは基本すぎてまだまだ開発に便利と言えません。例えば「マウスの位置に写真を表示する」場合だと、

  • マウスの位置を取得する
  • 写真データを画面に転送する
  • 正常に画面に転送できたかを確認する
  • 転送できた写真を表示する
  • などなど

というように、OSが提供するAPIは、コンピュータとその他のハードウェアのやりとりに特化していると言っても良いでしょう。この、やりとりのことを「インターフェース」と言います。 ですがこれだとまだ不便なので、プログラム言語ではAPIよりさらに簡略した形にしています。「1行書けば人のAPIをいくつか呼び出して人の目的を実現する」ことに特化する形にするわけです。人とコンピュータとのやりとりのことを「ユーザインターフェース」と言ったりします。また、APIよりさらに簡略した形というのはプログラム言語によって違います。


というわけで、printという命令文を呼び出すことによって、いくつかのAPIを呼び出して「画面に表示するという目的を実現」してくれるわけです。これも、言語によって書き方が違いますので、言語仕様と言えるでしょう。
さて、続きですが、print(number)とすることで、実行タイミングでのnumberの値を表示することが可能となります。
次の「number = number + 1」ですが、=は代入演算子でしたので、numberという既存の変数に、number+1の値を代入しているわけです。これは結果的にnumberの値が更新されることになります。
プログラムの実行結果は、printで出力された値となります。

実行結果

0
1

では次回(素数を表示してみる(プログラミング))から、pythonで反復を使った素数の表示にチャレンジします。


1)
AIは経験則に近しい近似値を出力している
2)
マルチスレッドという方式もありますが、ここでは順次処理という意味で「順番に1つずつ」と表現している
3)
一時保存とうのは、プログラム実行中は保存している内容を取り出せる状態だが、プログラム終了と同時に変数に保存されている内容は取り出せなくなる。ハードでは揮発性メモリとも言う。
4)
行頭に空白を入れる
5)
参照と言います
6)
数学で言うところの等価を表すわけではないことに注意してください。
7)
基本ソフト
  • wiki/algorithm/素数を表示してみる.txt
  • 最終更新: 2024/11/04 00:47
  • by 127.0.0.1