thedeemon: (Default)
[personal profile] thedeemon
Всякий знает, что вычисление чисел Фибоначчи - важнейшая задача программирования, поэтому именно с нее нередко начинают обучение. Некоторые языки программирования, похоже, были созданы специально для решения этой задачи (prooflink). Однако не все еще нашли применение этим чудесным числам в быту.

Понадобилось мне тут недавно уметь компактно представить набор целых чисел: при проверке на наличие новых версий а также при деинсталляции программа передает на сервер номер своей версии, а заодно кое-какую статистику, вроде числа запусков, количества дней с момента установки и т.п. Передавать нужно GET'ом, ибо при деинсталляции это делает не сама программа, а скрипт установщика, к тому же хочется сразу иметь эти данные в логах апача. Чисел получилось много, в явном виде запрос получился бы сильно длинным и некрасивым. Решил применить какое-нибудь простое сжатие.

Числа все неотрицательные, сверху не ограниченные, и вероятность встречи числа быстро уменьшается с ростом самого числа. Для таких данных есть семейство кодов переменной длины, называемое универсальными кодами. Большинство из них сначала кодируют некоторым образом число битов (например, унарно), а затем сами биты кодируемого числа. Но длина таких кодов растет довольно быстро. Зато есть более интересный и для нужных мне значений более эффективный способ представления - коды Фибоначчи. Они основаны на теореме одного зубного врача из бельгийской армии, которая говорит, что всякое натуральное число можно уникальным образом представить в виде суммы чисел Фибоначчи, причем в такой сумме никогда не будет двух последовательных членов ряда. Ряд начинается так: 1, 2, 3, 5, 8, 13, 21... Тогда 6 = 1+5, 15 = 2+13, 31 = 2+8+21. Чтобы закодировать число последовательностью битов, достаточно пройтись по ряду Фибоначчи до требуемого числа и проставить единицы напротив входящих в сумму членов и нули напротив невходящих. Поскольку по построению у нас не может быть подряд двух единиц, этот факт используется для маркировки конца числа: после последней единицы ставим еще одну и код готов, длину числа хранить нигде не надо. Примеры:
Fib: 1 2 3 5 8 13 21 34  | Code
  1: *                   | 11
  2:   *                 | 011
  3:     *               | 0011
  4: *   *               | 1011
 10:   *     *           | 010011
 16:     *      *        | 0010011
 42:         *        *  | 000010011


В итоге у меня требуемый набор чисел превращается в такую битовую последовательность, которая затем кодируется по пять цифрами и буквами в Base32. Получается обычно по 12-15 символов на 24 исходных инта.

А вот и сам автор теоремы - полковник Эдуард Цекендорф:
Edouard Zeckendorf

Date: 2010-09-17 06:19 pm (UTC)
From: [identity profile] xeno-by.livejournal.com
Помню, когда мы в лицее увлекались архивацией, в книжке Ватолина и компании я также видел описание гамма-, дельта- и других кодов Элиаса (http://ru.wikipedia.org/wiki/Гамма-код_Элиаса и дальше по линкам). Вы про эти коды, наверняка, знаете (я вас видел на компрешн.ру =)), а вот читателям бложека, может быть, будет интересна релевантная информация.

Когда мы делали BWT-компрессор, то на одном из этапов остановились именно на DC + Фибоначчи. Вот только запамятовал почему - то ли потому, что арифметик тогда было писать непонятно как, то ли за перфомансом гнались... В общем, интересно все это. Спасибо за воспоминания =)

Date: 2010-09-17 06:24 pm (UTC)
From: [identity profile] thedeemon.livejournal.com
Да, коды Элиаса к тому же семейству универсальных относятся, для чисел до 10^4 Фибоначчи эффективнее.

Date: 2010-09-17 06:26 pm (UTC)
From: [identity profile] xeno-by.livejournal.com
Там, вроде бы, от распределения вероятностей зависело?

Date: 2010-09-17 06:25 pm (UTC)
From: [identity profile] xeno-by.livejournal.com
А вот еще интересная тривия. Коды Фибоначчи неплохо противостоят коррапшену. Повредившийся бит от силы может помешать правильному раскодированию своего блока, а также двух соседних. Но никогда не будет такой ситуации, что этот бит аля домино повредит всю закодированную последовательность.
Edited Date: 2010-09-17 06:26 pm (UTC)

Date: 2010-09-17 06:32 pm (UTC)
From: [identity profile] thedeemon.livejournal.com
Верно, осталось найти этому факту применение. ;)

Date: 2010-09-17 09:46 pm (UTC)
From: [identity profile] 109.livejournal.com
число битов, необходимое для кодирования длины числа в битах растёт как log(log), а разница между fibonacci и direct binary растёт как просто log. таким образом, чем больше число, тем менее выгодно кодировать его fibonacci. что интересно, маленькие числа кодировать fibonacci тоже невыгодно, поскольку их всё равно надо в конечном итоге (чтобы передать) запихивать во что-то стандартное, типа int. таким образом, использовать fibonacci не имеет смысла никогда.

Date: 2010-09-18 12:35 am (UTC)
From: [identity profile] thedeemon.livejournal.com
>чем больше число, тем менее выгодно кодировать его fibonacci

Верно. Невыгодность начинается где-то с 10^4, а ожидаемое распределение таково, что 99,9% чисел будут в пределах тысячи, а 90% в пределах сотни. На таких порядках варианты с длинами проигрывают, что и показано на картинке.

>что интересно, маленькие числа кодировать fibonacci тоже невыгодно, поскольку их всё равно надо в конечном итоге (чтобы передать) запихивать во что-то стандартное, типа int

Bitstream уже отменили? По одному их передавать действительно нет смысла, но набор - уже есть.

>таким образом, использовать fibonacci не имеет смысла никогда

Очевидная глупость, ибо применима и к Хаффману и к прочим VLC, используемым повсеместно - mp3, MPEG4, H.264 etc.

Date: 2010-09-20 08:22 am (UTC)
From: [identity profile] 109.livejournal.com
слово "глупость" вы зря произнесли. теперь придётся отдуваться. давайте мне последовательность чисел и сколько битов вам потребовалось, чтобы её закодировать с помощью фибоначчи. а я скажу, сколько у меня без помощи. код у вас уже написан, так что вам легче, чем мне. если у меня уйдёт меньше битов, вы пишете отдельный пост про то, как вы проиграли пари, а если наоборот - то я.

(no subject)

From: [identity profile] thedeemon.livejournal.com - Date: 2010-09-20 09:06 am (UTC) - Expand

(no subject)

From: [identity profile] 109.livejournal.com - Date: 2010-09-20 07:03 pm (UTC) - Expand

(no subject)

From: [identity profile] thedeemon.livejournal.com - Date: 2010-09-21 01:20 am (UTC) - Expand

(no subject)

From: [identity profile] 109.livejournal.com - Date: 2010-09-21 08:29 am (UTC) - Expand

(no subject)

From: [identity profile] thedeemon.livejournal.com - Date: 2010-09-21 09:05 am (UTC) - Expand

(no subject)

From: [identity profile] thedeemon.livejournal.com - Date: 2010-09-27 11:21 am (UTC) - Expand

(no subject)

From: [identity profile] 109.livejournal.com - Date: 2010-09-27 07:05 pm (UTC) - Expand

(no subject)

From: [identity profile] thedeemon.livejournal.com - Date: 2010-09-28 01:48 am (UTC) - Expand

(no subject)

From: [identity profile] trueview.livejournal.com - Date: 2014-02-20 09:13 pm (UTC) - Expand

(no subject)

From: [identity profile] thedeemon.livejournal.com - Date: 2014-02-21 03:25 am (UTC) - Expand

(no subject)

From: [identity profile] trueview.livejournal.com - Date: 2014-02-21 08:24 am (UTC) - Expand

(no subject)

From: [identity profile] thedeemon.livejournal.com - Date: 2014-02-21 09:02 am (UTC) - Expand

(no subject)

From: [identity profile] trueview.livejournal.com - Date: 2014-02-21 04:07 pm (UTC) - Expand

(no subject)

From: [identity profile] thedeemon.livejournal.com - Date: 2010-09-21 02:27 am (UTC) - Expand

(no subject)

From: [identity profile] 109.livejournal.com - Date: 2010-09-21 08:25 am (UTC) - Expand

Date: 2010-09-27 10:11 am (UTC)
From: [identity profile] sassa-nf.livejournal.com
не смешите мои тапочки.

меньше одного байта не перешлёшь, отсюда и плясать.

например, если речь о 8-битных числах (для простоты):

- числа 0..0x7f пересылаются одним байтом.
- числа 0x80..0x3fff пересылаются двумя байтами, где первый байт сообщения маскированный 0xc0 даёт 0x80. это уже числа до 16К. далее забиваем на выигрыш от уродства кодов переменной длины и кодируем длину сообщения в байтах.

аналогичная схема для base64.

(no subject)

From: [identity profile] sassa-nf.livejournal.com - Date: 2010-09-27 10:32 am (UTC) - Expand

(no subject)

From: [identity profile] sassa-nf.livejournal.com - Date: 2010-09-27 11:16 am (UTC) - Expand

(no subject)

From: [identity profile] thedeemon.livejournal.com - Date: 2010-09-27 11:27 am (UTC) - Expand

(no subject)

From: [identity profile] thedeemon.livejournal.com - Date: 2010-09-27 11:25 am (UTC) - Expand

(no subject)

From: [identity profile] sassa-nf.livejournal.com - Date: 2010-09-27 12:35 pm (UTC) - Expand

Date: 2010-09-17 07:51 pm (UTC)
From: [personal profile] alll
> Числа все ... сверху не ограниченные
...
> Большинство из них сначала кодируют некоторым образом число битов

Гм, я правильно понимаю, что если числа неограничены сверху, то возникает задача определения числа битов в числе битов и мы в некотором роде впадаем в рекурсию? :)

Date: 2010-09-18 12:22 am (UTC)
From: [identity profile] thedeemon.livejournal.com
Правильно, некоторые универсальные коды так и устроены.

Date: 2010-09-17 09:53 pm (UTC)
From: [identity profile] juan-gandhi.livejournal.com
Фигасе. Век живи век учись.

Date: 2010-09-17 10:06 pm (UTC)
From: [identity profile] soonts.livejournal.com
Неочевидный код, пара часов разработки, не human-readable данные в запросах - ради экономии нескольких байт трафика, на операции которую пользователь сделает раз в год?
Обращаю внимание, что типичный HTTP GET заголовок - это уже порядка 300 байт по TCP. То есть экономия с 60 байт до 12 не в 5 раз, а всего на 15%.

Я бы закодировал числа в строчку как-нить так
0-12-F5-AE75-0-0-0-2-0-FFDDEE-0-0-0-0-0-0
(минусы вместо например '|' - шоб разделитель оставался одним символом а не "%A6", помним про URL encoding)
Да, у меня было бы минимум 47 байт в запросе. Однако плюсы в данном случае IMO перевешивают: время разработки минуты а не часы, и human readable формат данных.

P.S. А как ты кодируешь число 0 ?

Date: 2010-09-18 12:26 am (UTC)
From: [identity profile] thedeemon.livejournal.com
Дело не в экономии трафика, а в "страшности" URL'a (при деинсталляции там не просто запрос посылается, а открывается браузер с uninstall feedback form, если пользователь согласился на нее пройти, конечно).

Про 0 - кодирую везде N+1.

Date: 2010-09-18 12:46 am (UTC)
From: [identity profile] thedeemon.livejournal.com
Плюс помимо передачи это же представление используется для хранения статистики. В таком виде у юзера не возникнет желания ее выборочно попортить и беспокойств меньше, чем видеть какие-то счетчики в реестре.

Date: 2010-09-18 03:22 pm (UTC)
From: [identity profile] soonts.livejournal.com
>открывается браузер с uninstall feedback form
Например пишешь 10 строк C# кода, который, если запрошен файл "Uninstall.aspx?s=...", сохраняет Request.QueryString["s"] в Session["s"], после чего отправляет клиенту HTTP 301 на Uninstall.aspx без параметров.
Все параметры на месте + чистый URL.

>это же представление используется для хранения статистики
А если в след. версии один счотчик исчезнет (причом не последний, а тот шо посередине), и появятся 2 новых?

>не возникнет желания ее выборочно попортить
IMO чувак, который открывает RegEdit и правит непонятные для него значения - весьма редкий use-case.

Когда на сайте support.microsoft.com есть инструкции по правке реестра, они всегда сопровождаются warning "serious problems might occur if you modify the registry incorrectly".
Где-то мне попадался warning даже страшнее, о том шо "вы можете сломать систему так шо потребуется её переустановка".

Несмотря на то что удалив единственный ключ (например из ветки HKCR), можно поиметь очень разнообразные проблемы, Microsoft не делает попыток защититься от таких чуваков.
Точнее, делает только если пиздец: "Restore last known good configuration".

Date: 2010-09-18 05:21 pm (UTC)
From: [identity profile] thedeemon.livejournal.com
А если в след. версии один счотчик исчезнет (причом не последний, а тот шо посередине), и появятся 2 новых?

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

Я не спорю, можно было сделать и так, как ты говоришь. Но все же мне идея хранения и передачи статистики в явном виде не очень симпатична, а сжатие кодами Фибоначчи это фан. Могу позволись себе совместить приятное с полезным. :)

(no subject)

From: [identity profile] soonts.livejournal.com - Date: 2010-09-18 11:29 pm (UTC) - Expand

(no subject)

From: [identity profile] thedeemon.livejournal.com - Date: 2010-09-19 05:15 am (UTC) - Expand

(no subject)

From: [identity profile] soonts.livejournal.com - Date: 2010-09-20 09:12 pm (UTC) - Expand

(no subject)

From: [identity profile] thedeemon.livejournal.com - Date: 2010-09-21 01:28 am (UTC) - Expand

(no subject)

From: [identity profile] kisa-i-osya.livejournal.com - Date: 2010-09-25 01:55 pm (UTC) - Expand

Date: 2010-09-18 08:34 pm (UTC)
From: [identity profile] nponeccop.livejournal.com
> Передавать нужно GET'ом

как Вы могли пожертвовать идемпотентностью ради искусства!

Date: 2010-09-19 05:19 am (UTC)
From: [identity profile] thedeemon.livejournal.com
Виноват, каюсь! :)

Date: 2010-10-06 06:58 pm (UTC)
From: [identity profile] http://users.livejournal.com/_winnie/
Получился интересный способ генерирования кодов Хафманна (т.е. без общего префикса) для какого-то фиксированной частоты чисел. Наверное если померять настоящие частоты входящих символов, то можно сделать в среднем короче.
Если их частоты невозможно померять, то такой способ наверное лучший ибо красивый :)

Date: 2010-10-07 01:53 am (UTC)
From: [identity profile] thedeemon.livejournal.com
Две трудности - бесконечный алфавит и отсутствие априорной статистики.

Date: 2014-02-21 08:33 am (UTC)
From: [identity profile] trueview.livejournal.com
> всякое натуральное число можно уникальным образом представить в виде суммы чисел Фибоначчи, причем в такой сумме никогда не будет двух последовательных членов ряда.

Не совсем корректная формулировка. Всякое натуральное число можно представить уникальным образом в виде суммы чисел Фибоначчи, если запретить использование в сумме двух соседних членов последовательности.
Edited Date: 2014-02-21 08:34 am (UTC)

Date: 2014-02-21 09:08 am (UTC)
From: [identity profile] thedeemon.livejournal.com
Да, можно так сформулировать.

Date: 2014-02-21 11:19 am (UTC)
From: [identity profile] trueview.livejournal.com
Нужно. В вашем случае телега впереди кобылы.:)

Profile

thedeemon: (Default)
Dmitry Popov

December 2025

S M T W T F S
 12 3456
789101112 13
14151617181920
21222324252627
28293031   

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Jan. 27th, 2026 10:20 pm
Powered by Dreamwidth Studios