Processingで学ぶ 実践的プログラミング専門課程

第6回共通部分をまとめる

導入

コードを書いていると、同じようなコードを何度も書いていることがあります。同じようなコードを書きながら、めんどくさいなぁ、何とかならないかなと気付くうちは良いのです。やがてなんとかすることでしょう。コードが大きくなってくると、共通点があることにすら気がつかないことがあります。じっくり考えないと分からないこともあります。 その場合には小さな部分の共通点を見つけ出し、まとめることから始めましょう。図表を活用しながらじっくりと洗い出してもよいでしょう。対象、問題をぐっとにらんでその違いを見極め、共通点を見いだす目と、共通部分をまとめる力を持ちたいものです。

今回と次回は 構造化プログラミングで学習し身につけておきたい大事な要素、 共通点をまとめることを学習します。今回は数式の共通点をまとめることを取り扱います。

展開

数式の共通部分をまとめよう

プログラムの小さな部分といえば数式です。計算するコードを書く際は、まず、取り扱う数式を紙の上に書き下しましょう。そうすると、同じ形の項があることに気付くものです。そのような項は事前に定数化してから式に組み入れれば計算が速くなりますし、コードにした際に読みやすくなります。

次の式のように共通する項が多い式は整理する価値があります。コードの見通しが良くなりますし、繰り返し計算の回数が多い場合ほど実行時間の節約になります。

1本のボルトにかかる荷重を求める式
画像

次の例(sketch ABoltPullingForceEquation.pdeではわずか数ミリ秒の違いですが、もっと複雑な式を数多く繰り返す場合には大きな差になるでしょう。一度の計算量の大きな関数が用いられる場合はなおのことになります。 この中で最も大切にすべきなのは読みやすいコードにすることです。式が読みにくくなるようなものを「整理」とは呼べないからです。

sketch ABoltPullingForceEquation.pde
double Q = 0;
double a = 10;
double b = 20;
double c = 30;
double s = 40;
double myu = 50;
double Wa = 60;
double Wb = 70;

// 整理しない場合
long timePassed = 0;
long timeStart = millis();
for ( long i = 0; i < 10000000; ++i ){
  Q = ( a / c + s / (2 * myu * c) ) / ( 1 + s / (2 * myu * c) ) * Wa
    + ( b / c + s / (2 * myu * c) ) / ( 1 + s / (2 * myu * c) ) * Wb;
  Wa += i;
  Wb += i;
}
timePassed = millis() - timeStart;
println("1: Q = " + Q + ", " + timePassed + " milli sec passed.");

// 整理した場合
double term01 = 2 * myu * c;
double term02 = ( a / c + s / term01 );
double term03 = ( 1 + s / term01 );
double term04 = ( b / c + s / term01 );
Wa = 60;
Wb = 70;

timePassed = 0;
timeStart = millis();
for ( long i = 0; i < 10000000; ++i ){
  Q = term02 / term03 * Wa
    + term04 / term03 * Wb;
  Wa += i;
  Wb += i;
}
timePassed = millis() - timeStart;
println("2: Q = " + Q + ", " + timePassed + " milli sec passed.");

分解整理した後の式はコンパクトで読みやすいと感じるでしょう。計算効率も大切ですが、読みやすく、後でメンテナンスしやすくなるようにコードを書くようにしましょう。

[作業] sketch ABoltPullingForceEquation.pde中の計算式を、改行無しに1行で記述してみましょう。改行ありの場合と比べて読みやすさに変化がありますか? どちらが読みやすいでしょうか? より読みやすい形に整えましょう。

公式は積極的に活用しよう

世の中には退屈な計算をだらだらと繰り返す必要のある状況があるものです。級数の公式はその代表的な例です。定義に従い、コンピュータの精度の限界まで泥臭く繰り返し計算を行って値を求める方法もありますが、単に計算結果が必要なだけならば既にある便利な公式を活用しましょう。公式集を手元に置いておき、随時参照するべきです。時間の無駄、計算資源の無駄以外の何ものにもなりません。

例えば次の級数の公式を見てください。

1からnの総和
画像

実際にこの級数を計算をするsketchを書いてみるとよくわかります(sketch Sum1ToN.pde⁠。nの値が大きくなるほど公式を使わない計算は時間がかかります。一方公式を使うとnの値がどんなに大きくても計算時間は一定かつ一瞬です。

sketch Sum1ToN.pde
//Sum1ToN.pde
double sum = 0;
double n = 100;

//定義通り計算
// sum = 1 + 2 + 3 + ... + n
//これは長いし無駄。便利なfor文を使ってみよう。
for ( int k = 1; k<=n; k++ ){
  sum += k;
}
println("1: sum = " + sum);

//公式を使って計算
sum = 1. / 2 * n * ( n + 1 );
println("2: sum = " + sum);

コンピュータは一定のルールに従って次々と仕事を行うことが得意ですが、時間という有限の資源を効率良く使うために、よく知られた公式があるならば遠慮なく利用しましょう。たいていはコードが短くなり、シンプルになります。繰り返し処理をしなくて済むというのはとても有り難いことです。

[作業] Sum1ToN.pdeの計算をそれぞれ100万回繰り返してかかる時間を計測してみましょう。

演習

演習1(難易度:middle)

Wの値を1から100まで1ずつ変化させ、P1P2P3の値を求める計算を100万回繰り返しましょう。式を整理する前と後で、計算時間にどれだけの差が出るでしょうか。それぞれの記号の意味を知る必要は無いので安心して取り組んでください。L=1000, l1=10, l2=20, l3=30とします。ファイル名はExercise6_1.pdeとしましょう。

3本のボルトにかかる荷重を求める式
画像

演習2(難易度:easy)

次の級数の値を求めるsketchを作成してください。a=2, r=0.5, n=1000として、この計算1000000回にかかる時間を計測してください。定義通りの左辺での計算と、公式を用いた右辺の計算と両方作りましょう。ファイル名はExercise6_2.pdeとしましょう。

等比級数
画像

まとめ

  • プログラムの最も小さな単位となる数式をまとめることを学習しました。

学習の確認

それぞれの項目で、Aを選択できなければ、本文や演習にもう一度取り組みましょう。

  1. 式を整理することの大切さが理解できましたか?
    1. 理解できた。自分のコードに活かす決心をした。
    2. 理解できた。しかし、自分のコードに活かす必要を感じない。
    3. 理解できない。
  2. 公式を利用する意味を理解できましたか?
    1. 理解できた。
    2. 理解はできるが必要を感じない。
    3. 理解できない。

参考文献

  • 『いかにして問題をとくか』⁠G・ポリア 著、丸善株式会社
  • 数学的に問題を解く考え方を丁寧に解説した書籍。プログラミングは数学パズルです。是非ご一読を。

  • 『ハッカーのたのしみ 本物のプログラマはいかにして問題を解くか』⁠ヘンリー・S・ウォーレン、ジュニア 著、株式会社エスアイビー・アクセス)
  • 計算効率と速度を究極まで求めると、こうなる。お仕事でこれを活用するのはオススメできませんが。

演習解答

  1. sketch Exercise6_1.pde
    • 筆者の環境では式を整理した場合の方が時間がかかっていました。不思議ですが、そういうこともあるのですね。ただし、式の読みやすさは整理した後の方が優れています。
  2. sketch Exercise6_2.pde
    • 圧倒的じゃないか。

おすすめ記事

記事・ニュース一覧