Рассмотрим четыре процента ниже, представленные в виде float
чисел:
13.626332%
47.989636%
9.596008%
28.788024%
-----------
100.000000%
Мне нужно представить эти проценты в виде целых чисел. Если я просто использую Math.round()
, у меня получается 101%.
14 + 48 + 10 + 29 = 101
Если я использую parseInt()
, я получаю в итоге 97%.
13 + 47 + 9 + 28 = 97
Какой хороший алгоритм для представления любого количества процентов в виде целых чисел при сохранении в общей сложности 100%?
Изменить : После прочтения некоторых комментариев и ответов, есть много способов решить эту проблему.
На мой взгляд, чтобы оставаться верным числам, «правильный» результат - это тот, который сводит к минимуму общую ошибку, определяемую тем, сколько округления ошибок может привести к действительному значению:
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)
В случае ничьей (3.33, 3.33, 3.33) может быть принято произвольное решение (например, 3, 4, 3).
источник
Ответы:
Так как ни один из ответов здесь, кажется, не решает это должным образом, вот моя полуобфусцированная версия, использующая подчеркивание :
источник
Есть много способов сделать это, при условии, что вы не обеспокоены использованием исходных десятичных данных.
Первым и, возможно, самым популярным методом будет метод наибольших остатков
Что в основном:
В вашем случае это будет выглядеть так:
Если вы берете целочисленные части, вы получите
который добавляет до 97, и вы хотите добавить еще три. Теперь вы смотрите на десятичные части, которые
и возьмите самые большие, пока общее число не достигнет 100. Таким образом, вы получите:
В качестве альтернативы вы можете просто выбрать показ одного десятичного знака вместо целочисленных значений. Таким образом, числа будут 48,3 и 23,9 и т. Д. Это значительно снизит дисперсию от 100.
источник
Вероятно, «лучший» способ сделать это (цитируется, поскольку «лучший» является субъективным термином) - это поддерживать (нецелую) подсчет того, где вы находитесь, и округлять это значение.
Затем используйте это вместе с историей, чтобы понять, какое значение следует использовать. Например, используя значения, которые вы дали:
На каждом этапе вы не округляете само число. Вместо этого вы округляете накопленное округляете значение и вычисляете наилучшее целое число, которое достигает этого значения из предыдущей базовой линии - эта базовая линия является совокупным значением (округленным) предыдущей строки.
Это работает, потому что вы не теряете информацию на каждом этапе, а используете ее более разумно. «Правильные» округленные значения находятся в последнем столбце, и вы можете видеть, что они составляют 100.
Вы можете увидеть разницу между этим и слепым округлением каждого значения в третьем значении выше. Хотя
9.596008
обычно округляется до10
, накопленное71.211976
правильно округляется до71
- это означает, что9
нужно только добавить к предыдущему базовому значению62
.Это также работает для «проблемной» последовательности, например трех грубых значений, где одно из них должно быть округлено:
1/3
источник
26, 25, 26, 23
, второй1, 0, 1, 0, 1, 0, ...
.Цель округления - генерировать наименьшее количество ошибок. Когда вы округляете одно значение, этот процесс прост и понятен, и большинство людей легко это понимают. Когда вы округляете несколько чисел одновременно, процесс усложняется - вы должны определить, как ошибки будут объединяться, то есть что должно быть сведено к минимуму.
Хорошо проголосовали ответ на Varun Вохра минимизирует сумму абсолютных ошибок, и это очень просто реализовать. Однако есть крайние случаи, которые он не обрабатывает - что должно быть результатом округления
24.25, 23.25, 27.25, 25.25
? Один из них должен быть округлен вверх, а не вниз. Вы, вероятно, просто произвольно выберете первый или последний в списке.Возможно, лучше использовать относительную ошибку вместо абсолютной ошибки. Округление с 23.25 до 24 изменяет его на 3.2%, а при округлении 27.25 до 28 - только 2.8%. Теперь есть явный победитель.
Это можно настроить еще дальше. Одним из распространенных методов является возведение в квадрат каждой ошибки, так что большие ошибки считаются непропорционально больше, чем маленькие. Я бы также использовал нелинейный делитель для получения относительной ошибки - кажется неправильным, что ошибка в 1% в 99 раз важнее, чем ошибка в 99%. В приведенном ниже коде я использовал квадратный корень.
Полный алгоритм выглядит следующим образом:
У вас может быть несколько комбинаций с одной и той же суммой ошибок, например
33.3333333, 33.3333333, 33.3333333
. Это неизбежно, и результат будет совершенно произвольным. Код, который я даю ниже, предпочитает округлять значения слева.Собрать все это вместе в Python выглядит следующим образом.
Как вы можете видеть из этого последнего примера, этот алгоритм все еще способен давать неинтуитивные результаты. Хотя 89.0 не нуждается ни в каком округлении, одно из значений в этом списке необходимо округлить; самая низкая относительная ошибка является результатом округления этого большого значения, а не намного меньших альтернатив.
Этот ответ первоначально предусматривал прохождение всех возможных комбинаций округления вверх / вниз, но, как указано в комментариях, более простой метод работает лучше. Алгоритм и код отражают это упрощение.
источник
if actual == 0: return 0
кerror_gen
работе прекрасно.isclose
метод в началеround_to_100
?НЕ суммируйте округленные числа. У вас будут неточные результаты. Общее количество может быть значительно меньше в зависимости от количества слагаемых и распределения дробных частей.
Показать округленные числа, но суммировать фактические значения. В зависимости от того, как вы представляете цифры, реальный способ сделать это будет различным. Таким образом, вы получаете
В любом случае вы будете иметь расхождение. В вашем примере нет способа показать числа, которые складываются до 100, без «округления» одного значения в неправильном направлении (наименьшая ошибка изменила бы 9,596 на 9)
РЕДАКТИРОВАТЬ
Вам нужно выбрать один из следующих вариантов:
Большую часть времени при работе с процентами # 3 - это лучший вариант, потому что он более очевиден, когда общая сумма равна 101%, чем когда отдельные элементы не равны 100, и вы сохраняете точность отдельных элементов. «Округление» с 9.596 до 9, на мой взгляд, неточно.
Чтобы объяснить это, я иногда добавляю сноску, объясняющую, что отдельные значения округлены и могут не составлять 100% - любой, кто понимает округление, должен быть в состоянии понять это объяснение.
источник
Я написал помощник по округлению версии C #, алгоритм такой же, как и у ответа Варуна Вохры , надеюсь, это поможет.
Проходят следующие юнит-тесты:
источник
Вы можете попытаться отследить свою ошибку из-за округления, а затем округлить до зернистости, если накопленная ошибка больше, чем дробная часть текущего числа.
Не уверен, что это будет работать в целом, но, похоже, работает аналогично, если порядок обратный:
Я уверен, что есть крайние случаи, когда это может сломаться, но любой подход будет по крайней мере несколько произвольным, так как вы в основном изменяете свои входные данные.
источник
Однажды я написал инструмент unround, чтобы найти минимальное возмущение для набора чисел, соответствующего цели. Это была другая проблема, но теоретически можно использовать аналогичную идею здесь. В этом случае у нас есть множество вариантов.
Таким образом, для первого элемента мы можем либо округлить его до 14, либо до 13. Стоимость (в смысле двоичного целочисленного программирования) для этого меньше для округления вверх, чем для округления вниз, потому что округление вниз требует от нас переместите это значение на большее расстояние. Точно так же мы можем округлить каждое число вверх или вниз, поэтому мы должны выбрать из 16 вариантов.
Обычно я решал бы общую проблему в MATLAB, используя здесь bintprog, инструмент двоичного целочисленного программирования, но есть только несколько вариантов для тестирования, поэтому с помощью простых циклов достаточно просто протестировать каждый из 16 вариантов. Например, предположим, что мы должны были округлить этот набор как:
Общая абсолютная ошибка составляет 1,25266. Его можно немного уменьшить с помощью следующего альтернативного округления:
Фактически это будет оптимальным решением с точки зрения абсолютной погрешности. Конечно, если бы было 20 терминов, пространство поиска будет иметь размер 2 ^ 20 = 1048576. Для 30 или 40 терминов это пространство будет иметь значительный размер. В этом случае вам нужно будет использовать инструмент, который может эффективно искать пространство, возможно, используя схему ветвей и границ.
источник
Я думаю, что следующее достигнет того, что вы после
И последнее, я запустил функцию, используя числа, приведенные в вопросе для сравнения с желаемым результатом.
Это отличалось от того, что хотел вопрос => [48, 29, 14, 9]. Я не мог этого понять, пока не посмотрел на общую погрешность
По сути, результат от моей функции фактически вносит наименьшее количество ошибок.
Скрипка здесь
источник
Я не уверен, какой уровень точности вам нужен, но я бы просто добавил 1 к первым
n
числам,n
являющимся пределом общей суммы десятичных дробей. В данном случае это3
так, поэтому я бы добавил 1 к первым 3 пунктам и оставил остальные. Конечно, это не очень точно, некоторые числа могут быть округлены в большую или меньшую сторону, если это не так, но это работает нормально и всегда приводит к 100%.Так
[ 13.626332, 47.989636, 9.596008, 28.788024 ]
было бы,[14, 48, 10, 28]
потому чтоMath.ceil(.626332+.989636+.596008+.788024) == 3
Вы всегда можете сообщить пользователям, что числа округлены и могут быть не очень точными ...
источник
Если вы округлите его, нет хорошего способа получить его точно таким же во всех случаях.
Вы можете взять десятичную часть N процентов, которые у вас есть (в приведенном вами примере это 4).
Добавьте десятичные части. В вашем примере у вас есть общее количество дробной части = 3.
Ceil 3 числа с самыми высокими фракциями и пол остальных.
(Извините за правки)
источник
Если вам действительно нужно их округлить, здесь уже есть очень хорошие предложения (наибольший остаток, наименьшая относительная ошибка и т. Д.).
Также есть одна веская причина не округлять (вы получите хотя бы одно число, которое «выглядит лучше», но «неправильно»), и как решить эту проблему (предупреждаю ваших читателей), и именно это я и делаю.
Позвольте мне добавить «неправильную» часть номера.
Предположим, у вас есть три события / сущности / ... с некоторыми процентами, которые вы приближаете как:
Позже значения немного изменятся, чтобы
В первой таблице уже упоминалась проблема наличия «неправильного» числа: 33,34 ближе к 33, чем к 34.
Но теперь у вас есть большая ошибка. Сравнивая день 2 с днем 1, реальное процентное значение для А увеличилось на 0,01%, но аппроксимация показывает уменьшение на 1%.
Это качественная ошибка, вероятно, намного хуже, чем первоначальная количественная ошибка.
Можно придумать аппроксимацию для всего набора, но, возможно, вам придется публиковать данные в первый день, поэтому вы не будете знать о втором дне. Так что, если вы действительно, действительно, не должны приблизиться, вам, вероятно, лучше этого не делать.
источник
проверьте, является ли это действительным или нет, насколько мои тестовые случаи я могу заставить это работать.
скажем, число к;
источник
Я реализовал метод из ответа Варуна Вохры здесь для списков и диктов.
источник
Вот более простая реализация Python ответа @ varun-vohra:
Вам нужно
math
,itertools
,operator
.источник
Для тех, у кого есть проценты в серии панд, вот моя реализация метода « Самый большой остаток» (как в ответе Варуна Вохры ), где вы можете даже выбрать десятичные дроби, до которых вы хотите округлить.
источник
Это случай банковского округления, иначе говоря, «полукруглой формы». Поддерживается BigDecimal. Его цель - обеспечить балансирование округления, то есть не выгодно ни банку, ни клиенту.
источник