Pregunta

Tenemos un costo C que debe asignarse a los departamentos 1..n. Otro cálculo produce la participación para cada departamento, es un número de 0 a 1, con más de 5 decimales. La suma de todas las acciones del departamento es exactamente 1, pero no son necesariamente iguales.

El objetivo es calcular los dólares y centavos exactos para facturar a cada departamento. La suma de la factura debe coincidir EXACTAMENTE con el costo C, no puede ser de unos centavos por debajo o por encima. Además, la parte de cada departamento no debe requerir centavos fraccionales. Además, aunque no es justo volcar el resto en el último departamento, no es necesario mirar hacia atrás en los plazos anteriores. Tenga en cuenta que el simple redondeo de la parte de cada departamento al centavo casi siempre da como resultado un exceso / disminución de unos pocos centavos.

Ejemplo extremadamente simplificado: C = 33.34, 4 departamentos cada uno con exactamente 0.2500 de participación. Debido a que 33.34 * 0.25 = 8.335 puede ver que dos departamentos deben pagar 8.33 y dos deben pagar 8.34. Una asignación correcta sería: d1 = 8.33, d2 = 8.34, d3 = 8.33, d4 = 8.34. Si realiza una ronda, cada departamento paga 8.34, lo que resulta en un exceso de $ 0.02. Si multiplica esto por muchos más departamentos y muchos más costos, terminará con discrepancias de cien dólares.

Quiero hacer esto en 1 pasada, es decir, no quiero recorrer, descubrir que estoy apagado en 0.02, luego repetir nuevamente y ajustar los valores hasta que sea correcto. Quiero hacer esto en 1 pasada. También me gustaría saber si este algoritmo tiene un nombre.

¿Fue útil?

Solución

En 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;
        

En Perl:

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

Salida:

<*> /= $denom for @shares; } return \@alloc; }

Salida:

<*>

Otros consejos

No uses flotadores. Use números decimales de punto fijo, o use ints de modo que pueda dividir por cualquiera que sea su factor de escala para obtener el valor en dólares.

Nunca, nunca, nunca calcule dinero usando flotadores.

Yo lo llamaría "total acumulado" e implementarlo de la siguiente manera:

  1. Calcule la parte adeudada por el departamento # 1 (puede tener errores de redondeo)
  2. Hacer que el departamento # 1 pague la cantidad calculada en el paso 1
  3. Calcule la participación que deben conjuntamente el departamento n. ° 1 y el departamento n. ° 2
  4. Haga que el departamento # 1 pague la cantidad calculada en el paso 3, menos la cantidad realmente pagada en el paso 2.
  5. Etc. por la cantidad que deben conjuntamente los depósitos n.º 1, 2 y 3.

Esto asegurará que el error experimentado por cualquier departamento sea como máximo un error de redondeo (no una suma de varios errores de redondeo), y que el total final sea correcto.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top