Быстрый и эффективный поиск и изменение массива

63
5

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

МОЙ ТЕКУЩИЙ МЕТОД

Я разрабатываю шахматную игру, и части хранятся в виде чисел (действительно байтов для сохранения памяти) в одномерный массив. Есть позиция для курсора, соответствующая индексу в массиве. Для доступа к куску в текущем положении в массиве легко (piece = pieces[cursorPosition]).

Проблема в том, что для получения значений x и y для проверки правильности перемещения требуется деление и операторы по модулю (x = cursorPosition % 8; y = cursorPosition/8).

Аналогичным образом, при использовании x и y для проверки правильности ходов (вы должны сделать это таким образом по причинам, которые заполнят всю страницу), вам нужно сделать что-то вроде - просто как пример - if pieces[y * 8 + x] != 0: movePiece = False. Очевидная проблема состоит в том, чтобы сделать y * 8 + x несколько раз для доступа к массиву.

В конечном счете, это означает, что получение части тривиально, но затем получение x и y требует еще одного бит памяти и очень небольшого количества времени, чтобы вычислить его в каждом раунде.

БОЛЬШЕ ТРАДИЦИОННОГО МЕТОДА

Используя двумерный массив, можно реализовать описанный выше процесс немного легче, за исключением того факта, что поиск деталей теперь немного сложнее, и используется больше памяти. (Ie piece = pieces[cursorPosition[0]][cursorPosition[1]] или piece = pieces[x][y]).

Я не думаю, что это быстрее, и это определенно не выглядит менее интенсивным в памяти.

ЦЕЛЬ

Моя конечная цель - иметь самый быстрый код, который использует наименьший объем памяти. Это будет разработано для терминала unix (и, возможно, Windows CMD, если я смогу понять, как представлять части без цвета, используя escape-последовательности Ansi), и я либо буду использовать безопасное (зашифрованное протоколом и структурой) TCP-соединение для подключения людей p2p играть в шахматы или что-то еще, и я не знаю, сколько людей будет иметь память или насколько быстро будет работать их компьютер или насколько они будут иметь интернет-соединение.

Я также хочу научиться делать это наилучшим образом и посмотреть, можно ли это сделать.

-

Я полагаю, что мой вопрос является одним из следующих:

Какой из вышеперечисленных методов лучше предположить, что есть несколько больше вычислений, связанных с проверкой перемещения (что означает, что y * 8 + x нужно использовать много)?

или

Может быть, есть метод, который включает в себя оба преимущества 1-го и 2-го массивов с не так много сокращений, как я описал?

спросил(а) 2021-01-25T18:08:39+03:00 4 месяца, 2 недели назад
1
Решение
77

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

Во-вторых, если вы представляете свою позицию как неподписанный байт, разлагая его на X и Y, координаты будут очень быстрыми. Если мы используем следующий код C:

int getX(unsigned char pos) {
return pos%8;
}

Мы получаем следующую сборку с gcc 4.8 -O2:

getX(unsigned char):
shrb $3, %dil
movzbl %dil, %eax
ret

Если мы получим координату Y с:

int getY(unsigned char pos) {
return pos/8;
}

Мы получаем следующую сборку с gcc 4.8 -O2:

getY(unsigned char):
movl %edi, %eax
andl $7, %eax
ret

ответил(а) 2021-01-25T18:08:39+03:00 4 месяца, 2 недели назад
63

На этот вопрос нет короткого ответа; все зависит от того, сколько времени вы тратите на оптимизацию.

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

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

    (x * 8) совпадает с (x << 3) (x% 8) совпадает с (x & (8 - 1)) (x/8) совпадает с (x >> 3)

Эти операции обычно выполняются за один такт. На многих современных архитектурах они могут выполняться менее чем за один такт (включая архитектуры ARM). Не беспокойтесь об использовании побитовых операторов вместо *,% и /. Если вы используете компилятор менее десяти лет, он оптимизирует его для вас и использует побитовые операции.

Вместо этого вы должны сосредоточиться на том, насколько легко вам будет узнать, является ли движение законным, например. Это поможет вашему компьютерному игроку "быстро подумать".

Если вы используете массив 8 * 8, то вам легко увидеть, где замок может двигаться, проверяя, изменились ли только x или y. Если вы проверяете королеву, то X должен либо быть одинаковым, либо перемещать то же количество шагов, что и позиция Y.

Если вы используете одномерный массив, у вас также есть преимущества.

Но с точки зрения производительности, может быть хорошей идеей использовать массив 16x16 или массив 1x256. Заполните весь массив значениями 0x80 (например, "незаконная позиция"). Затем заполните поля с полем 0x00.


Если вы используете массив 1x256, вы можете проверить бит 3 и 7 индекса. Если какой-либо из них установлен, то позиция находится вне доски. Тестирование может быть выполнено следующим образом:

if(position & 0x88)
{
/* move is illegal */
}
else
{
/* move is legal */
}

... или...

if(0 == (position & 0x88))
{
/* move is legal */
}

"position" (индекс) должен быть байтом без знака (uint8_t в C). Таким образом, вам никогда не придется беспокоиться о том, чтобы указывать за пределами буфера.

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

Лично я считаю, что одномерный массив вообще может быть лучшим способом сделать это. Я рекомендую познакомиться (хорошо знакомы) с AND, OR, XOR, бит-сдвигом и вращением.

Дополнительную информацию см. В разделе " Бит Twiddling Hacks".

ответил(а) 2021-01-25T18:08:39+03:00 4 месяца, 2 недели назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема