#python #list
#python #Список
Вопрос:
Я создаю шахматный движок на python, поэтому мне нужно оптимизировать все, чтобы работать как можно быстрее. Когда черные и белые стороны зеркально отражены, мне нужно отобразить список оценок на квадратах.
Это упрощенный список:
A = [1,2,3,4,5,6,7,8,9]
Давайте представим, что этот список представляет собой матрицу:
A = [
1,2,3,
4,5,6,
7,8,9]
Каков самый быстрый способ изменить «строки» в этом списке, чтобы конечный результат был:
A = [
7,8,9,
4,5,6,
1,2,3]
или написано в той же строке:
A = [7,8,9,4,5,6,1,2,3]
Реальные списки содержат 64 элемента, и мне нужно изменить 8 «строк» в них.
Это переворачивание выполняется миллионы раз, поэтому каждое улучшение приветствуется.
Я ищу самый быстрый способ сделать это. Спасибо!
Редактировать:
Я сделал это таким образом, есть ли более быстрый способ сделать это?
A = [1,2,3,4,5,6,7,8,9]
A_size = len(A)
row_length = 3
for x in range(0,A_size,row_length):
A.extend(A[A_size-row_length-x:A_size-x])
del A[0:A_size]
print(A)
Комментарии:
1. Вы уже придумали какой-нибудь способ?
2. Numpy оптимизировал функции для различных манипуляций с матрицей. Я бы подумал, что вам стоило бы подумать о том, чтобы использовать это
3. 1d массив не является матрицей
4. Я знаю, что список не является матрицей, я никогда этого не говорил
5. почему вы не используете двумерные массивы?
Ответ №1:
Изучаем некоторые варианты:
numpy
должен быть в состоянии сделать это эффективно, если вы используете reshape со своим списком:
import numpy
A = [1,2,3,4,5,6,7,8,9]
m = numpy.array(A).reshape(3,3)
print(numpy.flip(m, axis=0))
Удобный метод flipud
делает то же самое, и, как упоминалось там, вы используете индексацию для достижения того же:
print(numpy.array(A).reshape(3,3)[::-1,...])
Варианты numpy оказываются на удивление неэффективными, возможно, из-за накладных расходов на создание матрицы. Итак, альтернативой вашему циклу extend / del на чистом Python может быть создание нового списка из выражения генератора:
print([e for r in (A[A_size-row_length-x:A_size-x]
for x in range(0,A_size,row_length)) for e in r])
В другом варианте используется замена фрагмента Python «на месте», и, похоже, это самый быстрый из моих вариантов, перечисленных до сих пор:
for x in range(0,A_size//2,row_length):
A[x:x row_length], A[A_size-row_length-x:A_size-x] = A[A_size-row_length-x:A_size-x], A[x:x row_length]
Однако в моих локальных тестах этот последний вариант по-прежнему на ~ 10% медленнее, чем ваш исходный вариант extend / del, и, что еще более интересно, это соотношение кажется стабильным, т. Е. Ваш исходный вариант по-прежнему самый быстрый из них, при увеличении размера списка до 50 * 50.
В качестве побочного узла: если это вариант, я вижу довольно резкое ускорение с помощью PyPy. Я опробовал полную замену по индексу на месте:
A[0],A[1],A[2],A[3],A[4],A[5],A[6],A[7],A[8],A[9],A[10],A[11],A[12],A[13],A[14],A[15],A[16],A[17],A[18],A[19],A[20],A[21],A[22],A[23],A[24],A[25],A[26],A[27],A[28],A[29],A[30],A[31],A[32],A[33],A[34],A[35],A[36],A[37],A[38],A[39],A[40],A[41],A[42],A[43],A[44],A[45],A[46],A[47],A[48],A[49],A[50],A[51],A[52],A[53],A[54],A[55],A[56],A[57],A[58],A[59],A[60],A[61],A[62],A[63] = A[56],A[57],A[58],A[59],A[60],A[61],A[62],A[63],A[48],A[49],A[50],A[51],A[52],A[53],A[54],A[55],A[40],A[41],A[42],A[43],A[44],A[45],A[46],A[47],A[32],A[33],A[34],A[35],A[36],A[37],A[38],A[39],A[24],A[25],A[26],A[27],A[28],A[29],A[30],A[31],A[16],A[17],A[18],A[19],A[20],A[21],A[22],A[23],A[8],A[9],A[10],A[11],A[12],A[13],A[14],A[15],A[0],A[1],A[2],A[3],A[4],A[5],A[6],A[7]
Хотя это немного медленнее, чем вариант замены фрагмента выше со стандартным Python3, он превосходит исходный цикл extend / del примерно в 2,6 раза с помощью PyPy.
Комментарии:
1. спасибо за ваш ответ, на 1 миллион итераций это занимает 3,94 секунды на моем ПК, моя версия заняла 1,11 секунды
2. вторая версия занимает 1,44 секунды
3. Интересные измерения, спасибо за отзыв.
4. спасибо за ваше время, ваш третий код занимает 1,23 секунды
5. Спасибо за эту увлекательную кроличью нору. Никогда бы не подумал, что простое расширение / del будет таким производительным.
Ответ №2:
Если вы хотите преобразовать список и преобразовать его в матрицу, вы можете использовать следующий фрагмент.
def chunk(lst, n):
for i in range(0, len(lst), n):
yield lst[i:i n]
a = [1, 2, 3, 4, 5, 6, 7, 8, 9]
b = list(chunk(a, 3))
b.reverse()
b = sum(b, [])
print(b)
# [7, 8, 9, 4, 5, 6, 1, 2, 3]
При использовании двумерных массивов в качестве отправной точки эта задача намного проще:
a = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
a.reverse()
print(a)
# [[7, 8, 9], [4, 5, 6], [1, 2, 3]]
Комментарии:
1. спасибо, для 1 миллиона итераций ваша версия занимает 1,21 секунды, моя — 1,11
Ответ №3:
Попробуйте это, которое использует генератор для перемещения назад от конца списка к началу:
from itertools import chain
A = [1,2,3,4,5,6,7,8,9]
def chunk(lst, step):
start = len(lst)
for i in range(start, -1, -step):
yield lst[i-step:i]
yield lst[0:i] #any remaning bit
l = list(chain.from_iterable(chunk(A, 3)))
print(l)
Вывод:
[7, 8, 9, 4, 5, 6, 1, 2, 3]
Комментарии:
1. спасибо за ваш ответ, для 1 миллиона итераций ваша версия занимает 1,32 секунды, моя — 1,11
2. @BillieJoe Это интересно! Я немного обновил приведенный выше код, так как начало и конец не нужно передавать.
3. спасибо за ваше время, но это все еще медленнее, теперь у меня есть 1,36 секунды для последнего решения