なでしこさんでドント式計算
(ここにいつもの自虐を貼る)
今回やったこと
今日は参議院議員選挙があった。
そこで、選挙で使われるドント式の計算をするプログラムを、なでしこで作ってみた。
ドント式の計算
ドント式では、「総議席数」と「各団体の得票数」から、各団体に割り当てる議席数を以下のアルゴリズムで計算する。
各団体の議席数を0に初期化する
総議席数の分、以下を繰り返す
「得票数」を「現在の議席数+1」で割った値が最大の団体を探す
その団体の議席数を1増やす
値が最大の団体が複数ある (同点の) 場合の処理はよくわからなかったので、今回は入力順で最初の団体を選ぶようにした。
プログラム
ドント式の計算 (なでしこ3貯蔵庫)
ポイント
UIの作成
『「団体名,得票数」の形式で入力してください。』のラベル作成。
改行作成。
入力欄は空のテキストエリア作成。
その「cols」に30をDOM属性設定。
その「rows」に10をDOM属性設定。
改行作成。
「総議席数:」のラベル作成。
総議席数欄は「5」のエディタ作成。
その「type」に「number」をDOM属性設定。
その「min」に0をDOM属性設定。
計算ボタンは「計算」のボタン作成。
改行作成。
結果テーブルは「団体名,議席数」のテーブル作成。
それの「行揃え」に「右」をDOMスタイル設定。
各種部品の配置と設定を行い、UIを用意する。
入力の取得とチェック
定数の入力配列は入力欄のテキスト取得してCSV取得。
定数の団体数は入力配列の要素数。
定数の最初取得は関数(A) それはA@0。ここまで。
定数の二番目取得は関数(A) それはA@1。ここまで。
定数の団体名リストは入力配列に最初取得を配列関数適用。
定数の得票数リストは入力配列に二番目取得を配列関数適用。
得票数リストを反復
もし、(対象をNAN判定)または(対象<0)ならば
「得票数は非負の数でなければなりません。{改行}{対象}はダメです。」と言う。
空を戻す。
ここまで。
ここまで。
定数の総議席数は総議席数欄のテキスト取得して整数変換。
もし、(総議席数をNAN判定)または(総議席数<0)ならば
「総議席数は非負整数でなければなりません。」と言う。
空を戻す。
ここまで。
団体名と得票数のリスト、そして総議席数をUIから取得する。
そして、それらのデータが有効かのチェックを行う。
各団体の議席数の計算
定数の議席数リストは0を団体数だけ配列要素作成。
(総議席数)回、繰り返す
変数の議席獲得者は0。
iで0から(団体数-1)まで繰り返す
もし、(得票数リスト@i)×((議席数リスト@議席獲得者)+1)が(得票数リスト@議席獲得者)×((議席数リスト@i)+1)超ならば
議席獲得者はi。
ここまで。
ここまで。
議席数リスト@議席獲得者を1増やす。
ここまで。
ドント式のアルゴリズムに沿って、各団体の議席数を計算する。
割り算をすると誤差が心配なので、掛け算で比較するようにした。
計算結果の表示
定数の最大議席数は議席数リストの配列最大値。
定数の結果リストは空配列。
iで0から(団体数-1)まで繰り返す
変数の結果は[(団体名リスト@i), (議席数リスト@iの通貨形式)]。
jで0から最大議席数まで繰り返す
変数の結果セルは空。
もし、jが議席数リスト@i未満ならば
結果セルは結果セルに「(当) 」を追加。
ここまで。
得票数リスト@iを(j+1)で割って切り捨てして通貨形式。
結果セルは結果セルにそれを追加。
結果に結果セルを配列追加。
ここまで。
結果リストに結果を配列追加。
ここまで。
変数の結果ヘッダは["団体名", "議席数"]。
iで0から最大議席数まで繰り返す
結果ヘッダは結果ヘッダに「÷{i+1}」を配列追加。
ここまで。
結果リストの0に結果ヘッダを配列挿入。
結果テーブルを結果リストにテーブル更新。
計算結果をテーブルに出力する。
まず団体名と議席数を表示し、その右側に得票数を各値で割った商と、それがその団体の議席数に貢献したかを表示する。
ここでは見やすさのため、商は小数点以下を切り捨てて表示するが、計算中は小数点以下に相当する部分まで考慮されるはずである。
今回ハマった点
カッコの中で関数を3個連続させると解析エラーになる
関数を「して」で3個連続させてカッコに入れると解析エラーになる · Issue #2070 · kujirahand/nadesiko3
結果セルは結果セルに(得票数リスト@iを(j+1)で割って切り捨てして通貨形式)を追加。
と書くと、解析エラーになってしまった。
得票数リスト@iを(j+1)で割って切り捨てして通貨形式。
結果セルは結果セルにそれを追加。
と分割することで、これを回避した。
「クリック時」で実行する関数ではエラーメッセージが表示されない
「クリック時」で実行する関数内でエラーが発生した時、ページ上にメッセージが表示されない · Issue #2071 · kujirahand/nadesiko3
今回、変数名の勘違い、typo、文法の理解不足などで、エラーを発生させることが少なくなかった。
今回のプログラムでは、主要なロジックは全て「クリック時」で実行する関数の中に書いている。
このような関数では、エラーが発生してもエラーメッセージを表示してくれないようである。
ブラウザの開発者ツールのコンソールには例外の内容が出力されたが、そこでは具体的になでしこのプログラムの何行目でエラーが発生したかの情報は無さそうだった。
そこで、このようなエラーが発生したときは、
「あ」を表示。
のような命令をプログラムに挟み、これで出力される内容によりどこでエラーが発生しているかの特定に繋げた。
(いわゆるprintfデバッグのようなもの……表示デバッグ?)
おわりに
なでしこ3で、ドント式の計算を行い、各団体の得票数と総議席数から各団体の議席数を求めるプログラムを作成した。
整数の計算であれば、1億票×500議席程度なら正確に計算できるはずである。
しかし、按分などにより小数の票が発生した場合は、どうなるか……?
コメント