Я когда-то реализовал его (даже не зная, что это было действительно двоичным поиском) для управления GUI, показывающего двумерные данные в графике. Нажатие с мышью должно установить курсор данных на точку с самым близким значением x. При контакте с большими количествами точек (несколько 1000, это было путем назад, когда x86 только начинал преобладать над частотой ЦП на 100 МГц) это не было действительно применимо в интерактивном режиме - я делал линейный поиск от запуска. После некоторых взглядов мне пришло в голову, что я мог приблизиться к этому в делении и завоевать вид. Взял меня некоторое время для получения его работающий под всеми пограничными случаями.
Это было только некоторое время спустя, что я узнал, что это - действительно фундаментальный алгоритм CS...
Я подумал, что для этого подойдет лог-подгонка, но, глядя на результаты, я не уверен.
Однако 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.
В общем (поскольку мне непонятно, относится ли это к вопросу Java или языковой независимости), вы должны разделить имеющееся у вас значение на Integer.MAX_VALUE
, умножить на 255
и преобразовать в целое число.
Для линейного отображения диапазона 0-2 ^ 32 в 0-255 просто возьмите старший байт. Вот как это будет выглядеть с использованием двоичных и
и битового сдвига:
r = value & 0xff000000 >> 24
Использование mod 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:
The "fairest" linear scaling is actually done like this:
floor(256 * value / (Integer.MAX_VALUE + 1))
Note that this is just pseudocode and assumes floating-point calculations.
If we assume that Integer.MAX_VALUE + 1 is 2^31, and that / will give us integer division, then it simplifies to
value / 8388608
Why other answers are wrong
Some answers (as well as the question itself) suggsted a variation of (255 * value / Integer.MAX_VALUE)
. Presumably this has to be converted to an integer, either using round()
or floor()
.
If using floor()
, the only value
that produces 255 is Integer.MAX_VALUE itself. This distribution is uneven.
If using round()
, 0 and 255 will each get hit half as many times as 1-254. Also uneven.
Using the scaling method I mention above, no such problem occurs.
Non-linear methods
If you want to use logs, try this:
255 * log(value + 1) / log(Integer.MAX_VALUE + 1)
You could also just take the square root of the value (this wouldn't go all the way to 255, but you could scale it up if you wanted to).
Я мог бы просто сделать (value / (Integer.MAX_VALUE / 255)), но это приведет к тому, что многие низкие значения будут нулевыми.
Один из возможных подходов - использовать оператор по модулю ( r = value% 256;
). Хотя это не гарантирует, что это гарантирует число от 0 до 255. Это также позволит распределить низкие числа по 0 -255 диапазон. Integer.MAX_VALUE
окажется равным 255,
РЕДАКТИРОВАТЬ:
Как ни странно, когда я это тестирую, Integer.MAX_VALUE% 256
действительно приводит к 255
(изначально я ошибочно тестировал % 255
, что дало неверные результаты). Это кажется довольно простым решением.
Если вы жалуетесь на то, что младшие числа становятся равными нулю, вы можете нормализовать значения до 255, а не всего диапазона значений.
Формула будет выглядеть так:
currentValue / (максимальное значение набора)
The best answer really depends on the behavior you want.
If you want each cell just to generally have a color different than the neighbor, go with what akf said in the second paragraph and use a modulo (x % 256).
If you want the color to have some bearing on the actual value (like "blue means smaller values" all the way to "red means huge values"), you would have to post something about your expected distribution of values. Since you worry about many low values being zero I might guess that you have lots of them, but that would only be a guess.
In this second scenario, you really want to distribute your likely responses into 256 "percentiles" and assign a color to each one (where an equal number of likely responses fall into each percentile).
Ask yourself the question, "What value should map to 128?" Если ответ - около миллиарда (я сомневаюсь, что это так), используйте линейный. Если ответ находится в диапазоне 10–100 тысяч, тогда рассмотрите квадратный корень или логарифм.
Другой ответ предложил это (я пока не могу комментировать или голосовать). Я согласен.
r = log (значение) / log (pow (2,32)) * 256
The linear implementation is discussed in most of these answers, and Artelius' answer seems to be the best. But the best formula would depend on what you are trying to achieve and the distribution of your values. Without knowing that it is difficult to give an ideal answer.
But just to illustrate, any of these might be the best for you:
Again, you need to determine what you are trying to achieve & what the data will be used for. If you have been tasked to build this then I would strongly recommend you get this clarified to ensure that it is as useful as possible - and to avoid having to redevelop it later on.
Значение, которое вы ищете: r = 255 * (value / Integer.MAX_VALUE). Таким образом, вам придется превратить это в двойное, а затем вернуть в int.
Это работает! r = значение / 8421504;
8421504 на самом деле является «магическим» числом, равным MAX_VALUE / 255. Таким образом, MAX_VALUE / 8421504 = 255 (и некоторые изменения, но достаточно мелкие целочисленные математические вычисления избавят от него.
если вам нужен тот, в котором нет магических чисел, это должно сработать (и с такой же производительностью, поскольку любой хороший компилятор заменит его фактическим значением:
r = value / (Integer.MAX_VALUE / 255);
Хорошая деталь в том, что это не потребует никаких значений с плавающей запятой.
Note that if you want brighter and brighter, that luminosity is not linear so a straight mapping from value to color will not give a good result.
The Color class has a method to make a brighter color. Have a look at that.