8

コードでこの問題を解決するための最良の方法は何ですか?

問題は、私が2ドルの金額(ポットと呼ばれる)を持っていることです。これは3人に割り当てる必要があります。各人は両方のポットから得られる特定の金額を受け取り、レートはほぼ同じでなければなりません。割り当ての合計が多すぎたり少なすぎたりする丸めの問題に遭遇し続けます。

具体的な例を次に示します。

ポット#1 987,654.32
ポット#2 123,456.78

人#1は割り当て量を取得します:345,678.89
人#2は割り当て量を取得します:460,599.73
人#3は割り当て量を取得します:304,832.48

私のロジックは次のとおりです(コードはc#にあります):

foreach (Person person in People)
{
    decimal percentage = person.AllocationAmount / totalOfAllPots;

    decimal personAmountRunningTotal = person.AllocationAmount;

    foreach (Pot pot in pots)
    {
        decimal potAllocationAmount = Math.Round(percentage * pot.Amount, 2);
        personAmountRunningTotal -= potAllocationAmount;

        PersonPotAssignment ppa = new PersonPotAssignment();
        ppa.Amount = potAllocationAmount;

        person.PendingPotAssignments.Add(ppa);
    }

    foreach (PersonPotAssignment ppa in person.PendingPotAssignments)
    {
        if (personAmountRunningTotal > 0) //Under Allocated
        {
            ppa.Amount += .01M;
            personAmountRunningTotal += .01M;
        }
        else if (personAmountRunningTotal < 0) //Over Allocated
        {
            ppa.Amount -= .01M;
            personAmountRunningTotal -= .01M;
        }
    }
}

私が得た結果は次のとおりです。

ポット#1、人#1 = 307,270.13
ポット#1、人#2 = 409,421.99
ポット#1、人#3 = 270,962.21
ポット#1合計= 987,654.33(1ペニーオフ)

ポット#2、人#1 = 38,408.76
ポット#2、人#2 = 51,177.74
ポット#2、人#3 = 33,870.27
ポット#2合計= 123,456.77(1ペニーオフ)

ポットの合計は、元の合計と一致する必要があります。

私は何かが足りないか、私が取らなければならない余分なステップがあるかもしれないと思います。私は正しい方向に進んでいると思います。

どんな助けでも大歓迎です。

4

5 に答える 5

14

これは、最も近いペニーに丸めるときに財務計算でよく発生します。個々の操作の丸めアルゴリズムを微調整しても、すべての場合に機能するわけではありません。

丸めおよび分配操作の後に割り当てられた量を追跡するアキュムレータが必要です。割り当ての最後に、アキュムレータを実際の結果(合計)と照合し、残りのペニーを分配します。

以下の数学の例では、0.133を取り、それを0.13に丸めて3回追加すると、最初に0.133を3回追加してから丸めた場合よりも、ペニーが少なくなります。

 0.13    0.133
 0.13    0.133
+0.13   +0.133
_____   ______
 0.39    0.399 -> 0.40
于 2009-05-29T02:21:46.150 に答える
3

MattSpradleyのソリューションの+1。

マットの解決策への追加のコメントとして、もちろん、目標額よりも少ないペニー(またはそれ以上)を割り当てることになった場合も考慮する必要があります-その場合、1つ以上の割り当てられた金額。

また、割り当てられた金額$ 0.00からペニーを差し引くことにならないようにする必要があります(多数の受信者にごくわずかな金額を割り当てている場合)。

于 2009-05-29T02:52:09.347 に答える
2

MidpointRounding引数を使用して丸め動作を制御しようとしましたか?

public static decimal Round( decimal d, MidpointRounding mode )
于 2009-05-29T01:42:42.640 に答える
1

お金を分割するときに何をすべきかは、長年の問題です。マーティンファウラーはここでいくつかの解説を提供しています(私は彼の実際のPoEAA本にもっと詳細があると思います):

しかし、誤ったペニーの世話をしなければならないので、分割は[簡単]ではありません。これを行うには、配列の合計が元の量と等しくなり、元の量が配列の要素間で公平に分散されるように、金銭の配列を返します。この意味でかなり、最初の人は余分なペニーを手に入れることを意味します。

class Money... 
    public Money[] divide(int denominator) {
        BigInteger bigDenominator = BigInteger.valueOf(denominator);
        Money[] result = new Money[denominator];
        BigInteger simpleResult = amount.divide(bigDenominator);
        for (int i = 0; i < denominator ; i++) {
            result[i] = new Money(simpleResult, currency, true);
        }
        int remainder = amount.subtract(simpleResult.multiply(bigDenominator)).intValue();
        for (int i=0; i < remainder; i++) {
            result[i] = result[i].add(new Money(BigInteger.valueOf(1), currency, true));
        }
        return result;
    }
于 2009-05-29T10:43:18.867 に答える
0

間違いなくMath.Round。

計算結果は四捨五入しないことをお勧めしますが、表示する必要がある場合は、最も近いペニーに四捨五入してください。または、ペニーを最小の分母として使用することもできます。したがって、表示するときは、すべてを100で割ります。

于 2009-05-29T01:37:41.557 に答える