<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Блоги &#187; atercattus</title>
	<atom:link href="http://software.intel.com/ru-ru/blogs/author/atercattus/feed/" rel="self" type="application/rss+xml" />
	<link>http://software.intel.com/ru-ru/blogs</link>
	<description></description>
	<lastBuildDate>Thu, 24 May 2012 12:16:29 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.1.3</generator>
		<item>
		<title>Развлечения ради: OpenMP для построения фракталов</title>
		<link>http://software.intel.com/ru-ru/blogs/2011/12/30/openmp-4/</link>
		<comments>http://software.intel.com/ru-ru/blogs/2011/12/30/openmp-4/#comments</comments>
		<pubDate>Fri, 30 Dec 2011 08:44:59 +0000</pubDate>
		<dc:creator>atercattus</dc:creator>
				<category><![CDATA[Intel Software Network]]></category>
		<category><![CDATA[Графика]]></category>
		<category><![CDATA[Параллельное программирование]]></category>
		<category><![CDATA[c++ parallel programming]]></category>
		<category><![CDATA[openmp]]></category>
		<category><![CDATA[картинки]]></category>
		<category><![CDATA[фрактал]]></category>

		<guid isPermaLink="false">http://software.intel.com/ru-ru/blogs/2011/12/30/openmp-4/</guid>
		<description><![CDATA[<p>Данный пост будет первым из, надеюсь, серии небольших очерков о применении различных библиотек распараллеливания вычислений. В качестве прикладной задачи выбрано графическое построение всем хорошо знакомого <a href="http://ru.wikipedia.org/wiki/%D0%9C%D0%BD%D0%BE%D0%B6%D0%B5%D1%81%D1%82%D0%B2%D0%BE_%D0%9C%D0%B0%D0%BD%D0%B4%D0%B5%D0%BB%D1%8C%D0%B1%D1%80%D0%BE%D1%82%D0%B0">множества Мандельброта</a>. В качестве библиотеки реализации вычислений в этот раз возьму OpenMP, а для унификации работы с разными оконными подсистемами - GLUT/OpenGL.</p>]]></description>
			<content:encoded><![CDATA[<p>Данный пост будет первым из, надеюсь, серии небольших очерков о применении различных библиотек распараллеливания вычислений. В качестве прикладной задачи выбрано графическое построение всем хорошо знакомого <a href="http://ru.wikipedia.org/wiki/%D0%9C%D0%BD%D0%BE%D0%B6%D0%B5%D1%81%D1%82%D0%B2%D0%BE_%D0%9C%D0%B0%D0%BD%D0%B4%D0%B5%D0%BB%D1%8C%D0%B1%D1%80%D0%BE%D1%82%D0%B0">множества Мандельброта</a>. В качестве библиотеки реализации вычислений в этот раз возьму OpenMP, а для унификации работы с разными оконными подсистемами - GLUT/OpenGL.</p>
<p><span id="more-2006700"></span></p>
<p>В качестве пиксельной матрицы возьмем массив 32-битных целых, рассматривая его как ARGB-плоскость. 4 байта на тексель не сильно экономят память <img src='http://software.intel.com/ru-ru/blogs/wordpress/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> , но избавляют от необходимости следить за выравниваем при заполнении буфера. Альфа-канал использоваться не будет.</p>
<p>После заполнения его каким-либо способом (зависит от библиотеки распараллеливания), загружаем данные в память видеокарты вызовом glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA...), а затем отрисовываем одним блоком GL_QUADS на весь экран (выводим прямоугольник).</p>
<blockquote><p>Вариант с glDrawPixels и так не советуют к использованию, так еще и по результатам эксперимента применение данной функции приводит к падению скорости отрисовки на порядок. Не пойдет. А применение VBO (Vertex Buffer Objects) в данном случае не обосновано - у нас всего 4 вершины и одна текстура.</p>
</blockquote>
<p>На выбор OpenGL+GLUT прежде всего повлияло желание собрать и запустить приложение как под Linux, так и под Windows и сравнить результаты на одинаковом "железе".</p>
<p>Распараллеливание будем выполнять, разделив текстуру на горизонтальные блоки по числу используемых потоков/ядер. В SLI/CrossFire конфигурациях видеокарт используется подобный режим Split Frame/Scissor (изображение взято с <a href="http://ru.wikipedia.org/wiki/%D0%A4%D0%B0%D0%B9%D0%BB:Scissor.png">википедии</a>):</p>
<p><img src="http://software.intel.com/ru-ru/blogs/wordpress/wp-content/uploads/Scissor1.png" alt="" class="alignnone size-thumbnail wp-image-2006709" /></p>
<p>Заменим видеокарты на процессорные ядра - и получим вариант распараллеливания.</p>
<p>Просто выводить множество целиком не столь интересно, как рассматривать его фрагменты в приближении. Для этого сразу введем координаты ограничивающего окна, в пределах которого строится изображение и данные самой текстуры:</p>
<pre name="code" class="cpp">
typedef struct {
    double x1, y1,
           x2, y2;
} Bounds;

typedef unsigned char byte;

typedef struct {
    int  width,     // ширина в пикселях
         height,    // высота в пикселях
         size;      // размер в пикселях == width*height
    byte *buff;     // кусок памяти с пиксельными данными
} Buffer;
</pre>
<p>Само множество на комплексной плоскости находится в пределах (-2,-1) x (1,1), так что можно начать с вывода с запасом (-2,-1.5) x (1,1.5) - вариант квадратной области.</p>
<p>Текстура хранится также квадратная, размерами 2^n (текстуры размерами не кратными степени 2 будут работать далеко не на всех видеокартах). Размер текстуры (T), равно как и предельное число итераций  проверки сходимости (n) выражают общую сложность построения как O(T^2*n).</p>
<p>При распараллеливании каждому из потоков на вход нужно подать ограничивающий объем в координатах комплексной плоскости, а также две V-координаты текстуры (высота/ось Y), обозначающие первую и последнюю строки, в которых следует отобразить результаты.</p>
<p>Сразу картинку возможного вывода:</p>
<p><a href="http://software.intel.com/ru-ru/blogs/wordpress/wp-content/uploads/Screenshot-12_cut.png"><img src="http://software.intel.com/ru-ru/blogs/wordpress/wp-content/uploads/Screenshot-12_cut-300x222.png" alt="" width="300" height="222" class="alignnone size-medium wp-image-2006717" /></a></p>
<p>Итого имеем:</p>
<pre name="code" class="cpp">
// координаты "окна" в комплексной плоскости
Bounds screen;

// текущие координаты центра "окна" в комплексной плоскости
double cX=0, cY=0;

// размер клиентской области окна в системе
int window_width  = 800,
    window_height = 800;

// текущий шаг приближения
int STEP = 0;

// предел числа итераций проверки сходимости
int ITERS = MIN_ITERS;
</pre>
<p>Собственно рендер фрактала в текстуру выполняется обычным распараллеленым циклом:</p>
<pre name="code" class="cpp">
void fractal() {
    int parts = omp_get_max_threads();
    if ( parts &lt;= 0 ) parts = 1;

    omp_set_dynamic( 0 );
    omp_set_num_threads( parts );

    int p, y=0, dy;
    double sy = (screen.y2-screen.y1) / parts;
    dy = buffer.height / parts;

    #pragma omp parallel for firstprivate(y) ordered
    for ( p=0; p&lt;parts; ++p ) {
        Bounds bounds;
        bounds.x1 = screen.x1;
        bounds.x2 = screen.x2;

        bounds.y1 = screen.y1 + p*sy;
        bounds.y2 = bounds.y1 + sy;

        y = p*dy;

        //fractal_block( bounds, y, y+dy-1, (p+1)*6 );
        fractal_block( bounds, y, y+dy-1, 3 );
    }
}
</pre>
<p>Т.к. координата верхней строки каждого блока (y) используется внутри тела цикла, при этом меняясь, то ее следует передавать через firstprivate.</p>
<p>Функция fractal_block принимает координаты окна комплексной плоскости (bounds), верхнюю и нижнюю границы в текстуре (y, y+dy-1) и множитель "яркости" (о нем ниже):</p>
<pre name="code" class="cpp">
void fractal_block( Bounds block, int y1, int y2, int f ) {
    int *ptr = (int*)( buffer.buff + y1*buffer.width*4 );

    double x, y, sx, sy, z, zi;

    x = block.x1;
    y = block.y1;
    // приращения по комплексным осям между соседними текселями текстуры
    sx = (block.x2 - block.x1) / buffer.width;
    sy = (block.y2 - block.y1) / (y2-y1+1);

    int width = buffer.width;

    // раскидываю значение множителя по трем младшим байтам
    f = f%256;
    f = (f&lt;&lt;16) | (f&lt;&lt;8) | f;

    for ( ; y1&lt;=y2; ++y1 ) {
        int _x;
        for ( _x=0; _x&lt;width; ++_x ) {
            z = zi = 0.0;
            int steps = 0;
            for ( ; steps&lt;ITERS; ++steps ) {
                double tmp = (z*z) - (zi*zi) + x;
                zi = 2*z*zi + y;
                z = tmp;

                if ( z*z + zi*zi &gt; 4.0 ) break;
            }

            *ptr++ = f * steps2RGBA( steps&lt;ITERS ? steps : 0 );

            x += sx;
        }

        y += sy;
        x  = block.x1;
    }
}
</pre>
<p>Что же дает "множитель яркости" f? Благодаря ему можно визуально разделять блоки, которые рассчитывались разными ядрами. Достаточно использовать при генерации значение, зависящее от номера текущего блока/ядра, например так: <b>fractal_block( bounds, y, y+dy-1, (p+1)*6 );</b></p>
<p>При этом будет наблюдаться картина вроде такой:</p>
<p><a href="http://software.intel.com/ru-ru/blogs/wordpress/wp-content/uploads/Screenshot-10_cut.png"><img src="http://software.intel.com/ru-ru/blogs/wordpress/wp-content/uploads/Screenshot-10_cut-e1325188428659-300x221.png" alt="" width="300" height="221" class="alignnone size-medium wp-image-2006721" /></a></p>
<p>Хорошо заметны 5 блоков, из которых состыкована текстура.</p>
<p>Технические подробности инициализации GLUT, OpenGL, рендеринга текстуры, масштабирования вокруг точки, подстройки предела проверки сходимости оставлю за кадром. Это все есть в прилагаемом исходнике для желающих.</p>
<p>Реализация выполнена с подстройкой предела сходимости с целью снять ненужную нагрузку (на общем плане что 50 итераций, что 100 - визуально не заметно, а нагрузка ощущается). Соотвественно идет подстройка предела при масштабировании (также ее можно менять кнопками Up/Down). </p>
<p>Непосредственно по распараллеливанию. На машине с Core i5 750 в 2 потока отрисовка дает честный двухкратный прирост, на 4х потоках прирост порядка 3.5 раз. На приближении 2^40 и пределе итераций в 250 время работы 4х ядер 1.01сек против 3.64сек на одном. <i>Была бы машина с иксами с большим числом ядер - было бы конечно интересней. Не раз встречал мнение, что распараллеливание особо проявляется не менее, чем на 8ми ядрах. Если у кого есть возможность и желание - запустите, интересен коэффициент прироста скорости в сравнении с одним и двумя ядрами.</i></p>
<p>Приближение 2^40 выбрано с целью предотвращения величин ошибок округления, влияющих на результат. Примерно на 42-43 итерации приближения размер окна в комплексной плоскости становится менее 6.82e-13 и начинают проявляться ошибки округления (underflow):</p>
<p><a href="http://software.intel.com/ru-ru/blogs/wordpress/wp-content/uploads/fractal_underflow_cut.png"><img src="http://software.intel.com/ru-ru/blogs/wordpress/wp-content/uploads/fractal_underflow_cut-e1325190159240-300x213.png" alt="" width="300" height="213" class="alignnone size-medium wp-image-2006725" /></a></p>
<p>В итоге получаем достаточно неплохой результат ценой практически одной только строчки pragma <img src='http://software.intel.com/ru-ru/blogs/wordpress/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>На очереди ArBB, TBB и что еще подвернется.</p>
<p>И еще немного картинок:</p>
<p><a href="http://software.intel.com/ru-ru/blogs/wordpress/wp-content/uploads/Screenshot_cut.png"><img src="http://software.intel.com/ru-ru/blogs/wordpress/wp-content/uploads/Screenshot_cut-300x222.png" alt="" width="300" height="222" class="alignnone size-medium wp-image-2006727" /></a> <a href="http://software.intel.com/ru-ru/blogs/wordpress/wp-content/uploads/Screenshot-15_cut.png"><img src="http://software.intel.com/ru-ru/blogs/wordpress/wp-content/uploads/Screenshot-15_cut-300x222.png" alt="" width="300" height="222" class="alignnone size-medium wp-image-2006728" /></a> <a href="http://software.intel.com/ru-ru/blogs/wordpress/wp-content/uploads/Screenshot-1_cut.png"><img src="http://software.intel.com/ru-ru/blogs/wordpress/wp-content/uploads/Screenshot-1_cut-300x300.png" alt="" width="300" height="300" class="alignnone size-medium wp-image-2006731" /></a> <a href="http://software.intel.com/ru-ru/blogs/wordpress/wp-content/uploads/Screenshot-14_cut.png"><img src="http://software.intel.com/ru-ru/blogs/wordpress/wp-content/uploads/Screenshot-14_cut-300x222.png" alt="" width="300" height="222" class="alignnone size-medium wp-image-2006729" /></a></p>
<p><a href='http://software.intel.com/ru-ru/blogs/wordpress/wp-content/uploads/fractal_isn.tar.gz'>Исходники (tar.gz)</a></p>
<p><b>И всех с Наступающим!</b></p>
]]></content:encoded>
			<wfw:commentRss>http://software.intel.com/ru-ru/blogs/2011/12/30/openmp-4/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Интервальное кодирование (Range encoding), как частный случай кодирования арифметического</title>
		<link>http://software.intel.com/ru-ru/blogs/2011/12/28/range-encoding/</link>
		<comments>http://software.intel.com/ru-ru/blogs/2011/12/28/range-encoding/#comments</comments>
		<pubDate>Wed, 28 Dec 2011 07:10:55 +0000</pubDate>
		<dc:creator>atercattus</dc:creator>
				<category><![CDATA[Intel Software Network]]></category>
		<category><![CDATA[Разработка софта]]></category>
		<category><![CDATA[coding]]></category>
		<category><![CDATA[decoding]]></category>
		<category><![CDATA[range coding]]></category>

		<guid isPermaLink="false">http://software.intel.com/ru-ru/blogs/2011/12/28/range-encoding/</guid>
		<description><![CDATA[<p>При помощи <a href="http://software.intel.com/ru-ru/blogs/2011/12/20/2006336/">арифметического кодирования</a> <i>в теории</i> можно сжать любой объем данных до одного вещественного числа. Однако на практике все упирается в конечную точность вычислений чисел с плавающей точкой, ограничивая объем обрабатываемого за раз блока. Требуется следить за ситуацией потери точности (underflow),а также за алгоритмом округления (на x86 задается битами регистра CWR) на случай работы с сжатыми данными на разных платформах.</p>

<p>В качестве альтернативы можно использовать целочисленные операции, основанные на работе не с одним значением внутри интервала, а с текущими границами...</p>]]></description>
			<content:encoded><![CDATA[<p>При помощи <a href="http://software.intel.com/ru-ru/blogs/2011/12/20/2006336/">арифметического кодирования</a> <i>в теории</i> можно сжать любой объем данных до одного вещественного числа. Однако на практике все упирается в конечную точность вычислений чисел с плавающей точкой, ограничивая объем обрабатываемого за раз блока. Требуется следить за ситуацией потери точности (underflow),а также за алгоритмом округления (на x86 задается битами регистра CWR) на случай работы с сжатыми данными на разных платформах.</p>
<p>В качестве альтернативы можно использовать целочисленные операции, основанные на работе не с одним значением внутри интервала, а с текущими границами. Если арифметический кодер работает в пределах интервала [0,1), то интервальный кодер начинает с интервала [0,N), где N - произвольный верхний предел, от которого и зависит максимальный размер обрабатываемого блока. Логично брать N, основываясь на аппаратных возможностях архитектуры, например 2^32.</p>
<p>Каждый обрабатываемый символ сужает интервал, приводя его в итоге к [x,x+1), что является предельным значением точности и размера обрабатываемого блока. Подинтервалы, в которые сужается исходный [0,1), как и в случае с арифметическим кодированием берутся исходя из вероятности появления данных символов во входном потоке. Учет вероятностей позволяет оптимально использовать выделенный интервал, предоставляя "чаще используемым" символам больший диапазон значений.</p>
<blockquote><p>Эксперимента ради, попробуйте разделить интервал [0,20) на 10 частей, а потом на столько же частей интервал [0,5), сохраняя при этом целочисленную точность. И почувствуйте разницу <img src='http://software.intel.com/ru-ru/blogs/wordpress/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p></blockquote>
<p>Рассмотрим пример сжатия строки "abraha#" (сократил "abrahadabra") с вероятностями:</p>
<table border="1" style="border:1px solid #000;border-collapse:collapse">
<tr>
<th style="padding: 2px;">Символ</th>
<th style="padding: 2px;">Вероятность</th>
</tr>
<tr>
<td style="padding: 2px;">a</td>
<td style="padding: 2px;">40%</td>
</tr>
<tr>
<td style="padding: 2px;">b</td>
<td style="padding: 2px;">20%</td>
</tr>
<tr>
<td style="padding: 2px;">h</td>
<td style="padding: 2px;">10%</td>
</tr>
<tr>
<td style="padding: 2px;">r</td>
<td style="padding: 2px;">25%</td>
</tr>
<tr>
<td style="padding: 2px;">#</td>
<td style="padding: 2px;">5%</td>
</tr>
</table>
<p>&nbsp;</p>
<p>Символ "#" является служебным терминальным и нужен на случай, если после сжатия в потоке вывода <u>не</u> передается исходная длина строки. В таком случае появление данного символа при декодировании будет служить сигналом завершения работы (аналогично EOF, EOL, etc).</p>
<p>В качестве максимального значения возьмем N=100.000.000 (100 миллионов).<br />
Преобразуем вероятности появления символов в отрезки на интервале [0,N):</p>
<ul>
<li>a = 40% = 0.4 =&gt; 0.4*N - верхняя граница интервала</li>
<li>b = 20% = 0.2 =&gt; 0.2+0.4 = 0.6*N - верхняя граница интервала, а нижняя - 0.4*N</li>
<li>...</li>
</ul>
<p>Получаем:</p>
<table border="1" style="border:1px solid #000;border-collapse:collapse">
<tr>
<th style="padding: 2px;">Символ</th>
<th style="padding: 2px;">Интервал</th>
<th style="padding: 2px;">Значения интервала (*10^6)</th>
</tr>
<tr>
<td style="padding: 2px;">a</td>
<td style="padding: 2px;">
<pre>[     0,  0.4*N)</pre>
</td>
<td style="padding: 2px;">[ 0,  40)</td>
</tr>
<tr>
<td style="padding: 2px;">b</td>
<td style="padding: 2px;">
<pre>[ 0.4*N,  0.6*N)</pre>
</td>
<td style="padding: 2px;">[40,  60)</td>
</tr>
<tr>
<td style="padding: 2px;">h</td>
<td style="padding: 2px;">
<pre>[ 0.6*N,  0.7*N)</pre>
</td>
<td style="padding: 2px;">[60,  70)</td>
</tr>
<tr>
<td style="padding: 2px;">r</td>
<td style="padding: 2px;">
<pre>[ 0.7*N, 0.95*N)</pre>
</td>
<td style="padding: 2px;">[70,  95)</td>
</tr>
<tr>
<td style="padding: 2px;">#</td>
<td style="padding: 2px;">
<pre>[0.95*N,      N)</pre>
</td>
<td style="padding: 2px;">[95, 100)</td>
</tr>
</table>
<p></p>
<p>Кодирование строки по шагам:</p>
<table border="1" style="border:1px solid #000;border-collapse:collapse">
<tr>
<th style="padding: 2px;">Шаг</th>
<th style="padding: 2px;">Рабочий интервал</th>
<th style="padding: 2px;">Вход</th>
<th style="padding: 2px;">Интервал символа (*10^6)</th>
<th style="padding: 2px;">Новый интервал</th>
</tr>
<tr>
<td style="padding: 2px;">0</td>
<td style="padding: 2px;">[0,000,000 ... 100,000,000)</td>
<td style="padding: 2px;"><u>a</u>braha#</td>
<td style="padding: 2px;">[0 ... 40)</td>
<td style="padding: 2px;">[0,000,000 ... 40,000,000)</td>
</tr>
<tr>
<td style="padding: 2px;">1</td>
<td style="padding: 2px;">[0,000,000 ... 40,000,000)</td>
<td style="padding: 2px;">a<u>b</u>raha#</td>
<td style="padding: 2px;">[40 ... 60)</td>
<td style="padding: 2px;">[16,000,000 ... 24,000,000)</td>
</tr>
<tr>
<td style="padding: 2px;">2</td>
<td style="padding: 2px;">[16,000,000 ... 24,000,000)</td>
<td style="padding: 2px;">ab<u>r</u>aha#</td>
<td style="padding: 2px;">[70 ... 95)</td>
<td style="padding: 2px;">[21,600,000 ... 23,600,000)</td>
</tr>
<tr>
<td style="padding: 2px;">3</td>
<td style="padding: 2px;">[21,600,000 ... 23,600,000)</td>
<td style="padding: 2px;">abr<u>a</u>ha#</td>
<td style="padding: 2px;">[0 ... 40)</td>
<td style="padding: 2px;">[21,600,000 ... 22,400,000)</td>
</tr>
<tr>
<td style="padding: 2px;">4</td>
<td style="padding: 2px;">[21,600,000 ... 22,400,000)</td>
<td style="padding: 2px;">abra<u>h</u>a#</td>
<td style="padding: 2px;">[60 ... 70)</td>
<td style="padding: 2px;">[22,080,000 ... 22,160,000)</td>
</tr>
<tr>
<td style="padding: 2px;">5</td>
<td style="padding: 2px;">[22,080,000 ... 22,160,000)</td>
<td style="padding: 2px;">abrah<u>a</u>#</td>
<td style="padding: 2px;">[0 ... 40)</td>
<td style="padding: 2px;">[22,080,000 ... 22,112,000)</td>
</tr>
<tr>
<td style="padding: 2px;">6</td>
<td style="padding: 2px;">[22,080,000 ... 22,112,000)</td>
<td style="padding: 2px;">abraha<u>#</u></td>
<td style="padding: 2px;">[95 ... 100)</td>
<td style="padding: 2px;">[22,110,400 ... 22,112,000)</td>
</tr>
</table>
<p>&nbsp;</p>
<p>В результате имеем интервал [22,110,400 ... 22,112,000). Любое значение из этого интервала даст нам результат кодирования.<br />
Для выбора оптимального значения можно выбрать то, в котором будем максимальное число нулевых младших бит среди всех возможных. Если имеем интервал [f,t), то искомой величиной будет:</p>
<p><img src="http://software.intel.com/ru-ru/blogs/wordpress/wp-content/uploads/x_f_xor_t.png" alt="x = f xor t" width="84" height="21" class="alignnone size-full wp-image-2006627" /><br />
<img src="http://software.intel.com/ru-ru/blogs/wordpress/wp-content/uploads/xl.png" alt="xl=ceil(log2x)" width="113" height="23" class="alignnone size-full wp-image-2006629" /><br />
<img src="http://software.intel.com/ru-ru/blogs/wordpress/wp-content/uploads/result2-300x38.png" alt="result = ( f &amp; !( 2^xl - 1 ) ) or ( 2^(xl-1) )" width="300" height="38" class="alignnone size-medium wp-image-2006633" /></p>
<p>Сначала XOR'ом получаем различающиеся младшие биты, затем из двоичного логарифма получаем число различающихся бит. А результирующее оптимальное значение получаем, добавляя к неизменяющейся части нижней границы единицу в старшем разряде меняющейся части.</p>
<p>Если брать просто среднее значение интервала, то в качестве ответа будет 22,111,200. Если взять оптимальное значение, то получим 22,111,232. Для сравнения, их бинарное представление:</p>
<blockquote><p>22,111,200 = 10101000101100<b>01111100000</b><br />
22,111,232 = 10101000101100<b>10000000000</b></p></blockquote>
<p>Декодирование выполняется еще более похоже на арифметическое кодирование:</p>
<ol>
<li>имея значение <b>cur</b> из текущего интервала <b>[f,t)</b>, получаем соответствующий символ <b>c</b>;</li>
<li>нормализуем интервал до <b>[0,N)</b>, получая новое значение <b>new</b> по формуле:<br /><img src="http://software.intel.com/ru-ru/blogs/wordpress/wp-content/uploads/new1.png" alt="new" width="161" height="51" class="alignnone size-full wp-image-2006636" /></li>
<li>повторяем шаги 1 и 2 до встречи терминального символа, либо до достижения исходной длины строки (если известно).</li>
</ol>
<p>Декодирование строки по шагам:</p>
<table border="1" style="border:1px solid #000;border-collapse:collapse">
<tr>
<th style="padding: 2px;">Шаг</th>
<th style="padding: 2px;">Текущее значение</th>
<th style="padding: 2px;">Интервал</th>
<th style="padding: 2px;">Выход</th>
<th style="padding: 2px;">Новое значение</th>
</tr>
<tr>
<td style="padding: 2px;">0</td>
<td style="padding: 2px;">22,111,232</td>
<td style="padding: 2px;">[0,000,000 ... 40,000,000)</td>
<td style="padding: 2px;">a</td>
<td style="padding: 2px;">55,278,080</td>
</tr>
<tr>
<td style="padding: 2px;">1</td>
<td style="padding: 2px;">55,278,080</td>
<td style="padding: 2px;">[40,000,000 ... 60,000,000)</td>
<td style="padding: 2px;">b</td>
<td style="padding: 2px;">76,390,400</td>
</tr>
<tr>
<td style="padding: 2px;">2</td>
<td style="padding: 2px;">76,390,400</td>
<td style="padding: 2px;">[70,000,000 ... 95,000,000)</td>
<td style="padding: 2px;">r</td>
<td style="padding: 2px;">25,561,600</td>
</tr>
<tr>
<td style="padding: 2px;">3</td>
<td style="padding: 2px;">25,561,600</td>
<td style="padding: 2px;">[0,000,000 ... 40,000,000)</td>
<td style="padding: 2px;">a</td>
<td style="padding: 2px;">63,904,000</td>
</tr>
<tr>
<td style="padding: 2px;">4</td>
<td style="padding: 2px;">63,904,000</td>
<td style="padding: 2px;">[60,000,000 ... 70,000,000)</td>
<td style="padding: 2px;">h</td>
<td style="padding: 2px;">39,040,000</td>
</tr>
<tr>
<td style="padding: 2px;">5</td>
<td style="padding: 2px;">39,040,000</td>
<td style="padding: 2px;">[0,000,000 ... 40,000,000)</td>
<td style="padding: 2px;">a</td>
<td style="padding: 2px;">55,278,080</td>
</tr>
<tr>
<td style="padding: 2px;">6</td>
<td style="padding: 2px;">55,278,080</td>
<td style="padding: 2px;">[95,000,000 ... 100,000,000)</td>
<td style="padding: 2px;">#</td>
<td style="padding: 2px;">52,000,000</td>
</tr>
</table>
<p>&nbsp;</p>
<p>Ну и пример реализации на Python:</p>
<pre name="code" class="python">
#!/usr/bin/env python
#-*- coding: utf8 -*-

from math import floor, log, ceil

class Arithmetic:

    def __init__(self, freqs_tuples, MAX):
        # верхняя граница интервала
        self.max = MAX
        # подготовка словаря с интервалами вероятностей символов
        self.__freqs2dict( freqs_tuples )

    def encode(self, s):
        u"""
            Кодирование строки s на основе вероятностей в self.freqs
            Возвращает число из итогового диапазона
        """
        # начинает с полного интервала [0,1)
        (f, t) = (0, self.max)
        for c in s:
            # координаты нового интервала
            (cf, ct) = self.freqs[c]
            # длина базового интервала
            t_f = t-f
            # Получение нового интервала.
            # Важно: т.к. в вычислении фигурирует f, то его
            # изменение выполняется только после вычисления t.
            # Т.е. не надо менять строки местами : )
            t = int( floor( ct*t_f / self.max + f ) )
            f = int( floor( cf*t_f / self.max + f ) )
        return self.__optimize( f, t )

    def decode(self, i):
        u"""
            Декодирование числа в строку на основе вероятностей в self.freqs
        """
        res = []
        c = None
        while c != '#':
            c = self.__find_interval( i )
            res.append( c )
            (f,t) = self.freqs[c]
            i = int( ( i-f ) * self.max / ( t-f ) )
        return ''.join(res)

    def __freqs2dict(self, freqs_tuples):
        u"""
            Преобразование кортежа вероятностей символов строки в
              словарь интервалов с учетом self.max
        """
        self.freqs = {}
        left = 0
        for (k,v) in freqs:
            self.freqs[k] = ( int(left*self.max), int((left+v)*self.max) )
            left += v

    def __optimize(self, f, t):
        u"""
            Подбор числа из диапазона [f, t) такого, чтобы в нем
            было максимально возможное на интервале число нулевых младших бит
        """
        #return int( (t-f)/2 + f )
        f = int(f)
        t = int(t)
        x = f ^ t
        if not x: return f

        xl = int( ceil( log(x, 2) ) )
        if not xl: return f

        mask_and = ~((1&lt;&lt;xl)-1)
        mask_or  = 1&lt;&lt;(xl-1)

        r = (f &#038; mask_and) | mask_or

        return r

    def __find_interval(self, v):
        u"""
            Ищем интервал, которому принадлежит значение v
        """
        for ( c, (cf, ct) ) in self.freqs.items():
            if v&gt;=cf and v&lt;ct:
                return c

freqs = ( ('a',0.4), ('b',0.2), ('h',0.1), ('r',0.25), ('#',0.05) )
src = 'abraha#'
MAX = 100000000

coder = Arithmetic( freqs, MAX )

print 'SOURCE:  ', src

enc = coder.encode( src )
print 'ENCODED: ', enc

dec = coder.decode( enc )
print 'DECODED: ', dec
</pre>
<p>Несмотря на хранение промежуточных значений в целом виде, выполняется много операций деления и умножения. Существуют реализации интервального кодирования, исключающие эти медленные операции. Кроме того, при кодировании старшие разряды, которые уже не меняются, можно сразу отдавать в выходной поток. Но про это все уже не сейчас.</p>
]]></content:encoded>
			<wfw:commentRss>http://software.intel.com/ru-ru/blogs/2011/12/28/range-encoding/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Движемся к началу: MTF (Move To Front) в помощь энтропийному кодированию</title>
		<link>http://software.intel.com/ru-ru/blogs/2011/12/26/mtf-move-to-front/</link>
		<comments>http://software.intel.com/ru-ru/blogs/2011/12/26/mtf-move-to-front/#comments</comments>
		<pubDate>Mon, 26 Dec 2011 12:59:16 +0000</pubDate>
		<dc:creator>atercattus</dc:creator>
				<category><![CDATA[Intel Software Network]]></category>
		<category><![CDATA[Графика]]></category>
		<category><![CDATA[coding]]></category>
		<category><![CDATA[decoding]]></category>
		<category><![CDATA[move-to-front]]></category>
		<category><![CDATA[mtf]]></category>

		<guid isPermaLink="false">http://software.intel.com/ru-ru/blogs/2011/12/26/mtf-move-to-front/</guid>
		<description><![CDATA[<p>Решил написать небольшую статейку в дополнение к серии статей <a href="http://software.intel.com/ru-ru/blogs/author/dmitry-serkin/">Dmitry Serkin</a> по сжатию (в частности изображений).</p>

<p>Как-то все непосредственно само сжатие, да итоговое досжатие тем же арифметическим кодером.</p>

<p>Здесь же решил описать опциональную стадию предобработки сжатых основными шагами конвейера бинарных данных, но до подачи их энтропийному кодеру (ЭК).</p>]]></description>
			<content:encoded><![CDATA[<p>Решил написать небольшую статейку в дополнение к серии статей <a href="http://software.intel.com/ru-ru/blogs/author/dmitry-serkin/">Dmitry Serkin</a> по сжатию (в частности изображений).</p>
<p>Как-то все непосредственно само сжатие, да итоговое досжатие тем же арифметическим кодером.</p>
<p>Здесь же решил описать опциональную стадию предобработки сжатых основными шагами конвейера бинарных данных, но до подачи их энтропийному кодеру (ЭК).</p>
<p>Что лучше сожмет ЭК? Конечно можно подать на вход 100500 нулей, но с такой вырожденной задачей отлично справится RLE. Тут важна предсказуемость и монотонность данных в потоке: было бы хорошо, если бы одинаковые значения (байт, бит) шли последовательно.</p>
<p>Можно рассматривать два наиболее популярных алгоритма: BWT и MTF.</p>
<p><i>В этот раз напишу про второй, т.к. первый (Burrows–Wheeler transform, преобразование Барроуза-Уиллера) прекрасно описан на Википедии, и смысла рассказывать тоже самое своими словами мало. Но если только кому-то будет интересно... <img src='http://software.intel.com/ru-ru/blogs/wordpress/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </i></p>
<p>Итак, MTF (Move-To-Front, Движение к началу, Стопка книг, Сортировка стопки книг) является обратимым преобразованием над сжимаемым потоком, выполняемым с целью улучшения результатов последующего ЭК.</p>
<p>Алгоритм работает над блоками байт (машинных слов, etc), подменяя их соответствующими позициями в некотором словаре. При этом при обработке каждого символа словарь модифицируется, что приводит к выдаче различных позиций для одного и того же символа в процессе работы алгоритма.</p>
<p>Первоначально словарь заполняется всеми возможными значениями обрабатываемого блока (потока). Это заполнение идентично выполняется и на стороне архиватора, так и на стороне распаковщика.</p>
<p>Кодирование заключается в последовательном обходе всех символов входной последовательности. Символ ищется в словаре, а в выходной поток записывается его <u>текущая</u> позиция. После этого символ удаляется из своей позиции в словаре (сдвиг вправо значений словаря с 0 до позиции символа-1), и вставляется в начало словаря (присвоение нулевому элементу).</p>
<p>Для примера возьмем блок "20112012" и словарь из цифр "0".."9". Если сразу сжимать RLE, то будет найдено только "11" и ничего хорошего не выйдет. Если сжимать, Хаффманом или PPM с окном на все 8 байт, то будет найдено слово "201", что уже хорошо.</p>
<p>А теперь попробуем сначала преобразовать по MTF:</p>
<table border="1"  style="border:1px solid #888;cellpadding: 5">
<tr>
<th>Шаг</th>
<th>Вход</th>
<th>Выход</th>
<th>Словарь</th>
</tr>
<tr>
<td style="padding: 2px;">0</td>
<td style="padding: 2px;"><u>2</u>0112012</td>
<td style="padding: 2px;"><i>Пустой список</i></td>
<td style="padding: 2px;">0123456789</td>
</tr>
<tr>
<td style="padding: 2px;">1</td>
<td style="padding: 2px;"><u>2</u>0112012</td>
<td style="padding: 2px;">2</td>
<td style="padding: 2px;">2013456789</td>
</tr>
<tr>
<td style="padding: 2px;">2</td>
<td style="padding: 2px;">2<u>0</u>112012</td>
<td style="padding: 2px;">2,1</td>
<td style="padding: 2px;">0213456789</td>
</tr>
<tr>
<td style="padding: 2px;">3</td>
<td style="padding: 2px;">20<u>1</u>12012</td>
<td style="padding: 2px;">2,1,2</td>
<td style="padding: 2px;">1023456789</td>
</tr>
<tr>
<td style="padding: 2px;">4</td>
<td style="padding: 2px;">201<u>1</u>2012</td>
<td style="padding: 2px;">2,1,2,0</td>
<td style="padding: 2px;">1023456789</td>
</tr>
<tr>
<td style="padding: 2px;">5</td>
<td style="padding: 2px;">2011<u>2</u>012</td>
<td style="padding: 2px;">2,1,2,0,2</td>
<td style="padding: 2px;">2103456789</td>
</tr>
<tr>
<td style="padding: 2px;">6</td>
<td style="padding: 2px;">20112<u>0</u>12</td>
<td style="padding: 2px;">2,1,2,0,2,2</td>
<td style="padding: 2px;">0213456789</td>
</tr>
<tr>
<td style="padding: 2px;">7</td>
<td style="padding: 2px;">201120<u>1</u>2</td>
<td style="padding: 2px;">2,1,2,0,2,2,2</td>
<td style="padding: 2px;">1023456789</td>
</tr>
<tr>
<td style="padding: 2px;">8</td>
<td style="padding: 2px;">2011201<u>2</u></td>
<td style="padding: 2px;">2,1,2,0,2,2,2,2</td>
<td style="padding: 2px;">2103456789</td>
</tr>
</table>
<p>И по шагам:</p>
<ol start="0">
<li>Во входном буфере смотрим на первый символ, результирующий список позиций пуст, словарь с исходном виде;</li>
<li>Текущий символ "2" имеет позицию 2 в словаре. Добавляем 2 в результат, а "2" в словаре переносим в начало, получая "20134...";</li>
<li>Следущий символ "0" имеет позицию 1 в текущем словаре. Добавляем 1 в результат, а "0" в словаре переносим в начало, получая "02134..."</li>
<li>Аналогично повторяем для всего блока;</li>
<li>В результате получаем последовательность позиций символов в меняющемся словаре.</li>
</ol>
<p>В итоге получаем последовательность "21202222". Которая в некоторых случаях будет сжата компактнее исходного варианта <img src='http://software.intel.com/ru-ru/blogs/wordpress/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' />  </p>
<p>Однако толку мало, если обратно раскодировать нельзя. А делается это просто: выполняем все в обратную сторону (но с исходным словарем):</p>
<table border="1" style="border:1px solid #888;border-collapse:collapse">
<tr>
<th>Шаг</th>
<th>Вход</th>
<th>Выход</th>
<th>Словарь</th>
</tr>
<tr>
<td style="padding: 2px;">0</td>
<td style="padding: 2px;"><u>2</u>1202222</td>
<td style="padding: 2px;"><i>Пусто</i></td>
<td style="padding: 2px;">0123456789</td>
</tr>
<tr>
<td style="padding: 2px;">1</td>
<td style="padding: 2px;"><u>2</u>1202222</td>
<td style="padding: 2px;">2</td>
<td style="padding: 2px;">2013456789</td>
</tr>
<tr>
<td style="padding: 2px;">2</td>
<td style="padding: 2px;">2<u>1</u>202222</td>
<td style="padding: 2px;">2,0</td>
<td style="padding: 2px;">0213456789</td>
</tr>
<tr>
<td style="padding: 2px;">3</td>
<td style="padding: 2px;">21<u>2</u>02222</td>
<td style="padding: 2px;">2,0,1</td>
<td style="padding: 2px;">1023456789</td>
</tr>
<tr>
<td style="padding: 2px;">4</td>
<td style="padding: 2px;">212<u>0</u>2222</td>
<td style="padding: 2px;">2,0,1,1</td>
<td style="padding: 2px;">1023456789</td>
</tr>
<tr>
<td style="padding: 2px;">5</td>
<td style="padding: 2px;">2120<u>2</u>222</td>
<td style="padding: 2px;">2,0,1,1,2</td>
<td style="padding: 2px;">2103456789</td>
</tr>
<tr>
<td style="padding: 2px;">6</td>
<td style="padding: 2px;">21202<u>2</u>22</td>
<td style="padding: 2px;">2,0,1,1,2,0</td>
<td style="padding: 2px;">0213456789</td>
</tr>
<tr>
<td style="padding: 2px;">7</td>
<td style="padding: 2px;">212022<u>2</u>2</td>
<td style="padding: 2px;">2,0,1,1,2,0,1</td>
<td style="padding: 2px;">1023456789</td>
</tr>
<tr>
<td style="padding: 2px;">8</td>
<td style="padding: 2px;">2120222<u>2</u></td>
<td style="padding: 2px;">2,0,1,1,2,0,1,2</td>
<td style="padding: 2px;">2103456789</td>
</tr>
</table>
<p>
И по шагам:</p>
<ol start="0">
<li>Во входном буфере смотрим на первый символ (позицию в словаре), результирующий список позиций пуст, словарь с исходном виде;</li>
<li>Текущий символ 2, а по позиции 2 в словаре символ "2". Добавляем "2" в результат, а "2" в словаре переносим в начало, получая "20134...";</li>
<li>Следущий символ 1, с этой позицией у нас символ "0". Добавляем "0" в результат, а "0" в словаре переносим в начало, получая "02134..."</li>
<li>Аналогично повторяем для всего блока;</li>
<li>В результате получаем исходную строку байт "20112012".</li>
</ol>
<p>Как можно заметить, итоговый словарь совпадает в обоих случаях.</p>
<p>Пример реализации на Python:</p>
<pre name="code" class="python">
#!/usr/bin/env python
#-*- coding: utf8 -*-

# создаем исходный словарь
DICT = [chr(i) for i in range(ord('0'),ord('9')+1)]

src = '20112012'

def encode(s,d):
    seq = []
    for c in s:
        i = d.index(c)
        seq.append(i)
        d.pop(i)
        d.insert(0,c)
    return seq

def decode(seq,d):
    s = []
    for i in seq:
        c = d[i]
        s.append(c)
        d.pop(i)
        d.insert(0,c)
    return ''.join(s)

print 'SOURCE:  ', src

enc=encode( src, DICT[:] )  # передаем копию словаря
print 'ENCODED: ', enc

dec=decode(enc,DICT[:])  # передаем копию словаря
print 'DECODED: ', dec
</pre>
<p>Стоит заметить, что битовые длины значений выходной последовательности не превышают длины значений во входном потоке (т.к. позиции не превышают размер словаря, который содержит все возможные значения в потоке).</p>
<p>Это позволяет сохранять результирующую последовательность индексов в туже область памяти, откуда читаются входные данные.</p>
<p>Само собой, если нет требования к сохранению исходных данных.</p>
]]></content:encoded>
			<wfw:commentRss>http://software.intel.com/ru-ru/blogs/2011/12/26/mtf-move-to-front/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

