Сколько движется, чтобы добраться до места назначения? Эффективное заполнение паводков - PullRequest
3 голосов
/ 14 ноября 2011

Я хочу вычислить расстояние ячеек от ячейки назначения, используя количество четырехсторонних движений для достижения чего-либо. Таким образом, четыре ячейки, непосредственно примыкающие к месту назначения, имеют расстояние 1, а ячейки в четырех основных направлениях каждой из них имеют расстояние 2 и так далее. Существует максимальное расстояние, которое может составлять около 16 или 20, и есть ячейки, которые заняты барьерами; расстояние может течь вокруг них, но не через них.

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

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

Я доволен функциональностью и теперь хочу сосредоточиться на оптимизации кода, так как он очень чувствителен к производительности.

Какие хитрые и быстрые подходы могут быть?

An example map for all cells within four moves of the target x; black denotes barrier

Ответы [ 4 ]

2 голосов
/ 14 ноября 2011

Я думаю, что вы все сделали правильно.Если вы закодировали его правильно, то для вычисления заливки затрачивается время O (n) и память O (n), где n - количество ячеек, и можно доказать, что это невозможно сделать лучше (в общем случае).И после того, как заполнение завершено, вы просто возвращаете расстояние для любого пункта назначения с помощью O (1), легко увидеть, что это также можно сделать лучше.

Так что, если вы хотите оптимизировать производительность, вы можете сосредоточиться только на CODEМЕСТНАЯ ОПТИМИЗАЦИЯ.Что не повлияет на асимптотику, но может значительно улучшить ваше реальное время выполнения.Но трудно дать вам какой-либо совет по оптимизации кода, не видя исходного кода.

Так что, если вы действительно хотите увидеть оптимизированный код, посмотрите следующее (Pure C):

include

int* BFS()
{
    int N, M; // Assume we have NxM grid.
    int X, Y; // Start position. X, Y are unit based.
    int i, j;
    int movex[4] = {0, 0, 1, -1}; // Move on x dimension.
    int movey[4] = {1, -1, 0, 0}; // Move on y dimension.

    // TO DO: Read N, M, X, Y

    // To reduce redundant functions calls and memory reallocation 
    // allocate all needed memory once and use a simple arrays.
    int* map = (int*)malloc((N + 2) * (M + 2)); 
    int leadDim = M + 2;
    // Our map. We use one dimension array. map[x][y] = map[leadDim * x + y];
    // If (x,y) is occupied then map[leadDim*x + y] = -1;
    // If (x,y) is not visited map[leadDim*x + y] = -2;

    int* queue = (int*)malloc(N*M);
    int first = 0, last =1; 

    // Fill the boarders to simplify the code and reduce conditions
    for (i = 0; i < N+2; ++i)
    {
        map[i * leadDim + 0] = -1;
        map[i * leadDim + M + 1] = -1;
    }

    for (j = 0; j < M+2; ++j)
    {
        map[j] = -1;
        map[(N + 1) * leadDim + j] = -1;
    }

    // TO DO: Read the map.

    queue[first] = X * leadDim + Y;
    map[X * leadDim + Y] = 0;

    // Very simple optimized process loop.
    while (first < last) 
    {
        int current = queue[first];
        int step = map[current];

        for (i = 0; i < 4; ++i)
        {
            int temp = current + movex[i] * leadDim + movey[i];
            if (map[temp] == -2) // only one condition in internal loop.
            {
                map[temp] = step + 1;
                queue[last++] = temp;
            }
        }

        ++first;
    }

    free(queue);

    return map;
}

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

1 голос
/ 14 ноября 2011

Это обычная задача для BFS .Сложность O (cellCount)

Моя реализация c ++:

vector<vector<int> > GetDistance(int x, int y, vector<vector<int> > cells)
{
    const int INF = 0x7FFFFF;
    vector<vector<int> > distance(cells.size());
    for(int i = 0; i < distance.size(); i++)
        distance[i].assign(cells[i].size(), INF);
    queue<pair<int, int> > q;

    q.push(make_pair(x, y));
    distance[x][y] = 0;

    while(!q.empty())
    {
        pair<int, int> curPoint = q.front();
        q.pop();
        int curDistance = distance[curPoint.first][curPoint.second];
        for(int i = -1; i <= 1; i++)
            for(int j = -1; j <= 1; j++)
            {
                if( (i + j) % 2 == 0 ) continue;
                pair<int, int> nextPoint(curPoint.first + i, curPoint.second + j);
                if(nextPoint.first >= 0 && nextPoint.first < cells.size()
                   && nextPoint.second >= 0 && nextPoint.second < cells[nextPoint.first].size()
                   && cells[nextPoint.first][nextPoint.second] != BARRIER
                   && distance[nextPoint.first][nextPoint.second] > curDistance + 1)
                   {
                       distance[nextPoint.first][nextPoint.second] = curDistance + 1;
                       q.push(nextPoint);
                   }                    
            }
    }
    return distance;
}
0 голосов
/ 09 октября 2016

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

Начиная с начального квадрата, сканируйте влево и вправо, пока не будут найдены "стены". Теперь у вас есть «промежуток», который составляет один квадрат в высоту и N квадратов в ширину. Отметьте промежуток как «заполненный», в этом случае каждый квадрат с расстоянием до исходного квадрата.

Для каждого квадрата выше и ниже текущего диапазона, если он не является «стеной» или уже заполнен, выберите его в качестве нового источника диапазона.

Повторять до тех пор, пока не будут найдены новые пролеты.

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

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

0 голосов
/ 14 ноября 2011

Начните с рекурсивной реализации: (непроверенный код)

 int visit( int xy, int dist) {
    int ret =1;
    if (array[xy] <= dist) return 0;
    array[xy] = dist;
    if (dist == maxdist) return ret;
    ret += visit ( RIGHT(xy) , dist+1);
    ... 
    same for left, up, down
    ...
    return ret;
    }

Вам нужно будет обработать инициализацию и крайние случаи.И вы должны решить, хотите ли вы двумерный массив или одномерный массив.

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

...