0.4444444444444444って何分の何、の計算

2.1461806日は何日何時間何分何秒なのか、34.590707は何度何分何秒なのか、の計算」で、ほんとに標準添付ライブラリで出来ないのか、数値と数学モジュール(と組み込み関数)を見返してて、本題とは違うものを見つけたはなし。

本題の前に、「2.1461806日は何日何時間何分何秒なのか、34.590707は何度何分何秒なのか、の計算」「公約数リストの求め方」を書いたときに頭にあった別のことについて触れておこうと思う。

たとえばdivmodなんだけどね、これ、わたしにとっては「いつも使いたいときにどこにあったか忘れる」筆頭なのですよ。まず大抵 math にあるもんだと思い込んでる。math でないことがわかると、NumPy 依存で書いてる場合は、(import numpy as np してるとして)np.divmod してみてこれも失敗する。時間にすればせいぜい数秒の出来事だけれども、こういう無駄な時間は毎度のこと。NumPy, SciPy が膨大であって、素の Python でさえ至れり尽くせりであるがゆえに、些細な迷子はまま日常なわけね。

NumPy, SciPy を使いこなし始めるに従って、だんだん素の Python でも出来ることまで NumPy で書き始めるのも結構悩ましいことで、ラフにプロトタイプして、いざ人に配ろうというときに困ったことになることが多いわけね。日出日没計算、やってみようで実際それをやらかしてる。

数値と数学モジュール(と組み込み関数)を見返すという作業はつまり、こうしたことを踏まえて、でもある。

さて本題。

そうやって漁ってて、「あ、知らなかった、得した」と思った2つ。一つ目はmath.modf

1 >>> import math
2 >>> math.modf(34.590707)
3 (0.5907070000000019, 34.0)

2.1461806日は何日何時間何分何秒なのか、34.590707は何度何分何秒なのか、の計算」での未整理前の計算だと使いたくなりそうなのね、これ。34.590707 度、なら、「34度」は計算しなくなって「人間さま」ならわかるわけだ。「計算」必要なのは「.590707」部分だけだ。これはホモ・サピエンスは苦手な「計算」だ。そうかそうか、どっかで使えるかもなぁ。

math モジュールって基本的に C の math がベースになってるんだけど、C の math に記憶がまったくない。なんだけど、c90 時代から既にあったらしい。これを知らなかったことで損をしていた例を思いつかないので、単にニーズがなかっただけなんだろうけれど、ひょっとしたら C/C++ でも「損してたかも」なのかも。

もう一つが今回のエントリのタイトルにした、小数を分数で近似するための機能:

1 >>> from fractions import Fraction
2 >>> Fraction(0.4444444444444444)
3 Fraction(2001599834386887, 4503599627370496)
4 >>> Fraction(0.4444444444444444).limit_denominator()
5 Fraction(4, 9)
6 >>> 4/9.
7 0.4444444444444444

人間さまにとっては、小数表現よりは、分数や百分率が直感的にイメージしやすいわけである。見慣れている 0.25 や 0.125 や 0.333… や 0.666… を別とすれば。ははぁん。これ、日常的にやるんだよね、この計算。「だいたい何分の何」と言葉にするために。

「limit_denominator」という名前なのは、分母をコントロール出来るから。こういうこと:

 1 >>> import math
 2 >>> from fractions import Fraction
 3 >>> math.pi
 4 3.141592653589793
 5 >>> Fraction(math.pi).limit_denominator(10)
 6 Fraction(22, 7)
 7 >>> 22./7
 8 3.142857142857143
 9 >>> Fraction(math.pi).limit_denominator(100)
10 Fraction(311, 99)
11 >>> 311/99.
12 3.1414141414141414
13 >>> Fraction(math.pi).limit_denominator(1000)
14 Fraction(355, 113)
15 >>> 355/113.
16 3.1415929203539825
17 >>> f = Fraction(math.pi).limit_denominator(1000)
18 >>> f.numerator
19 355L
20 >>> f.denominator
21 113L
22 >>> f.numerator / f.denominator
23 3L
24 >>> f.numerator / float(f.denominator)
25 3.1415929203539825

プログラム的にも使いやすいケースもあるかもね。何かを整数で分割しなければならないときなんかは。ちょっと例は思いつかないけど。というのもね、「2.1461806日は何日何時間何分何秒なのか、34.590707は何度何分何秒なのか、の計算」で役に立つと思いたいじゃない? でもそうもいかない:

 1 >>> from fractions import Fraction
 2 >>> f = Fraction(0.5907070000000019)
 3 >>> f
 4 Fraction(83134619533911, 140737488355328)
 5 >>> f.limit_denominator(3600)
 6 Fraction(1462, 2475)
 7 >>> f.limit_denominator(10000)
 8 Fraction(4119, 6973)
 9 >>> f.limit_denominator(6000)
10 Fraction(2657, 4498)
11 >>> f.limit_denominator(5000)
12 Fraction(2657, 4498)
13 >>> f.limit_denominator(4000)
14 Fraction(1462, 2475)

つぅわけで、「これはこれ」なのね、結局。










ところで、「何かの言語を学習する」話。これは「日常化する」ことに勝る方法はない、です。毎日動かしてりゃ、なんだって身についてく。そしてそれが Python のような「非コンパイル型言語、かつ、汎用言語」の場合は、そのための最も手っ取り早いのが、「計算機として使う」こと、なのよ。だからどんな初学者向けの書籍や WEB サイトも、大抵そこから始まる。Python 公式サイトのチュートリアルも同じ。

まずスクリプト言語の対話モードを計算機として使うことの良さは、計算過程が全部残ること、だし、readline 搭載の対話モードでは、過去の入力を簡単に呼び戻せること、だし、NumPy なんぞ使い出せば、「超弩級高機能電卓」なわけで、その行為そのものが価値があるわけなんだけれど、「学習」という点でも良い習慣なんですよ、っと。

なんでこんなことわざわざ言うかってぇと、せっかく Python 持ってて使える状態にあるのに、Windows の電卓を使うことを「死守する」同僚があまりに多かったから。学ぶ機会も使いやすさも放棄してどーすんだ、と、そのたんびにワタシは思うわけだ。だってそのくせ「馬鹿でも出来る Python」的な本だけは真面目に買いやがるんだからさ。