Значения шкалы должны быть <= 255? - PullRequest
12 голосов
/ 11 октября 2009

У меня есть ячейки, числовое значение которых может быть любым от 0 до Integer.MAX_VALUE. Я хотел бы раскрасить эти ячейки соответственно.

Если значение = 0, то r = 0. Если значение Integer.MAX_VALUE, то r = 255. Но как насчет значений между ними?

Я думаю, мне нужна функция, предел которой как x => Integer.MAX_VALUE равен 255. Что это за функция? Или есть лучший способ сделать это?

Я мог бы просто сделать (value / (Integer.MAX_VALUE / 255)), но это приведет к тому, что многие низкие значения будут равны нулю. Так что, возможно, я должен сделать это с помощью функции журнала.

Большинство моих значений будет в диапазоне [0, 10000]. Поэтому я хочу выделить различия там.

Ответы [ 13 ]

16 голосов
/ 11 октября 2009

«Самое справедливое» линейное масштабирование фактически выполняется так:

floor(256 * value / (Integer.MAX_VALUE + 1))

Обратите внимание, что это просто псевдокод и предполагает вычисления с плавающей запятой.

Если мы предположим, что Integer.MAX_VALUE + 1 равно 2 ^ 31, и что / даст нам целочисленное деление, то оно упрощается до

value / 8388608

Почему другие ответы неверны

В некоторых ответах (а также в самом вопросе) предлагается вариант (255 * value / Integer.MAX_VALUE). Предположительно это должно быть преобразовано в целое число, используя round() или floor().

При использовании floor() единственное value, которое выдает 255, - это само Integer.MAX_VALUE. Это распределение неравномерно.

Если использовать round(), 0 и 255 будут поражены вдвое больше, чем 1-254. Также неравномерно.

При использовании метода масштабирования, о котором я упоминал выше, такой проблемы не возникает.

Нелинейные методы

Если вы хотите использовать логи, попробуйте это:

255 * log(value + 1) / log(Integer.MAX_VALUE + 1)

Вы также можете просто взять квадратный корень из значения (это не дойдет до 255, но вы можете увеличить его, если хотите).

5 голосов
/ 11 октября 2009

Я подумал, что для этого подойдет журнал, но, глядя на результаты, я не уверен.

Однако Wolfram | Alpha отлично подходит для экспериментов с подобными вещами :

Я начал с этого и закончил:

r(x) = floor(((11.5553 * log(14.4266 * (x + 1.0))) - 30.8419) / 0.9687)

Интересно, что получается, что это дает результаты, почти идентичные ответу Артелия:

r(x) = floor(255 * log(x + 1) / log(2^31 + 1)

ИМХО, вам лучше использовать функцию разделения для 0-10000 и 10000-2 ^ 31.

3 голосов
/ 11 октября 2009

Для линейного отображения диапазона от 0-2 ^ 32 до 0-255, просто возьмите старший байт. Вот как это будет выглядеть при использовании двоичного & и сдвига битов:

r = value & 0xff000000 >> 24

Использование мода 256 обязательно вернет значение 0-255, но вы не сможете извлечь какой-либо смысл группировки из результатов - 1, 257, 513, 1025 все будут отображаться в масштабированное значение 1, даже если они далеко друг от друга.

Если вы хотите быть более разборчивыми среди низких значений и объединить много более больших значений вместе, то выражение журнала будет работать:

r = log(value)/log(pow(2,32))*256

РЕДАКТИРОВАТЬ : Да, моя учительница алгебры в старшей школе миссис Бакенмейер упала бы в обморок! log(pow(2,32)) - это то же самое, что и 32*log(2), а намного дешевле для оценки. И теперь мы также можем учесть это лучше, так как 256/32 - это даже четное 8:

r = 8 * log(value)/log(2)

log(value)/log(2) на самом деле log-base-2 of value, что делает журнал для нас очень аккуратно:

r = 8 * log(value,2)

Там, миссис Бакенмейер - ваши усилия не были полностью потрачены впустую!

2 голосов
/ 11 октября 2009

Это работает! r= value /8421504;

8421504 на самом деле является «магическим» числом, равным MAX_VALUE / 255. Таким образом, MAX_VALUE / 8421504 = 255 (и некоторые изменится, но достаточно маленькая целочисленная математика избавится от него.

если вы хотите, чтобы в нем не было магических чисел, это должно сработать (и с одинаковой производительностью, поскольку любой хороший компилятор заменит его на фактическое значение:

r= value/ (Integer.MAX_VALUE/255);

Приятно то, что для этого не потребуются значения с плавающей запятой.

2 голосов
/ 11 октября 2009

В целом (поскольку для меня неясно, является ли это вопросом, связанным с Java или языком), вы должны разделить полученное значение на Integer.MAX_VALUE, умножить на 255 и преобразовать в целое число.

1 голос
/ 30 июля 2011

Вот несколько алгоритмов для масштабирования, нормализации, ранжирования и т. Д. Чисел с использованием методов расширения в C #, хотя вы можете адаптировать их к другим языкам:

http://www.redowlconsulting.com/Blog/post/2011/07/28/StatisticalTricksForLists.aspx

Существуют пояснения и графики, поясняющие, когда вы можете захотеть использовать тот или иной метод.

1 голос
/ 11 октября 2009

Задайте себе вопрос: «Какое значение должно соответствовать 128?» Если ответ около миллиарда (я сомневаюсь, что это так), то используйте линейный. Если ответ находится в диапазоне 10–100 тысяч, то рассмотрим квадратный корень или лог.

Другой ответ предложил это (я пока не могу комментировать или голосовать). Я согласен.

r = log (значение) / log (pow (2,32)) * 256

1 голос
/ 11 октября 2009

Линейная реализация обсуждается в большинстве этих ответов, и ответ Артелиуса кажется лучшим. Но лучшая формула будет зависеть от того, чего вы пытаетесь достичь, и от распределения ваших ценностей. Не зная, что сложно дать идеальный ответ.

Но просто для иллюстрации, любой из них может быть лучшим для вас:

  • Линейное распределение, каждое отображение на диапазон которого составляет 1/266 от общего диапазона.
  • Логарифмическое распределение (наклонено к низким значениям), которое выделит различия в более низких величинах и уменьшит различия в более высоких величинах
  • Обратное логарифмическое распределение (смещено в сторону высоких значений), которое выделит различия в более высоких величинах и уменьшит различия в более низких величинах.
  • Нормальное распределение количества цветов, где каждый цвет встречается столько же раз, сколько и любой другой цвет.

Опять же, вам нужно определить, чего вы пытаетесь достичь и для чего будут использоваться данные. Если вам было поручено создать это, я настоятельно рекомендую вам разъяснить это, чтобы оно было максимально полезным - и чтобы вам не пришлось его перерабатывать позже.

1 голос
/ 11 октября 2009

Обратите внимание, что если вы хотите ярче и ярче, яркость не является линейной, поэтому прямое сопоставление значения и цвета не даст хорошего результата.

В классе Color есть метод для создания более яркого цвета. Посмотрите на это.

1 голос
/ 11 октября 2009

Значение, которое вы ищете: r = 255 * (значение / Integer.MAX_VALUE). Таким образом, вы должны превратить это в двойную, а затем вернуть обратно в int.

...