Быстрый алгоритм размещения блока, совет необходим?

Я должен эмулировать стратегию размещения окна менеджера окон Fluxbox.

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

Если Вам установили Fluxbox и Xterm в Вашей системе, можно попробовать xwinmidiarptoy сценарий Bash для наблюдения грубого прототипа того, что я хочу произойти. См. записки xwinmidiarptoy.txt, которые я написал об этом объясняющий, что это делает и как это должно использоваться.

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

Алгоритм должен быть Алгоритмом Онлайн, обрабатывающим данные "часть частью последовательным способом, т.е. порядком, что вход питается к алгоритму, не имея весь вход в наличии от запуска".

Стратегия размещения окна Fluxbox имеет три двоичных опции, которые я хочу эмулировать:

  • Сборка Windows горизонтальные строки или вертикальные столбцы (потенциально)

  • Windows помещается слева направо или справа налево

  • Windows помещается сверху донизу или нижняя часть к вершине

Различия между целевым алгоритмом и алгоритмом размещения окна

Координатные единицы не являются пикселями. Сетка, в которой будут помещены блоки, будет единицами 128 x 128. Кроме того, область размещения может быть далее уменьшена граничной областью, помещенной в сетке.

Почему алгоритм является проблемой?

Это должно работать к крайним срокам оперативного потока в аудиоприложении.

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

И хотя алгоритм никогда не будет помещать окно, которое перекрывает другого, пользователь сможет поместить и переместить определенные типы блоков, накладывающиеся окна будут существовать. Структура данных, используемая для хранения окон и/или свободного пространства, должна смочь обработать это перекрытие.

До сих пор у меня есть два варианта, для которых я создал свободные прототипы:

1) Порт алгоритма размещения Fluxbox в мой код.

Проблема с этим, клиент (моя программа) вышиблен из аудио сервера (ДЖЕК), когда я пытаюсь поместить худший вариант развития событий 256 блоков с помощью алгоритма. Этот алгоритм выполняет более чем 14 000 полных (линейных) сканирований списка блоков, уже помещенных при размещении 256-го окна.

Для демонстрации этого я создал программу, названную text_boxer-0.0.2.tar.bz2, который берет текстовый файл в качестве входа и располагает его в полях ASCII. Проблема make создавать его. Немного недружелюбный, использовать --help (или любая другая недопустимая опция) для списка параметров командной строки. Необходимо указать текстовый файл при помощи опции.

2) Мой альтернативный подход.

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

Кроме того, каждая структура данных блока также содержит четыре ссылки, которые соединяются с каждым сразу смежным (касающимся) блоком на каждой из этих четырех сторон.

ВАЖНОЕ ПРАВИЛО: Каждый блок может только затронуть с одним блоком на сторону. Это - правило, характерное для способа алгоритма сохранить свободное неиспользуемое место, и не переносит фактора в том, сколько фактических окон может коснуться друг друга.

Проблема с этим подходом, это очень сложно. Я реализовал простые случаи где 1) пространство удалено из одного угла блока, 2) разделение соседних блоков так, чтобы ВАЖНОЕ ПРАВИЛО придерживалось к.

Менее простой случай, где пространство, которое будет удалено, может только быть найдено в столбце или строке полей, только частично реализован - если один из блоков, которые будут удалены, является точным пригодным для ширины (т.е. столбец) или высота (т.е. строка) затем, проблемы происходят. И даже не упоминайте факт, это только проверяет столбцы одно широкое поле, и располагает в ряд одно высокое поле.

Я реализовал этот алгоритм в C - язык, который я использую для этого проекта (я не использовал C++ в течение нескольких лет, и неудобное использование его сосредоточив все мое внимание к разработке C, это - хобби). Реализация 700 + строки кода (включая большое количество пустых строк, строк фигурной скобки, комментарии и т.д.). Реализация только работает на горизонтальные строки + лево-право + стратегия размещения главной нижней части.

Таким образом, я или добрался для добавления некоторого способа сделать это +700 строками работы кода для других 7 опций стратегии размещения, или я оказываюсь перед необходимостью копировать те +700 строк кода для других семи опций. Ни один из них не является привлекательным, первым, потому что существующий код является достаточно сложным, вторым из-за чрезмерного увеличения размера.

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

Текущее состояние реализации C этого алгоритма является freespace.c. Я использую gcc -O0 -ggdb freespace.c создавать, и выполнять его в xterm, измеренном к по крайней мере символам 124 x 60.

Что еще там?

Я скользил и обесценил:

  • Алгоритмы Упаковки мусорного ведра: их акцент на оптимальное соответствие не соответствует требованиям этого алгоритма.

  • Рекурсивные алгоритмы Размещения Деления пополам: обещание звуков, но это для проектирования схем. Их акцент является оптимальной проводной длиной.

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

Каковы Ваши мысли об этом? Как Вы приблизились бы к нему? На что другие алгоритмы я должен посмотреть? Или даже что понятия я должен исследовать наблюдение, поскольку я не изучил информатику / разработка программного обеспечения?

Задайте вопросы в комментариях, если дополнительная информация необходима.

Дальнейшие идеи, разработанные начиная с задавания этого вопроса

  • Некоторая комбинация моего "альтернативного алгоритма" с пространственным hashmap для идентификации, если бы большое окно, которое будет помещено, покрыло бы несколько блоков свободного пространства.

10
задан 12 revs, 2 users 100% 21 April 2015 в 01:05
поделиться

3 ответа

#include <limits.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>


#define FSWIDTH 128
#define FSHEIGHT 128


#ifdef USE_64BIT_ARRAY
    #define FSBUFBITS 64
    #define FSBUFWIDTH 2
    typedef uint64_t fsbuf_type;
    #define TRAILING_ZEROS( v ) __builtin_ctzl(( v ))
    #define LEADING_ONES( v )   __builtin_clzl(~( v ))
#else
#ifdef USE_32BIT_ARRAY
    #define FSBUFBITS 32
    #define FSBUFWIDTH 4
    typedef uint32_t fsbuf_type;
    #define TRAILING_ZEROS( v ) __builtin_ctz(( v ))
    #define LEADING_ONES( v )   __builtin_clz(~( v ))
#else
#ifdef USE_16BIT_ARRAY
    #define FSBUFBITS 16
    #define FSBUFWIDTH 8
    typedef uint16_t fsbuf_type;
    #define TRAILING_ZEROS( v ) __builtin_ctz( 0xffff0000 | ( v ))
    #define LEADING_ONES( v )   __builtin_clz(~( v ) << 16)
#else
#ifdef USE_8BIT_ARRAY
    #define FSBUFBITS 8
    #define FSBUFWIDTH 16
    typedef uint8_t fsbuf_type;
    #define TRAILING_ZEROS( v ) __builtin_ctz( 0xffffff00 | ( v ))
    #define LEADING_ONES( v )   __builtin_clz(~( v ) << 24)
#else
    #define FSBUFBITS 1
    #define FSBUFWIDTH 128
    typedef unsigned char fsbuf_type;
    #define TRAILING_ZEROS( v ) (( v ) ? 0 : 1)
    #define LEADING_ONES( v )   (( v ) ? 1 : 0)
#endif
#endif
#endif
#endif


static const fsbuf_type fsbuf_max =   ~(fsbuf_type)0;
static const fsbuf_type fsbuf_high =  (fsbuf_type)1 << (FSBUFBITS - 1);


typedef struct freespacegrid
{
    fsbuf_type buf[FSHEIGHT][FSBUFWIDTH];

    _Bool left_to_right;
    _Bool top_to_bottom;

} freespace;


void freespace_dump(freespace* fs)
{
    int x, y;

    for (y = 0; y < FSHEIGHT; ++y)
    {
        for (x = 0; x < FSBUFWIDTH; ++x)
        {
            fsbuf_type i = FSBUFBITS;
            fsbuf_type b = fs->buf[y][x];

            for(; i != 0; --i, b <<= 1)
                putchar(b & fsbuf_high ? '#' : '/');
/*
            if (x + 1 < FSBUFWIDTH)
                putchar('|');
*/
        }
        putchar('\n');
    }
}


freespace* freespace_new(void)
{
    freespace* fs = malloc(sizeof(*fs));

    if (!fs)
        return 0;

    int y;

    for (y = 0; y < FSHEIGHT; ++y)
    {
        memset(&fs->buf[y][0], 0, sizeof(fsbuf_type) * FSBUFWIDTH);
    }

    fs->left_to_right = true;
    fs->top_to_bottom = true;

    return fs;
}


void freespace_delete(freespace* fs)
{
    if (!fs)
        return;

    free(fs);
}

/* would be private function: */
void fs_set_buffer( fsbuf_type buf[FSHEIGHT][FSBUFWIDTH],
                    unsigned x,
                    unsigned y1,
                    unsigned xoffset,
                    unsigned width,
                    unsigned height)
{
    fsbuf_type v;
    unsigned y;

    for (; width > 0 && x < FSBUFWIDTH; ++x)
    {
        if (width < xoffset)
            v = (((fsbuf_type)1 << width) - 1) << (xoffset - width);
        else if (xoffset < FSBUFBITS)
            v = ((fsbuf_type)1 << xoffset) - 1;
        else
            v = fsbuf_max;

        for (y = y1; y < y1 + height; ++y)
        {
#ifdef FREESPACE_DEBUG
            if (buf[y][x] & v)
                printf("**** over-writing area ****\n");
#endif
            buf[y][x] |= v;
        }

        if (width < xoffset)
            return;

        width -= xoffset;
        xoffset = FSBUFBITS;
    }
}


_Bool freespace_remove(   freespace* fs,
                          unsigned width, unsigned height,
                          int* resultx,   int* resulty)
{
    unsigned x, x1, y;
    unsigned w, h;
    unsigned xoffset, x1offset;
    unsigned tz; /* trailing zeros */

    fsbuf_type* xptr;
    fsbuf_type mask =   0;
    fsbuf_type v;

    _Bool scanning = false;
    _Bool offset = false;

    *resultx = -1;
    *resulty = -1;

    for (y = 0; y < (unsigned) FSHEIGHT - height; ++y)
    {
        scanning = false;
        xptr = &fs->buf[y][0];

        for (x = 0; x < FSBUFWIDTH; ++x, ++xptr)
        {
            if(*xptr == fsbuf_max)
            {
                scanning = false;
                continue;
            }

            if (!scanning)
            {
                scanning = true;
                x1 = x;
                x1offset = xoffset = FSBUFBITS;
                w = width;
            }
retry:
            if (w < xoffset)
                mask = (((fsbuf_type)1 << w) - 1) << (xoffset - w);
            else if (xoffset < FSBUFBITS)
                mask = ((fsbuf_type)1 << xoffset) - 1;
            else
                mask = fsbuf_max;

            offset = false;

            for (h = 0; h < height; ++h)
            {
                v = fs->buf[y + h][x] & mask;

                if (v)
                {
                    tz = TRAILING_ZEROS(v);
                    offset = true;
                    break;
                }
            }

            if (offset)
            {
                if (tz)
                {
                    x1 = x;
                    w = width;
                    x1offset = xoffset = tz;
                    goto retry;
                }
                scanning = false;
            }
            else
            {
                if (w <= xoffset) /***** RESULT! *****/
                {
                    fs_set_buffer(fs->buf, x1, y, x1offset, width, height);
                    *resultx = x1 * FSBUFBITS + (FSBUFBITS - x1offset);
                    *resulty = y;
                    return true;
                }
                w -= xoffset;
                xoffset = FSBUFBITS;
            }
        }
    }
    return false;
}


int main(int argc, char** argv)
{
    int x[1999];
    int y[1999];
    int w[1999];
    int h[1999];

    int i;

    freespace* fs = freespace_new();

    for (i = 0; i < 1999; ++i, ++u)
    {
        w[i] = rand() % 18 + 4;
        h[i] = rand() % 18 + 4;

        freespace_remove(fs, w[i], h[i], &x[i], &y[i]);
/*
        freespace_dump(fs);
        printf("w:%d h:%d x:%d y:%d\n", w[i], h[i], x[i], y[i]);
        if (x[i] == -1)
            printf("not removed space %d\n", i);
        getchar();
*/
    }

    freespace_dump(fs);
    freespace_delete(fs);

    return 0;
}

Приведенный выше код требует одного из USE_64BIT_ARRAY, USE_32BIT_ARRAY, USE_16BIT_ARRAY, USE_8BIT_ARRAY должны быть определены, иначе она вернется к использованию только старшего бита unsigned char для хранения состояния ячеек сетки.

Функция fs_set_buffer не будет объявлена в заголовке и станет статической в реализации, когда этот код будет разделен между .h и .c файлами. Для удаления использованного пространства из сетки будет предоставлена более удобная функция, скрывающая детали реализации.

В целом, эта реализация без оптимизации быстрее, чем мой предыдущий ответ с максимальной оптимизацией (использование GCC на 64-битной Gentoo, параметры оптимизации -O0 и -O3 соответственно).

Что касается USE_NNBIT_ARRAY и различных размеров битов, я использовал два различных метода синхронизации кода, который делает 1999 вызовов freespace_remove.

Тайминг main() с помощью команды Unix time (и отключение любого вывода в коде), казалось, подтвердил правильность моих ожиданий - что более высокие битовые размеры быстрее.

С другой стороны, хронометраж отдельных вызовов freespace_remove (с помощью gettimeofday) и сравнение максимального времени, затраченного на 1999 вызовов, показало, что меньшие размеры битов быстрее.

Это было проверено только на 64-разрядной системе (Intel Dual Core II).

1
ответ дан 4 December 2019 в 02:25
поделиться

Я бы подумал о какой-то пространственной структуре хэширования. Представьте, что все ваше свободное пространство имеет грубую сетку, назовем их блоками. Когда окна появляются и исчезают, они занимают определенные наборы смежных прямоугольных блоков. Для каждого блока необходимо отслеживать наибольший неиспользуемый прямоугольник, приходящийся на каждый угол, поэтому на каждый блок нужно хранить 2*4 вещественных числа. Для пустого блока прямоугольники в каждом углу имеют размер, равный размеру блока. Таким образом, блок может быть "использован" только в своих углах, и поэтому в любом блоке может находиться не более 4 окон.

Теперь каждый раз, когда вы добавляете окно, вы должны искать прямоугольный набор блоков, в который поместится окно, и при этом обновлять размеры свободных углов. Размер блоков должен быть таким, чтобы несколько (~4x4) из них поместились в типичное окно. Для каждого окна отслеживайте, каких блоков оно касается (вам нужно отслеживать только экстенты), а также какие окна касаются данного блока (не более 4, в данном алгоритме). Существует очевидный компромисс между гранулярностью блоков и объемом работы по вставке/удалению окна.

При удалении окна следует перебрать все блоки, которых оно касается, и для каждого блока заново вычислить размеры свободных углов (вы знаете, какие окна его касаются). Это быстро, поскольку внутренний цикл имеет длину не более 4.

Я представляю себе структуру данных типа

struct block{
    int free_x[4]; // 0 = top left, 1 = top right,
    int free_y[4]; // 2 = bottom left, 3 = bottom right
    int n_windows; // number of windows that occupy this block
    int window_id[4]; // IDs of windows that occupy this block
};
block blocks[NX][NY];

struct window{
    int id;
    int used_block_x[2]; // 0 = first index of used block,
    int used_block_y[2]; // 1 = last index of used block
};

Edit

Вот картинка:

alt text

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

Вы упомянули в редактировании, что сетка, на которой будут располагаться ваши окна, уже довольно грубая (127x127), поэтому размеры блоков, вероятно, будут что-то вроде 4 ячеек сетки на сторону, что, вероятно, не даст вам большого выигрыша. Этот метод подходит, если координаты углов вашего окна могут принимать много значений (я думал, что это будут пиксели), но в вашем случае это не так много. Вы все равно можете попробовать, так как это просто. Возможно, вы захотите также вести список абсолютно пустых блоков, чтобы при появлении окна, размер которого превышает 2 ширины блока, сначала посмотреть в этом списке, прежде чем искать подходящее свободное место в сетке блоков.

5
ответ дан 4 December 2019 в 02:25
поделиться

После нескольких неудачных попыток я в конце концов прибыл сюда. Здесь отказались от использования структур данных для хранения прямоугольных областей свободного пространства. Вместо этого есть 2d-массив с 128 x 128 элементами для достижения того же результата, но с гораздо меньшей сложностью.

Следующая функция сканирует массив на предмет размера области ширина * высота . В первой найденной позиции он записывает верхние левые координаты, куда указывают resultx и resulty.

_Bool freespace_remove( freespace* fs,
                        int width,     int height,
                        int* resultx,  int* resulty)
{
    int x = 0;
    int y = 0;
    const int rx = FSWIDTH - width;
    const int by = FSHEIGHT - height;

    *resultx = -1;
    *resulty = -1;

    char* buf[height];

    for (y = 0; y < by; ++y)
    {
        x = 0;
        char* scanx = fs->buf[y];

        while (x < rx)
        {
            while(x < rx && *(scanx + x))
                ++x;

            int w, h;

            for (h = 0; h < height; ++h)
                buf[h] = fs->buf[y + h] + x;

            _Bool usable = true;
            w = 0;

            while (usable && w < width)
            {
                h = 0;
                while (usable && h < height)
                    if (*(buf[h++] + w))
                        usable = false;
                ++w;
            }

            if (usable)
            {
                for (w = 0; w < width; ++w)
                    for (h = 0; h < height; ++h)
                        *(buf[h] + w) = 1;

                *resultx = x;
                *resulty = y;
                return true;
            }

            x += w;
        }
    }

    return false;
}

2d-массив инициализирован нулем. Любые области в массиве, где используется пространство, имеют значение 1. Эта структура и функция будут работать независимо от фактического списка окон, занимающих области, отмеченные 1.

Преимущество этого метода в его простоте. Он использует только одну структуру данных - массив. Функция короткая, и ее не должно быть слишком сложно адаптировать для обработки оставшихся вариантов размещения (здесь она обрабатывает только Row Smart + Left to Right + Top to Bottom).

Мои первые тесты также выглядят многообещающими в отношении скорости. Хотя я не думаю, что это подойдет для оконного менеджера, размещающего окна, например, на рабочем столе 1600 x 1200 с точностью до пикселей, для моих целей я считаю, что это будет намного лучше, чем любой из предыдущих методов, которые у меня есть. пытался.

Компилируемый тестовый код здесь: http://jwm-art.net/art/text/freespace_grid.c
(в Linux я использую gcc -ggdb -O0 freespace_grid .c для компиляции)

2
ответ дан 4 December 2019 в 02:25
поделиться
Другие вопросы по тегам:

Похожие вопросы: