👩‍🎓

情報工学を学んだことによる変化

2021/12/15に公開

ランサーズ Advent Calendar 15日目の@manamin0521mです。
昨日は@IsanaさんによるPHPの型に向き合いDXを向上させるでした。

文系学部を卒業したものの、エンジニアとして働いていく中で情報工学を大学で学んでみたい思いがあったことから、現在夜間の社会人向け大学に通わせていただいております。

まだ大学1年生なので初歩の初歩しか習っておらず、基本情報処理試験にも出るような内容が多いのですが、大学で学んだことで考え方が変わった、業務でコードを書く上で役立ちそうな意識チェンジについて今回は書いてみたいと思います。

なお講義がRubyを使って行われていた関係で、講義部分では主にRubyのコードを使用しています。

プログラミングと数学のつながりがわかった

課題において数式をコードに直すというようなものがいくつかあり、数式って関数(メソッド)で表せるんだ! と改めて気づくことができました。

簡単な例を示すと、以下のような階乗ですと
 n! = n×(n-1)×...×2×1

Rubyのコードでは以下のように書くことができます。
※参考文献[1]のP27, P36より。

def fact(n)
  result = 1
  n.times do |i|
    result = result * (i+1)
  end
  return result
end

関数における「引数」が数式における「変数に代入する値」、関数における「戻り値」が数式における「解」と対応しています。

関数 数式
引数 変数に代入する値
戻り値

このような課題を通じて、大量のデータを扱うコードや競技プログラミングなど、速度を考える必要があるコードにおいて数学の知識が重要であるということが少しわかるようになりました。

同様にテイラー展開や数列など、一見複雑そうな数式も繰り返し文(ループ処理)などのプログラミングにおける初歩の技術を使うことで書けます。講義では他に、数値積分をコードで表したりなどもしました。

アルゴリズムごとの速度の違いについて考えるようになった

アルゴリズムの時間計算量の測定

アルゴリズム自体の解説は今回こちらの記事では行わず、外部ページか書籍をご参照いただけるとと思います。

講義で扱っていたサイト
https://visualgo.net/ja

アルゴリズム自体は、基本情報の勉強や就活時のコーディング試験のために、サラッとだけはかじっていたのですが、時間計算量(=アルゴリズムごとにソートにかかる時間)を計測する課題を通じて、速度の違いを実感することができました。

時間計算量の計測例(Ruby)
※参考文献[1]P87より。

def randarray(n)
  return Array.new(n) do rand(1000) end
end

def bench
  t1 = Process.times.utime
  yield
  t2 = Process.times.utime
  return t2 - t1
end

irb> a = randarray(1000); bench do 計測したいメソッド(a) end

なお時間計算量について詳しく知りたい方は下記などが参考になるかと思います。
https://qiita.com/cotrpepe/items/1f4c38cc9d3e3a5f5e9c

内部のコードがどうなっているか気になるようになった

PHPのソートアルゴリズムを調べてみた

アルゴリズムを学んだことで、普段扱っているコードのソートのアルゴリズムがどのようになっているのか気になってきました。そこで自身が普段扱っているPHPにおいて、どのように書かれているのか見てみることにしました。

PHPのバージョンは弊社で使っているPHP ver7.3.29で調べていることをご了承ください。なおバージョンアップはバージョンアップチームに尽力いただいております🙇‍♀️

今回はPHPのソートの中でもsort()を例に調べてみました。ソートに関するコードは、こちらより。

当該ファイルのPHP_FUNCTION(sort)、またその内部のphp_get_data_compare_func()をみると、同ファイル内のphp_array_data_compare()にて、ソートのアルゴリズムが記載されていました。

/ext/standard/array.c
/* Numbers are always smaller than strings int this function as it
 * anyway doesn't make much sense to compare two different data types.
 * This keeps it consistent and simple.
 *
 * This is not correct any more, depends on what compare_func is set to.
 */
static int php_array_data_compare(const void *a, const void *b) /* {{{ */
{
	Bucket *f;
	Bucket *s;
	zval result;
	zval *first;
	zval *second;

	f = (Bucket *) a;
	s = (Bucket *) b;

	first = &f->val;
	second = &s->val;

	if (UNEXPECTED(Z_TYPE_P(first) == IS_INDIRECT)) {
		first = Z_INDIRECT_P(first);
	}
	if (UNEXPECTED(Z_TYPE_P(second) == IS_INDIRECT)) {
		second = Z_INDIRECT_P(second);
	}
	if (compare_function(&result, first, second) == FAILURE) {
		return 0;
	}

	ZEND_ASSERT(Z_TYPE(result) == IS_LONG);
	return ZEND_NORMALIZE_BOOL(Z_LVAL(result));
}
/* }}} */

なお上記コードで出てくる、zvalについて理解するにはこちらが参考になりました。
https://qiita.com/DQNEO/items/a5d9f5d80dc8b14071f4
https://net-newbie.com/phpext/7-zval.html

PHPのドキュメントを見る限り、上記はクイックソートで実装されているとのこと。

PHP の大半のソート関数と同様、sort() は Quicksort でそれを実装しています。

しかしこちらのコードを理解する上ではポインタの理解が必要であり、各言語のアルゴリズムを真に理解するにはC言語の知識が必要だとわかったので、来週以降から開始されるC言語の講義でより詳しく理解できるようにしたいと思います、、、!

このようにフレームワークや言語をそのまま使うのではなく、内部がどのようなコードになっているかが気になるようになったこと、内部のコードを調べることでC言語の重要性に気づいたことも大きな収穫のように思いました。

コンピューターの仕組みを理解してコードを書こうと思った

実数の誤差

一例として、実数の計算において、コンピューターの仕組みを理解している必要があると感じました。下記は双方1.0÷3を行った出力結果です1.0÷3 = 0.333...ですがそれぞれ下記のような結果となります。

irb(main):001:0> 1.0 / 3
=> 0.3333333333333333

表示する桁数を指定していない場合は、計算の精度の限界以降を表示していないため、小数点以下の桁数が16桁となっています。

irb(main):002:0> printf("value = %.20g\n", 1.0 / 3)
value = 0.33333333333333331483
=> nil

表示する小数点以下の桁数を20桁に指定した場合は、3が続かなくなります。

コンピューターはビット数が有限であることから、有限の桁数で計算するため、計算の精度に限界があります。そのため、本来1.0÷3は小数点以下に3が無限に続くはずが、このような結果となっています。

※参考文献[1]P6より。

浮動小数点について

前項の実数の計算をより広い範囲でできるようにしたのが、浮動小数点という方法です。下記画像の式の右辺と左辺は同じ数字を表しますが、小数点の位置が異なっています。このように小数点の位置を動かすものと考えることから、浮動小数点といいます。

指数と仮数に分けて扱うことで、有限のビット数の中でも広い範囲の数値を表すことができます。

引用:ビットで表す数字の世界~浮動小数点編~ 株式会社マクニカ

浮動小数点についても、丸め誤差や情報落ち、桁落ちなど、計算の精度に限界があります。浮動小数点について詳しく知りたい方はこちらも合わせてご覧ください。
https://qiita.com/angel_p_57/items/24078ba4aa5881805ab2

このようにコンピューターの仕組み上、実数の計算をするときは誤差が出る前提で考える必要があり、必要に応じて対策する必要があるということがわかりました。自分が開発する上でも、小数点切り上げ、切り捨ての計算で1円の誤差が出て困った経験があるので、小数点以下の数字についても考慮したいです。

終わりに

今回は紹介できませんでしたが、OS、CPU、Unixコマンド、セキュリティ、ネットワーク、再帰処理、データの型、オブジェクト指向なども講義で習い、勉強になりました。

その他精神的な面ではありますが、意味がわからない理系科目のテスト勉強/課題/実験レポート等が山積みな中、どんなに無理でもやらないと単位が来ない上に、常に締め切りに追われているので、「無理難題でも調べつつ、大急ぎでやってみる」 という姿勢が以前より身についたように思います。

普段、大学の話はnoteにも書いているので興味がある方は見てみてください。
https://note.com/manamin0521/m/mcdabdde0af1f

読んでいただきありがとうございました!

参考文献

[1]久野靖 電気通信大学, 基礎プログラミングおよび演習[第4版], 学術図書出版社, 2020

Discussion