Pergunta

Nós temos um custo C, que deve ser atribuído a departamentos 1..n. Outro cálculo produz a quota para cada departamento, é um número de 0 a 1, com mais de 5 casas decimais. A soma de todas as ações de departamento é exatamente 1, mas eles não são necessariamente iguais.

O objetivo é calcular os dólares exatas e centavos para projeto de lei para cada departamento. A soma da conta deve corresponder exatamente ao custo C, não pode ser alguns centavos abaixo ou acima. Além disso, a participação de cada departamento não deve ser exigem centavos fracionários. Além disso, embora não é justo simplesmente despejar o restante para o último departamento, não é necessário olhar para trás para prazos anteriores. Observe que o simples arredondamento parte de cada departamento para a moeda quase sempre resulta em um acima / abaixo de alguns centavos.

grosseiramente simplificada exemplo: C = 33,34, 4 departamentos cada um com exatamente 0,2500 ação. Porque 33,34 * 0,25 = 8,335, assim você pode ver que dois departamentos devem pagar 8,33 e dois deve pagar 8,34. Uma atribuição correta seria: d1 = 8,33, d2 = 8,34, d3 = 8,33, d4 = 8,34. Se você volta, cada departamento paga 8,34 o que resulta em um excesso de US $ 0,02. Se você multiplicar isso por muito mais departamentos e muitos mais custos, você acaba com cem discrepâncias dólar.

Eu quero fazer isso em 1 passe, ou seja, eu não quero percorrer, descobrir que eu estou fora de 0,02, então os valores de loop novamente e ajustar até que ele está correto. Eu quero fazer isso em uma passagem. Eu também gostaria de saber se este algoritmo tem um nome.

Foi útil?

Solução

Em 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;
}

Output:

$VAR1 = [
          '2300.00',
          '3700.00',
          '4000.00'
        ];
$VAR1 = [
          '8.34',
          '8.33',
          '8.34',
          '8.33'
        ];
$VAR1 = [
          '33.33',
          '33.34',
          '33.33'
        ];

Outras dicas

Não use carros alegóricos. Use números, quer fixas do ponto decimal ou uso ints de tal forma que você pode dividir por qualquer que seja seu fator de escala é obter o valor em dólares.

Nunca, jamais, nunca dinheiro calcular usando carros alegóricos.

eu diria que é "execução total" e implementá-lo da seguinte forma:

  1. Calcular a parcela devida pelo dept # 1 (pode ter erros de arredondamento)
  2. Faça dept # 1 pagar o valor calculado no passo 1
  3. calcular a quota que é devido em conjunto pelo departamento # 1 e # 2 dept
  4. Faça dept # 1 pagamento do montante apurado no passo 3, menos o montante efectivamente pago no passo 2.
  5. Etc. para o montante que é devido em conjunto pela depts #S 1, 2 e 3.

Isso irá garantir que o erro experimentado por qualquer departamento é no máximo um erro de arredondamento (não é uma soma de vários erros de arredondamento), e que o total final está correto.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top