rizka
(rizka)
2016 年 11 月 1 日午後 6:39
1
このスレッドの続き:投票のパーセント表示の丸め誤り :
最新の件は疑わしい丸め規則の問題ではなく、完全に誤った規則の問題であるため、新しいトピックを作成しました。したがって、このトピックは Contribute > Bug カテゴリーに属する必要があります。重大な問題ではありませんが、いずれにせよややこしいものです。
公開投票で106票が投じられました。"いいえ"が69票、"はい"が37票でした。単純に計算すると、69/106は約65.09%になります。しかし、システムは"いいえ"が66%、"はい"が34%と表示しています。以下に画像を示します:
最終結果は"いいえ"が72票、"はい"が38票で、投票は現在終了しています。72/110は約65.45%ですが、システムはこれを66%に丸めています。したがって、このバグはTappara.co で実際に確認できます。
バグの原因となるコードを探そうとしましたが、失敗しました。evenRound という関数が呼び出されていることを発見しましたが、そのソースを検索しても見つかりませんでした。これは推測ですが、この関数は数を最も近い偶数 に丸めているのでしょうか?
Not sure I understand what’s broken here. The poll will round the % so they add up to 100%.
rizka:
I tried to look at the code to find what causes the bug, but failed. I discovered that a function named evenRound is called, but when I seeked for its source, I didn’t find it.
// stolen from http://stackoverflow.com/a/13484088/11983
function sumsUpTo100(percentages) {
return percentages.map(p => Math.floor(p)).reduce((a, b) => a + b) === 100;
}
export default (percentages) => {
const sumOfDecimals = Math.ceil(percentages.map(a => a % 1).reduce((a, b) => a + b));
// compensate error by adding 1 to the first n "non-zero" items
for (let i = 0, max = percentages.length; i < sumOfDecimals && i < max; i++) {
if (percentages[i] > 0) {
percentages[i] = ++percentages[i];
// quit early when there is a rounding issue
if (sumsUpTo100(percentages)) break;
}
}
return percentages.map(p => Math.floor(p));
};
「いいね!」 1
rizka
(rizka)
2016 年 11 月 2 日午後 12:45
3
結果がひどいのが、問題なんです。
コードありがとうございます。見つけられなかったのが、Githubの使い方がどれほど下手かを示していますね。このコードはStack Overflow から引用したもので、そこでの著者は次のように述べています。
どの程度の精度が必要かはわかりませんが、私がやるなら、小数点以下の合計の天井値をnとして、最初のn個の数に1を足すという単純な方法を取ります。この場合、nは3になるので、最初の3つの項目に1を足し、残りは床関数で処理します。もちろん、これは非常に正確とは言えず、丸めるべきでない数値が切り上げまたは切り捨てされる可能性がありますが、概ね問題なく機能し、常に100%になるように調整されます。
このような過酷な方法では奇妙な結果になるのも無理はありません。もう少し洗練されたアプローチを取るべきだと提案します。同じトピックにあるこの投稿 は72件の高評価がありますが、コードが引用された投稿はわずか1件です。
元の小数データへの依存を気にしないのであれば、これを実現する方法はたくさんあります。
最初の方法、おそらく最も人気のある方法は、最大剰余法です。
基本的な手順は以下の通りです:
すべてを切り捨てる
合計と100の差を取得する
小数部分の大きい順に項目に1を分配して差を埋める
…
この方法が最善の選択だと思います。依然として非常にシンプルでありながら、はるかに良い結果をもたらします。現在のコードには、どの数を切り上げるべきかを関数に伝えるための数行の追加が必要です。しかし、考慮すべきもう一つの課題があります。同じトピックにあるこの投稿 からの引用です:
Varun Vohraによる高評価の回答は絶対誤差の合計を最小化しますが、実装は非常に簡単です。ただし、エッジケースを処理できない場合があります。24.25、23.25、27.25、25.25を丸めた場合の結果はどうなるべきでしょうか?そのうちの一つは、切り捨てではなく切り上げる必要があります。
この投稿では、どれを切り上げ、どれを切り捨てるかを選ぶための様々な方法が提案されていますが、任意の選択を完全に避けることはできません。完璧を求めたいのであれば、この投稿をさらに読んでいただいても構いませんが、このような稀な特殊なケースではこれでも十分満足できます。
おそらくリストの最初か最後の項目を適当に選ぶことになるでしょう。
これが役に立てば幸いです。基本的な構文を少し勉強すれば、私自身も改善できるスキルは十分にあると思います。今はやらないつもりですが、誰かがそれ以前に取り組んでくれないなら、そのうちやるかもしれません。
編集。タイトルを少し改善し、「broken」を「imprecise」に変更しました。このトピックは Contribute > Feature に移動するか、以前のトピックと統合されるべきです。
「いいね!」 4
Feel free to submit a pull request to change the rounding algorithm
「いいね!」 5
rizka
(rizka)
2016 年 11 月 3 日午前 9:22
5
It was like classic exercises for beginning programmers. Here is a way to do it, but someone who has more experience and isn’t totally new to JavaScript might find some shortcuts.
https://github.com/rizka10/discourse/pull/1/files
「いいね!」 2
Looks fine to me. Would you mind making it a PR so that I can merge it?
rizka
(rizka)
2016 年 11 月 3 日午前 9:27
7
I thought that was a pull request already… I’m as confused as always with Github.
I clicked some more green buttons now, maybe it does the trick?
rizka
(rizka)
2016 年 11 月 3 日午前 9:36
9
I think I’m getting it now. I ran into some Github tutorials online yesterday and I should really go through one to learn the basics.
「いいね!」 1
Awesome. Before we can merge the code, we’ll need you to sign the CLA. I promise, this is the last step (also, you only have to sign it once ;))
「いいね!」 6
rizka
(rizka)
2016 年 11 月 3 日午前 9:40
11
Thanks for all the help! I signed it now.
「いいね!」 4