わさっきhb

大学(教育研究)とか ,親馬鹿とか,和歌山とか,とか,とか.

整数になるよう比例配分

いきなりですが問題です.

任意個の数値を引数にとり,合計が100になるよう,比例配分した結果を返すプログラムを作成しなさい.
ただし,出力の各値は整数とします.切り上げ・切り捨ては,自分でルールを決めてください.
例えば,(1, 1, 1, 1)を入力に与えると,出力は(25, 25, 25, 25)です.
また,(1, 1, 1, 3)を入力に与えると,出力は(16, 17, 17, 50)または(17, 16, 17, 50)または(17, 17, 16, 50)です.

「比例配分」については,算数・比例配分の考え方と計算 : なるほどの素が分かりやすい内容でした.ただし,答えに出てくる数値はいずれも整数です.算数・数学・理科としては,小数や分数が現れる可能性もあります.プログラミングでは,小数点以下が必要な場合も,表示に際して不要な場合も,考えられます.
この問題の元ネタは,本日のもう1つの記事です.課題達成の寄与率を,「自力」「他の学生」「教員」「外部情報」に分けて回答してもらいました.Moodleの機能として,それぞれの値は0から100までと設定できましたが,合計が100というのはできなかった話で,そうすると合計が100でない回答もいくつかあり,そこで,「揃える」必要が出てきたのでした*1
さっそくですが解答をGistに公開します.Rubyスクリプトです.Arrayのメソッドとして,比例配分して新たなArrayオブジェクトを返すよう,prorateを定義しました.
このprorateには2つの引数を取ることができます.最初の引数は,比例配分の合計値で,デフォルトは100です.次の引数は真偽値で,比例配分したそれぞれの値が実数(Floatオブジェクト)でいい場合にはtrueを指定します.デフォルトはfalseで,各要素は整数値にします.
メソッド内のローカル変数のうち,a2,a3,a4は,self(処理対象のArrayオブジェクト)と同じ個数の値を持つArrayオブジェクトです.具体的には,a2は,実数でいい場合の比例配分の結果です.a3の初期値は,その各要素の整数部分,a4の初期値は,小数部分です.
切り上げ・切り捨ては,a4のどこかの要素が0でないときに発生します.そこで,a3の各要素の和が,比例配分の合計値になるまで,a4の最大値をとる要素について,対応するa3の要素を1加え(切り上げ)てから,a4の要素を0にしました.最終的に,a3の各要素の和が,比例配分の合計値となったとき,a4の0でない要素のところは,切り捨てられたことになります.
Rubyスクリプトとしては,コマンドライン引数に数値列を与えた場合には,それを対象として,元の数値列,実数の比例配分結果,整数の比例配分結果を出力します.コマンドライン引数がないか,「test」を指定した場合には,スクリプト内のサンプルデータで計算します.実行結果は以下の通りです.

$ ruby $S/priv/rb/prorate.rb test
[100, 200, 300, 400] => [10.0, 20.0, 30.0, 40.0] => [10, 20, 30, 40]
[1, 1, 1, 1] => [25.0, 25.0, 25.0, 25.0] => [25, 25, 25, 25]
[1, 1, 1, 3] => [16.666666666666668, 16.666666666666668, 16.666666666666668, 50.0] => [17, 17, 16, 50]
[1, 1, 2, 3] => [14.285714285714286, 14.285714285714286, 28.571428571428573, 42.857142857142854] => [14, 14, 29, 43]
[99, 30, 2, 37] => [58.92857142857143, 17.857142857142858, 1.1904761904761905, 22.023809523809526] => [59, 18, 1, 22]

*1:「出力の各値は整数」は,各回答の4つの値を,シェル上で一覧表示する際の要請なのでした.とはいうもののRubyでは,"%3.1f" % vといった書き方でも,差し支えなかったのですが.