[SML 7341] 合計ぜすに平均を

AOKI Atsushi aoki @ sra.co.jp
2007年 12月 12日 (水) 15:01:47 JST


青木@SRA先端技術研究所です。

一見、簡単そうな「平均(相加平均)を求める」プログラムを考え
てみましょう。実は大変に奥深い内容を含みます。皆さんのプログ
ラミングに対する話題のネタにしていただければ幸いです。

たとえば、999人の成績(百点満点)の平均を求めるプログラム
なんて、誰にでも作れそうですが、はたして本当でしょうか?

まず、999人の成績の仮データを生成するところが難しいでしょ。
乱数を使えば、なんとかなりそうですが、一様分布しか得られない
ことが多いはず。実際には正規分布しますもん。正規分布した仮の
成績データを即座に作り出せるプログラマが少ないですよね。

それに、平均を求めるのに、999人の成績を合計して、人数で割
る、なんてことをしたら、それこそペケ(平均プログラマ以下)。

合計を使わないこと、999人も仮であり、人数はこれからもどん
どん増えて、合計は桁あふれする恐れがあること、それでも即時に
平均を応答できなければならないこと、これらを満たすプログラム
(平均というオブジェクト)の作成です。

末尾に完全なプログラム(Smalltalk らしいメタなプログラム)を
添付しておきますが、要と記すと以下のようになります。

| normalDistribution numberOfPeople averageValue resultValue |
normalDistribution := JunNormalDistribution mean: 50 deviation: 15.
numberOfPeople := 0.
averageValue := 0.
999 timesRepeat:
        [resultValue := 0 max: (normalDistribution next min: 100).
        numberOfPeople := numberOfPeople + 1.
        averageValue := averageValue + ((resultValue - averageValue) * (1 / numberOfPeople))].
^averageValue

平均50点で偏差15の正規分布した仮の成績データをこしらえな
がら、合計をすることなく、平均を計算しています。多倍長整数演
算、分数、そして、浮動小数点ではない固定小数点数、などが具備
されたオブジェクト指向プログラミング言語ならではですね。

新たな成績(resultValue)を加えたときの新たな平均とは、その成
績の昔の平均からのズレ(resultValue - averageValue)に対して
影響力(1 / numberOfPeople)を乗じたものを、昔の平均に加えた
ものである、というところにあります。

多くの人の中で一人だけ突出しても、その影響は大したことないの
が明解です。また、総数を2とした特殊な場合、足して2で割れば
いいことも導出されます。

30、50、70、90、・・・、と来たとき、私たちはいちいち
合計して総数で割って平均なんか求めていないのよねぇ〜。突出を
演出したい(自分の影響力を出したい)のに、衆(多勢)を頼むな
んて可笑し過ぎますぅ〜。

------------------------------------------------------------
R2D2 (AOKI Atsushi)        http://www.sra.co.jp/people/aoki/


| aClass normalDistribution averageObject |
aClass := Smalltalk.Jun
            defineClass: #JunAverageObject
            superclass: #{Jun.JunAbstractObject}
            indexedType: #none
            private: false
            instanceVariableNames: 'numberOfPeople averageValue '
            classInstanceVariableNames: ''
            imports: ''
            category: 'Jun-Goodies-Misc'.
aClass
    compile: 'initialize
    super initialize.
    numberOfPeople := 0.
    averageValue := 0'
        classified: 'initialize-release' asSymbol;
    compile: 'value
    ^averageValue'
        classified: 'accessing' asSymbol;
    compile: 'add: resultValue
    numberOfPeople := numberOfPeople + 1.
    averageValue := averageValue + ((resultValue - averageValue) * (1 / numberOfPeople))'
        classified: 'adding' asSymbol.
(Kernel.Parcel parcelNamed: 'Jun') addClass: aClass.
(Store.Registry packageNamed: 'Jun-Goodies-Misc') addEntiretyOfClass: aClass.

[normalDistribution := JunNormalDistribution mean: 50 deviation: 15.
averageObject := aClass new.
999 timesRepeat: [averageObject add: (0 max: (normalDistribution next min: 100))].
^averageObject value]
        ensure: [aClass removeFromSystem]


------------------------------------------------------------


SML メーリングリストの案内