Domanda

Abbiamo un costo C che deve essere assegnato ai dipartimenti 1..n. Un altro calcolo produce la condivisione per ogni dipartimento, è un numero compreso tra 0 e 1, con più di 5 cifre decimali. La somma di tutte le azioni del dipartimento è esattamente 1, ma non sono necessariamente uguali.

L'obiettivo è calcolare i dollari e i centesimi esatti da fatturare a ciascun dipartimento. La somma del conto deve corrispondere ESATTAMENTE al costo C, non può essere inferiore o inferiore a qualche centesimo. Inoltre, la quota di ciascun dipartimento non deve richiedere centesimi frazionari. Inoltre, sebbene non sia giusto scaricare il resto nell'ultimo reparto, non è necessario guardare indietro ai tempi precedenti. Si noti che semplicemente arrotondando la quota di ciascun dipartimento al centesimo si ottiene quasi sempre un over / under di pochi centesimi.

Esempio grossolanamente semplificato: C = 33.34, 4 dipartimenti ciascuno con esattamente 0,2500 quote. Perché 33.34 * 0.25 = 8.335 quindi puoi vedere che due dipartimenti devono pagare 8.33 e due devono pagare 8.34. Un'assegnazione corretta sarebbe: d1 = 8.33, d2 = 8.34, d3 = 8.33, d4 = 8.34. Se giri, ogni dipartimento paga 8,34 che si traduce in un eccesso di $ 0,02. Se si moltiplica questo per molti più dipartimenti e molti più costi, si ottengono discrepanze di centinaia di dollari.

Voglio farlo in 1 passaggio, cioè non voglio fare il ciclo continuo, scoprire che sono spento per 0,02, quindi ripetere il ciclo e modificare i valori fino a quando non è corretto. Voglio farlo in 1 passaggio. Vorrei anche sapere se questo algoritmo ha un nome.

È stato utile?

Soluzione

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

In Perl:

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

Output:

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

Output:

<*>

Altri suggerimenti

Non usare float. Usa i numeri decimali in virgola fissa o usa in modo tale da poter dividere per qualunque sia il tuo fattore di scala per ottenere il valore in dollari.

Mai, mai, mai calcolare denaro usando i float.

Lo chiamerei " totale parziale " e implementarlo come segue:

  1. Calcola la quota dovuta dal dipartimento n. 1 (potrebbe contenere errori di arrotondamento)
  2. Fai in modo che il reparto 1 paghi l'importo calcolato nel passaggio 1
  3. Calcola la quota dovuta congiuntamente dal dipartimento n. 1 e dal reparto n. 2
  4. Fai in modo che il reparto 1 paghi l'importo calcolato nel passaggio 3, meno l'importo effettivamente pagato nel passaggio 2.
  5. Ecc. per l'importo dovuto congiuntamente dai dipartimenti n. 1, 2 e 3.

Ciò garantirà che l'errore riscontrato da qualsiasi reparto sia al massimo un errore di arrotondamento (non una somma di più errori di arrotondamento) e che il totale finale sia corretto.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top