Le but de l'arrondi est de générer le moins d'erreur possible. Lorsque vous arrondissez une valeur unique, ce processus est simple et direct et la plupart des gens le comprennent facilement. Lorsque vous arrondissez plusieurs nombres en même temps, le processus devient plus délicat - vous devez définir comment les erreurs vont se combiner, c'est-à-dire ce qui doit être minimisé.
La réponse bien votée de Varun Vohra minimise la somme des erreurs absolues et est très simple à mettre en œuvre. Cependant, il y a des cas extrêmes qu'il ne gère pas - quel devrait être le résultat de l'arrondissement 24.25, 23.25, 27.25, 25.25
? L'un de ces éléments doit être arrondi à la hausse plutôt qu'à la baisse. Vous choisiriez probablement arbitrairement le premier ou le dernier de la liste.
Il vaut peut-être mieux utiliser l' erreur relative au lieu de l' erreur absolue . Arrondir 23,25 à 24 le change de 3,2% tandis que arrondir 27,25 à 28 ne le change que de 2,8%. Maintenant, il y a un gagnant clair.
Il est possible de peaufiner cela encore plus. Une technique courante consiste à mettre au carré chaque erreur, de sorte que les grandes erreurs comptent disproportionnellement plus que les petites. J'utiliserais également un diviseur non linéaire pour obtenir l'erreur relative - il ne semble pas juste qu'une erreur à 1% soit 99 fois plus importante qu'une erreur à 99%. Dans le code ci-dessous, j'ai utilisé la racine carrée.
L'algorithme complet est le suivant:
- Additionnez les pourcentages après les avoir tous arrondis et soustrayez de 100. Cela vous indique combien de ces pourcentages doivent être arrondis à la place.
- Générez deux scores d'erreur pour chaque pourcentage, l'un lorsqu'il est arrondi vers le bas et l'autre lorsqu'il est arrondi vers le haut. Faites la différence entre les deux.
- Triez les différences d'erreur produites ci-dessus.
- Pour le nombre de pourcentages qui doivent être arrondis, prenez un élément de la liste triée et incrémentez le pourcentage arrondi de 1.
Vous pouvez toujours avoir plus d'une combinaison avec la même somme d'erreurs, par exemple 33.3333333, 33.3333333, 33.3333333
. Cela est inévitable et le résultat sera complètement arbitraire. Le code que je donne ci-dessous préfère arrondir les valeurs à gauche.
Tout rassembler en Python ressemble à ceci.
def error_gen(actual, rounded):
divisor = sqrt(1.0 if actual < 1.0 else actual)
return abs(rounded - actual) ** 2 / divisor
def round_to_100(percents):
if not isclose(sum(percents), 100):
raise ValueError
n = len(percents)
rounded = [int(x) for x in percents]
up_count = 100 - sum(rounded)
errors = [(error_gen(percents[i], rounded[i] + 1) - error_gen(percents[i], rounded[i]), i) for i in range(n)]
rank = sorted(errors)
for i in range(up_count):
rounded[rank[i][1]] += 1
return rounded
>>> round_to_100([13.626332, 47.989636, 9.596008, 28.788024])
[14, 48, 9, 29]
>>> round_to_100([33.3333333, 33.3333333, 33.3333333])
[34, 33, 33]
>>> round_to_100([24.25, 23.25, 27.25, 25.25])
[24, 23, 28, 25]
>>> round_to_100([1.25, 2.25, 3.25, 4.25, 89.0])
[1, 2, 3, 4, 90]
Comme vous pouvez le voir avec ce dernier exemple, cet algorithme est toujours capable de fournir des résultats non intuitifs. Même si 89,0 ne nécessite aucun arrondi, l'une des valeurs de cette liste a dû être arrondie; l'erreur relative la plus faible résulte de l'arrondissement de cette grande valeur plutôt que des alternatives beaucoup plus petites.
Cette réponse recommandait à l'origine de passer par toutes les combinaisons possibles d'arrondi vers le haut / arrondi vers le bas, mais comme indiqué dans les commentaires, une méthode plus simple fonctionne mieux. L'algorithme et le code reflètent cette simplification.