정확한 단일 패스 페니 계산
-
22-07-2019 - |
문제
부서 1..N에 할당 해야하는 비용 C가 있습니다. 또 다른 계산은 각 부서의 점유율을 생성하며, 0에서 1까지의 숫자이며, 10 진수 자리가 5 개 이상입니다. 모든 부서 주식의 합은 정확히 1이지만 반드시 같을 필요는 없습니다.
목표는 정확한 달러와 센트를 각 부서에 청구하는 것을 계산하는 것입니다. 청구서의 합은 비용 C와 정확히 일치해야하며, 몇 개의 동전이 될 수는 없습니다. 또한 각 부서의 비중은 분수 동전이 필요하지 않아야합니다. 또한 나머지 부서를 마지막 부서에 버리는 것은 공정하지 않지만 이전 기간을 되돌아 볼 필요는 없습니다. 단순히 각 부서의 지분을 페니에 반올림하면 거의 항상 몇 개의 동전의 오버/아래에 있습니다.
크게 지나치게 단순화 된 예 : C = 33.34, 각각 정확히 0.2500 점유율의 부서. 33.34 * 0.25 = 8.335이므로 두 부서가 8.33을 지불해야하고 두 부서는 8.34를 지불해야한다는 것을 알 수 있습니다. 올바른 할당은 다음과 같습니다. D1 = 8.33, D2 = 8.34, D3 = 8.33, D4 = 8.34입니다. 당신이 반올림하는 경우, 모든 부서는 8.34를 지불하여 $ 0.02의 초과를 초과합니다. 더 많은 부서와 더 많은 비용을 곱하면 수백 달러의 불일치로 끝납니다.
나는 1 패스로 이것을하고 싶다. 즉, 나는 루프를 통과하고 싶지 않다. 나는 0.02에 의해 꺼져 있다는 것을 알게 된 다음 다시 루프하고 그것이 올바른 값을 조정한다. 1 패스로 이것을하고 싶습니다. 이 알고리즘에 이름이 있는지 알고 싶습니다.
해결책
Perl에서 :
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
use List::AllUtils qw( sum );
print Dumper allocate(10_000, .23, .37, .4);
print Dumper allocate(33.34, .25, .25, .25, .25);
print Dumper allocate(100, 1/3, 1/3, 1/3);
sub allocate {
my ($C, @shares) = @_;
my @alloc;
while ( my $share = shift @shares ) {
push @alloc, sprintf '%.2f', $C * $share;
$C -= $alloc[-1];
my $denom = sum @shares;
$_ /= $denom for @shares;
}
return \@alloc;
}
산출:
$VAR1 = [ '2300.00', '3700.00', '4000.00' ]; $VAR1 = [ '8.34', '8.33', '8.34', '8.33' ]; $VAR1 = [ '33.33', '33.34', '33.33' ];
다른 팁
플로트를 사용하지 마십시오. 고정점 소수점 숫자를 사용하거나 ints를 사용하여 스케일링 계수로 값을 달러로 얻는 것이 무엇이든 나눌 수 있습니다.
절대로, 항상 플로트를 사용하여 돈을 계산하십시오.
나는 이것을 "Total Runing"이라고 부르고 다음과 같이 구현하겠습니다.
- 부서 #1이 빚진 주식을 계산합니다 (반올림 오류가있을 수 있음)
- 1 단계에서 계산 된 금액을 지불하는 부서 #1을 지불하십시오.
- 부서 1과 부서 #2가 공동으로 빚진 주식을 계산하십시오.
- Dept #1을 3 단계에서 계산 된 금액을 지불하십시오.
- #S 1, 2 및 3에 의해 공동으로 빚진 금액.
이렇게하면 모든 부서에서 경험 한 오류가 최대 하나의 반올림 오류 (여러 반올림 오류의 합이 아님)이며 최종 총계가 정확하도록합니다.