Допустим, у меня есть массив чисел с плавающей запятой в отсортированном (скажем, возрастающем) порядке, сумма которого, как известно, является целым числом N
. Я хочу округлить эти числа до целых, оставив их сумму без изменений. Другими словами, я ищу алгоритм, который преобразует массив чисел с плавающей запятой (назовем его fn
) в массив целых чисел (назовем его in
), такой что:
- оба массива имеют одинаковую длину
- сумма массива целых чисел равна
N
- разница между каждым числом с плавающей точкой
fn[i]
и соответствующим ему целым числом in[i]
меньше 1 (или равна 1, если вам действительно нужно)
- учитывая, что числа с плавающей точкой расположены в отсортированном порядке (
fn[i] <= fn[i+1]
), целые числа также будут в отсортированном порядке (in[i] <= in[i+1]
)
Учитывая, что эти четыре условия выполнены, алгоритм, который минимизирует дисперсию округления (sum((in[i] - fn[i])^2)
), является предпочтительным, но это не имеет большого значения.
Примеры:
[0.02, 0.03, 0.05, 0.06, 0.07, 0.08, 0.09, 0.1, 0.11, 0.12, 0.13, 0.14]
=> [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]
[0.1, 0.3, 0.4, 0.4, 0.8]
=> [0, 0, 0, 1, 1]
[0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1]
=> [0, 0, 0, 0, 0, 0, 0, 0, 0, 1]
[0.4, 0.4, 0.4, 0.4, 9.2, 9.2]
=> [0, 0, 1, 1, 9, 9] is preferable
=> [0, 0, 0, 0, 10, 10] is acceptable
[0.5, 0.5, 11]
=> [0, 1, 11] is fine
=> [0, 0, 12] is technically not allowed but I'd take it in a pinch
Чтобы ответить на несколько прекрасных вопросов, поднятых в комментариях:
- Повторные элементы разрешены в обоих массивах (хотя мне также было бы интересно услышать об алгоритмах, которые работают, только если массив с плавающей точкой не содержит повторов)
- Единого правильного ответа не существует - для данного входного массива чисел с плавающей запятой обычно существует несколько массивов целых чисел, которые удовлетворяют четырем условиям.
- Приложение, которое я имел в виду, было - и это довольно странно - распределять очки между лучшими финишерами в игре MarioKart ;-) На самом деле я никогда не играл в эту игру, но, наблюдая за кем-то еще, я заметил, что их было 24. очки распределялись между четырьмя лучшими финишерами, и я удивлялся, как можно распределить очки по времени финиша (поэтому, если кто-то финиширует с большим отрывом, он получает большую долю очков). В игре отслеживаются итоговые значения в виде целых чисел, поэтому возникает необходимость в таком округлении.
Для любопытных вот тестовый скрипт Я использовал, чтобы определить, какие алгоритмы работали.