Considérez les quatre pourcentages ci-dessous, représentés sous forme de float
nombres:
13.626332%
47.989636%
9.596008%
28.788024%
-----------
100.000000%
J'ai besoin de représenter ces pourcentages sous forme de nombres entiers. Si j'utilise simplement Math.round()
, je me retrouve avec un total de 101%.
14 + 48 + 10 + 29 = 101
Si j'utilise parseInt()
, je me retrouve avec un total de 97%.
13 + 47 + 9 + 28 = 97
Qu'est-ce qu'un bon algorithme pour représenter n'importe quel nombre de pourcentages sous forme de nombres entiers tout en conservant un total de 100%?
Edit : Après avoir lu certains des commentaires et des réponses, il existe clairement de nombreuses façons de résoudre ce problème.
Dans mon esprit, pour rester fidèle aux chiffres, le "bon" résultat est celui qui minimise l'erreur globale, définie par la quantité d'erreurs d'arrondi introduite par rapport à la valeur réelle:
value rounded error decision
----------------------------------------------------
13.626332 14 2.7% round up (14)
47.989636 48 0.0% round up (48)
9.596008 10 4.0% don't round up (9)
28.788024 29 2.7% round up (29)
En cas d'égalité (3.33, 3.33, 3.33), une décision arbitraire peut être prise (par exemple 3, 4, 3).
la source
Réponses:
Étant donné qu'aucune des réponses ici ne semble le résoudre correctement, voici ma version semi-obscurcie utilisant des underscorejs :
la source
Il existe de nombreuses façons de procéder, à condition que vous ne soyez pas préoccupé par la dépendance aux données décimales d'origine.
La première méthode et peut-être la plus populaire serait la méthode du plus grand reste
Ce qui est fondamentalement:
Dans votre cas, cela ressemblerait à ceci:
Si vous prenez les parties entières, vous obtenez
ce qui fait 97, et vous voulez en ajouter trois de plus. Maintenant, vous regardez les parties décimales, qui sont
et prenez les plus gros jusqu'à ce que le total atteigne 100. Vous obtiendrez donc:
Sinon, vous pouvez simplement choisir d'afficher une décimale au lieu de valeurs entières. Les nombres seraient donc 48,3 et 23,9, etc. Cela réduirait considérablement la variance de 100.
la source
La "meilleure" façon de faire ceci (citée puisque "meilleur" est un terme subjectif) est probablement de garder un compte courant (non intégral) de votre position et d'arrondir cette valeur.
Ensuite, utilisez-le avec l'historique pour déterminer la valeur à utiliser. Par exemple, en utilisant les valeurs que vous avez données:
À chaque étape, vous n'arrondissez pas le nombre lui-même. Au lieu de cela, vous arrondissez la valeur accumulée et calculez le meilleur entier qui atteint cette valeur à partir de la ligne de base précédente - cette ligne de base est la valeur cumulative (arrondie) de la ligne précédente.
Cela fonctionne parce que vous ne perdez pas d' informations à chaque étape, mais utilisez plutôt les informations de manière plus intelligente. Les valeurs arrondies `` correctes '' se trouvent dans la dernière colonne et vous pouvez voir qu'elles totalisent 100.
Vous pouvez voir la différence entre cela et arrondir aveuglément chaque valeur, dans la troisième valeur ci-dessus. Alors
9.596008
que normalement arrondirait à10
, le cumul est71.211976
correctement arrondi à71
- cela signifie qu'il suffit9
d'ajouter à la ligne de base précédente de62
.Cela fonctionne également pour une séquence "problématique" comme trois valeurs approximatives , où l' une d'elles doit être arrondie:
1/3
la source
26, 25, 26, 23
, le second1, 0, 1, 0, 1, 0, ...
.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:
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.
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.
la source
if actual == 0: return 0
deerror_gen
grandes œuvres.isclose
méthode au début deround_to_100
?NE additionnez PAS les nombres arrondis. Vous allez avoir des résultats inexacts. Le total pourrait être considérablement réduit en fonction du nombre de termes et de la distribution des parties fractionnaires.
Affichez les nombres arrondis mais additionnez les valeurs réelles. Selon la façon dont vous présentez les chiffres, la manière réelle de procéder varie. De cette façon vous obtenez
De toute façon, vous allez avoir des divergences. Il n'y a aucun moyen dans votre exemple d'afficher des nombres qui totalisent 100 sans "arrondir" une valeur dans le mauvais sens (la moindre erreur serait de changer 9,596 en 9)
ÉDITER
Vous devez choisir entre l'une des options suivantes:
La plupart du temps, traiter des pourcentages n ° 3 est la meilleure option, car elle est plus évidente lorsque le total est égal à 101% que lorsque les éléments individuels ne totalisent pas 100, et vous gardez les éléments individuels précis. «Arrondir» 9,596 à 9 est à mon avis inexact.
Pour expliquer cela, j'ajoute parfois une note de bas de page qui explique que les valeurs individuelles sont arrondies et peuvent ne pas totaliser 100% - toute personne qui comprend l'arrondi devrait être en mesure de comprendre cette explication.
la source
J'ai écrit un assistant d'arrondi de la version C #, l'algorithme est le même que la réponse de Varun Vohra , j'espère que cela aide.
Il réussit le test unitaire suivant:
la source
Vous pouvez essayer de garder une trace de votre erreur due à l'arrondissement, puis arrondir dans le sens contraire du grain si l'erreur accumulée est supérieure à la partie fractionnaire du nombre actuel.
Je ne sais pas si cela fonctionnerait en général, mais cela semble fonctionner de la même manière si l'ordre est inversé:
Je suis sûr qu'il y a des cas extrêmes où cela pourrait échouer, mais toute approche sera au moins quelque peu arbitraire puisque vous modifiez fondamentalement vos données d'entrée.
la source
Une fois, j'ai écrit un outil non arrondi, pour trouver la perturbation minimale d'un ensemble de nombres correspondant à un objectif. C'était un problème différent, mais on pourrait en théorie utiliser une idée similaire ici. Dans ce cas, nous avons un ensemble de choix.
Ainsi, pour le premier élément, nous pouvons l'arrondir à 14 ou à 13. Le coût (au sens de la programmation en nombres entiers binaires) est moindre pour l'arrondi vers le haut que pour l'arrondi vers le bas, car l'arrondi vers le bas nécessite que nous déplacez cette valeur sur une plus grande distance. De même, nous pouvons arrondir chaque nombre à la hausse ou à la baisse, il y a donc un total de 16 choix parmi lesquels nous devons choisir.
Je résoudrais normalement le problème général dans MATLAB, ici en utilisant bintprog, un outil de programmation d'entiers binaires, mais il n'y a que quelques choix à tester, il est donc assez facile avec des boucles simples de tester chacune des 16 alternatives. Par exemple, supposons que nous arrondissions cet ensemble comme suit:
L'erreur absolue totale commise est de 1,25266. Il peut être légèrement réduit par l'arrondi alternatif suivant:
En fait, ce sera la solution optimale en termes d'erreur absolue. Bien sûr, s'il y avait 20 termes, l'espace de recherche sera de taille 2 ^ 20 = 1048576. Pour 30 ou 40 termes, cet espace sera de taille significative. Dans ce cas, vous devrez utiliser un outil capable de rechercher efficacement l'espace, peut-être en utilisant un schéma de branche et lié.
la source
Je pense que ce qui suit réalisera ce que vous recherchez
Une dernière chose, j'ai exécuté la fonction en utilisant les nombres initialement donnés dans la question pour comparer à la sortie souhaitée
C'était différent de ce que voulait la question => [48, 29, 14, 9]. Je ne pouvais pas comprendre cela avant d'avoir examiné la marge d'erreur totale
Essentiellement, le résultat de ma fonction introduit en fait le moins d'erreur possible.
Violon ici
la source
Je ne suis pas sûr du niveau de précision dont vous avez besoin, mais ce que je ferais, c'est simplement ajouter 1 les premiers
n
nombres,n
soit le plafond de la somme totale des décimales. Dans ce cas3
, j'ajouterais donc 1 aux 3 premiers éléments et j'appliquerais le reste. Bien sûr, ce n'est pas très précis, certains nombres peuvent être arrondis à la hausse ou à la baisse alors que cela ne devrait pas, mais cela fonctionne bien et donnera toujours 100%.Ce
[ 13.626332, 47.989636, 9.596008, 28.788024 ]
serait aussi[14, 48, 10, 28]
parce queMath.ceil(.626332+.989636+.596008+.788024) == 3
Vous pouvez toujours informer les utilisateurs que les nombres sont arrondis et peuvent ne pas être très précis ...
la source
Si vous l'arrondissez, il n'y a pas de bon moyen d'obtenir exactement la même chose dans tous les cas.
Vous pouvez prendre la partie décimale des N pourcentages que vous avez (dans l'exemple que vous avez donné, c'est 4).
Ajoutez les parties décimales. Dans votre exemple, vous avez un total de partie fractionnaire = 3.
Placer les 3 nombres avec les fractions les plus élevées et plancher le reste.
(Désolé pour les modifications)
la source
Si vous devez vraiment les arrondir, il y a déjà de très bonnes suggestions ici (reste le plus important, erreur relative la moins élevée, etc.).
Il y a aussi déjà une bonne raison de ne pas arrondir (vous obtiendrez au moins un numéro qui "semble mieux" mais qui est "faux"), et comment résoudre cela (prévenez vos lecteurs) et c'est ce que je fais.
Permettez-moi d'ajouter la «mauvaise» partie du numéro.
Supposons que vous ayez trois événements / entités / ... avec des pourcentages que vous approximez comme:
Plus tard, les valeurs changent légèrement pour devenir
Le premier tableau a le problème déjà mentionné d'avoir un "faux" numéro: 33,34 est plus proche de 33 que de 34.
Mais maintenant, vous avez une erreur plus grave. En comparant le jour 2 au jour 1, la valeur réelle en pourcentage de A a augmenté de 0,01%, mais l'approximation montre une diminution de 1%.
C'est une erreur qualitative, probablement bien pire que l'erreur quantitative initiale.
On pourrait imaginer une approximation pour l'ensemble complet, mais vous devrez peut-être publier des données le premier jour, vous ne saurez donc pas le jour deux. Donc, à moins que vous ne deviez vraiment, vraiment, vous rapprocher, vous feriez probablement mieux de ne pas le faire.
la source
vérifier si cela est valide ou pas dans la mesure où mes cas de test, je suis capable de faire fonctionner cela.
disons que le nombre est k;
la source
J'ai implémenté la méthode de la réponse de Varun Vohra ici pour les listes et les dictionnaires.
la source
Voici une implémentation Python plus simple de la réponse @ varun-vohra:
Vous avez besoin
math
,itertools
,operator
.la source
Pour ceux qui ont les pourcentages dans une série de pandas, voici mon implémentation de la méthode du plus grand reste (comme dans la réponse de Varun Vohra ), où vous pouvez même sélectionner les décimales auxquelles vous voulez arrondir.
la source
C'est un cas pour l'arrondi des banquiers, alias «rond demi-pair». Il est pris en charge par BigDecimal. Son but est de s'assurer que l'arrondi s'équilibre, c'est-à-dire ne favorise ni la banque ni le client.
la source