Frage

Wir haben eine Kosten C, die 1..n Abteilungen zugeordnet werden müssen. Eine weitere Berechnung erzeugt für jede Abteilung, den Anteil, es ist eine Zahl von 0 bis 1, mit mehr als 5 Dezimalstellen. Die Summe aller Abteilung Aktien genau 1, aber sie sind nicht unbedingt gleich.

Das Ziel ist es, die genauen Dollar und Cent zu Rechnung zu jeder Abteilung zu berechnen. Die Summe der Rechnung muss genau die Kosten C übereinstimmen, kann es nicht ein paar Cent unter oder über sein. Darüber hinaus muss jeder Abteilung Anteil erfordern werden nicht fraktionierte ein paar Cent. Zusätzlich ist, obwohl es nicht fair ist, nur den Rest auf die letzte Abteilung Dump, ist es nicht notwendig schauen zurück zur vorherige Zeitrahmen. Beachten Sie, dass einfach Rundung Anteil jeder Abteilung auf den Cent fast immer in einer über Ergebnisse / unter der ein paar Cent.

Grossly vereinfachend Beispiel: C = 33,34, 4 Abteilungen mit jeweils exakt 0,2500 Aktien. Da 33.34 * 0,25 = 8,335, so können Sie sehen, dass zwei Abteilungen 8,33 zahlen müssen und zwei 8,34 bezahlen müssen. Eine korrekte Zuordnung wäre: d1 = 8,33, d2 = 8,34, d3 = 8,33, d4 = 8,34. Wenn Sie runden, zahlt jede Abteilung 8.34, die in einem overage von 0,02 $ ergibt. Wenn Sie dies durch viele Abteilungen vermehren und viele mehr Kosten, erhalten Sie hundert Dollar Diskrepanzen auf.

Das möchte ich Pass in 1 tun, das heißt, ich will nicht, eine Schleife durch, herauszufinden, ich bin weg von 0,02, dann Schleife wieder und optimierten Werte, bis es richtig ist. Ich möchte dies in 1 Durchgang tun. Ich würde auch gerne wissen, ob dieser Algorithmus einen Namen hat.

War es hilfreich?

Lösung

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;
        $_ /= $denom for @shares;
    }

    return \@alloc;
}

Ausgabe:

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

Andere Tipps

Sie nicht schwimmt verwenden. Verwenden Sie entweder feste Punkt Dezimalzahlen oder Verwendung Ints, so dass Sie teilen können, was auch immer Ihr Skalierungsfaktor ist, den Wert in US-Dollar zu erhalten.

Nie, nie, immer berechnet Geld mit Schwimmern.

Ich würde nennen es „laufende Summe“ und setzen es wie folgt:

  1. Berechnen Sie den Anteil von Abt # geschuldeten 1 (können Rundungsfehler haben)
  2. Erstellen 1 dept #, um die Menge des in Schritt 1
  3. berechnet zahlen
  4. Berechnen Sie die Freigabe, die von Abt # 1 und # 2 Abt
  5. gemeinsam geschuldet
  6. Erstellen 1 dept #, um die Menge des in Schritt 3 minus die Menge tatsächlich in Schritt 2 bezahlte berechnet wird.
  7. Etc. für die Menge, die von depts # s 1, 2 und 3
  8. gemeinsam geschuldet

Dadurch wird sichergestellt, dass der Fehler von jeder Abteilung erfahren wird, ist höchstens ein Rundungsfehler (nicht eine Summe von mehreren Rundungsfehlern), und dass die Endsumme richtig sind.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top