?
Ни о какой безапелляционности в моих высказываниях не может быть и речи! [entries|archive|friends|userinfo]
Anatoly Vorobey

[ website | Website ]
[ userinfo | livejournal userinfo ]
[ archive | journal archive ]

Links
[Links:| English-language weblog ]

hacker's delight (программистское) [июл. 6, 2014|03:21 pm]
Anatoly Vorobey
[Tags|]

Читаю Hacker's Delight, которая оправдывает свое название. Много трюков с битами, простых и не очень. Кстати, из тех, что попроще, можно настрогать легко разминки для ума. Например, у вас есть два беззнаковых целых числа x,y, и вы хотите вычислить их среднее арифметическое, округленное вниз. Докажите себе в уме, что это равно

x&y + (x^y)>>1

(здесь ^ это xor, конечно. Кстати, та же формула работает для знаковых x,y, только нужно, чтобы >> было арифметическим сдвигом. В Джаве это так и есть автоматически, в C/C++ можно >>1 заменить на /2).

Вы спросите, а зачем это нужно, почему просто не написать (x+y)/2? Ну хотя бы потому, что может случиться переполнение, если числа достаточно большие. А что, это реально мешает в жизни?

И это хороший повод напомнить об истории с багом в алгоритме двоичного поиска в массиве, которую обнаружили и починили в Джаве аж в 2006 году. Вот отличная запись Джоша Блоха об этом. Если вкратце, то вычисляя середину отрезка mid = (low+high)/2 в двоичном поиске, мы получим переполнение, если массив достаточно большой, больше примерно миллиарда элементов, что в наше время 64-битных процессов уже вполне случается. Однако этот код провисел в знаковой книге Programming Pearls, не говоря уж о куче библиотек и других книг, десятки лет, пока кто-то не обратил внимание. Эта история, кстати, поучительна и притом довольно малоизвестна.

Конкретно двоичный поиск починить легче, потому что известно, что low меньше или равно high, и можно просто написать: low + (high-low)/2. Но в случае, когда неизвестно, кто из x,y больше другого, вышеуказанная формула - наверное, самый экономный способ вычислить среднее арифметическое без переполнения.
СсылкаОтветить

Comments:
[User Picture]From: cdesz
2014-07-06 12:37 pm
a>>1 + b>>1 + a&b&1
(Ответить) (Thread)
[User Picture]From: antontsau
2014-07-06 12:42 pm
это же сложение столбиком. "один и один - дает один в следующем разряде, одинXORодин - дает один в текущем разряде". Одновременно это не случается.

только, ээээ, там вроде как надо результат & вниз на разряд сдвигать, а не результат хора.
(Ответить) (Thread)
[User Picture]From: miph
2014-07-06 12:59 pm
Да вот и я разминался вчера на JS и с удивлением (я у меня все двоичные махинации вызывают удивление) узнал, что floor легко реализовать как ~~x, где x вещественное. Или что-то типа (100/3)<<32 (что, кажется, аналогично (100/3)>>>0)
В общем, магия, с которой я лично в повседневной жизни не так часто сталкиваюсь.
(Ответить) (Thread)
[User Picture]From: _winnie
2014-07-07 09:51 am
if (~str.IndexOf(word)) { ... }
(Ответить) (Parent) (Thread)
[User Picture]From: inkittenus
2014-07-06 02:28 pm
Объясните чайнику в программировании: почему low + (high-low)/2 легче чем low/2 + high/2 ?

Edited at 2014-07-06 14:29 (UTC)
(Ответить) (Thread)
[User Picture]From: avva
2014-07-06 02:35 pm
low/2 + high/2 дает неправильный результат, когда и low, и high нечетные, например, если они 5 и 7, то получится 5.
(Ответить) (Parent) (Thread)
[User Picture]From: _winnie
2014-07-06 03:17 pm
А это кажется даже чуточку более гуманный код - x/2 + y/2 + ((x&1) + (y&1))/2

С которым можно придумывать новые полезные конструкции -
x/3 + y/3 + z/3 + (x%3+y%3+z%3)/3

А заменяя 3 на 65536 (4294967296 для uint64) - вообще универсальный молоток
(Ответить) (Parent) (Thread)
[User Picture]From: avva
2014-07-06 04:14 pm
Более гуманный, но значительно больше инструкций.
(Ответить) (Parent) (Thread)
[User Picture]From: inkittenus
2014-07-06 03:36 pm
Спасибо! Поняла.
(Ответить) (Parent) (Thread)
[User Picture]From: _winnie
2014-07-06 03:18 pm
Ура! avva вернулся! Новый memcached или ещё какой world-change инструмент не за горами!
(Ответить) (Thread)
From: ircicq
2014-07-06 03:52 pm
если взять за правило всегда использовать Int64 как индекс, проблемы не будет.
(Ответить) (Thread)
[User Picture]From: click0
2014-07-06 05:48 pm
При условии, что размер индекса не выйдет за пределы Int64 ...
(Ответить) (Parent) (Thread)
From: ircicq
2014-07-06 06:55 pm
По закону мура выйдет лет через 30.
Ну тогда уж Int128 ненакладно будет
(Ответить) (Parent) (Thread)
From: huzhepidarasa
2014-07-07 04:58 am
По закону Мурa закон Мура кончится через 15 лет :)

Edited at 2014-07-07 05:07 (UTC)
(Ответить) (Parent) (Thread)
From: pesec
2014-07-11 08:41 pm
В некоторых отраслях знания 64 бита уже не хватает.
(Ответить) (Parent) (Thread)
From: huzhepidarasa
2014-07-07 03:52 am
Эта запись плохо сочетается со следующей.
(Ответить) (Thread)
[User Picture]From: r419
2014-07-07 09:17 am
В ассемблере всё совсем просто:
add eax, edx
rcr eax, 1
И для знаковых тоже работает.
(Ответить) (Thread)