Изменение списков в матричном алгоритме Python

Я работаю над некоторыми проблемами алгоритма, чтобы получить больше практики Python. У меня возникли проблемы с проблемой, которая требует изменения значений внутри матрицы Python (список списков).

# Challenge
# After they became famous, the CodeBots all decided to move to a new building and live together. The building is represented by a 
# rectangular matrix of rooms. Each cell in the matrix contains an integer that represents the price of the room. Some rooms are 
# free (their cost is 0), but that's probably because they are haunted, so all the bots are afraid of them. That is why any room 
# that is free or is located anywhere below a free room in the same column is not considered suitable for the bots to live in.
# ex: matrix = [[0, 1, 1, 2],      [[x, 1, 1, 2],
#               [0, 5, 0, 0],  -->  [x, 5, x, x],  --> 5 + 1 + 1 + 2 = 9
 #              [2, 0, 3, 3]]       [x, x, x, x]]

Мой подход состоит из двух частей: 1) сначала найдите все нули в матрице и замените это значение на «x». 2) Как только это произойдет, просмотрите все списки и найдите индекс существующего «x», затем используйте это значение индекса и найдите его в других списках.. замените числовое значение на «x», если это число «ниже ' существующий 'x'.. надеюсь, это имеет смысл. У меня есть первая часть, и я пробовал вторую часть разными способами, но теперь сталкиваюсь с ошибкой.. Я чувствую, что я очень близок. Я также чувствую, что мой код довольно неэффективен (я новичок в Python), поэтому, если есть более эффективный способ сделать это, дайте мне знать.

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

Мой код:

def matrixElementsSum(matrix):
    numList = len(matrix) # essentially the number of 'rows' -> number of lists
    numCol = len(matrix[0]) # number of values in each list

    # replace 0's in each list with 'x'
    for x in matrix:
        if x.count(0) > 0:
            for index, i in enumerate(x):
                if i == 0:
                    x[index] = 'x'

    for x in matrix:
        for y in matrix[x]:
            if(matrix[x][y] == 'x'):
                x_ind = y
                for z in matrix:
                    if(z < x):
                        matrix[z][x_ind] = 'x'
     print(matrix)

Тестовый сценарий:

matrixElementsSum([[0, 1, 1, 2], 
                   [0, 5, 0, 0], 
                   [2, 0, 3, 3]])

person cexcelc    schedule 10.01.2019    source источник
comment
Вы забыли сообщить людям, что такое сообщение об ошибке.   -  person Sheldore    schedule 11.01.2019
comment
Извините, сообщение об ошибке: index is out of range - я отредактирую основной пост.   -  person cexcelc    schedule 11.01.2019
comment
В чем смысл if x.count(0) > 0:? В любом случае, вы пытаетесь проиндексировать свою матрицу с помощью значений в матрице: for x in matrix: ... for y in matrix[x], x будет list, строкой в ​​вашей матрице   -  person juanpa.arrivillaga    schedule 11.01.2019


Ответы (1)


Вам по-прежнему потребуются вложенные циклы for тем или иным образом, поскольку вы выполняете итерацию по списку списков, но вы можете немного упростить логику, используя понимание списков.

def solver(matrix):
     mx = [[v if v else 'x' for v in row] for row in matrix]
     mxx = [[v1 if v2 else 'x' for v1, v2 in zip(row1, row2)] for row1, row2 in zip(mx[1:], matrix)]
     return mx[:1] + mxx

Сначала я перебираю матрицу и заменяю 0 на "x" в новой матрице mx.

mx = [[v if v else 'x' for v in row] for row in matrix]

Это просто понимание вложенного списка, где мы работаем с каждым элементом в строке, для каждой строки в каждой матрице. ... if ... else ... — это просто классический тернарный оператор. Если выполняется v (в нашем случае не ноль), то оно оценивается как значение до «если», в противном случае оно оценивается как значение после «иначе» — в данном случае 'x'.

Затем я повторяю процесс, но смещаю строки на единицу, чтобы проверить, является ли вышеприведенный элемент теперь "x".

mxx = [[v1 if v2 else 'x' for v1, v2 in zip(row1, row2)] for row1, row2 in zip(mx[1:], matrix)]

Тут есть что ломать. Начнем с «снаружи» и пройдем внутрь.

... for row1, row2 in zip(mx[1:], matrix)

Это упаковывает новую матрицу со смещением на единицу (с использованием обозначения среза [1:]) с исходной матрицей. Таким образом, он возвращает итерируемый объект, функционально эквивалентный следующему списку:

[(mx_row1, matrix_row0), (mx_row2, matrix_row1), (mx_row3, matrix_row2), ...]

Это позволяет нам одновременно извлекать заданную строку и строку над ней, как row1 и row2. Потом другая половина-

[v1 if v2 else 'x' for v1, v2 in zip(row1, row2)]

- повторяет аналогичный процесс для каждого элемента в строке, а не для строки в матрице. Мы не смещаем элементы ни в одной строке, как мы смещаем строки матрицы mx, но в остальном логика идентична. Затем мы снова сравниваем с нашим тернарным оператором, чтобы увидеть, является ли вышеуказанный элемент 0, и если да, то оцениваем его как 'x'. Мы могли бы легко изменить это, чтобы сравнивать каждый элемент каждой строки от mx до 'x' вместо matrix до 0, но я решил отразить первое понимание списка.

Когда у меня есть эта новая матрица mxx, я просто добавляю первую строку mx, потому что мы фактически пропускаем эту строку, когда смещаем наше сравнение. Результатом является матрица, в которой все 0s и элементы ниже заменены на "x"s.


Согласно пояснению в комментариях, если вы хотите пометить "x", если какой-либо из вышеперечисленных элементов имеет значение 0, а не только тот, который непосредственно выше, вы можете сделать это, взяв фрагмент этого столбца матрицу и используя встроенную функцию all(), чтобы увидеть, являются ли они 0. Пересмотренный код ниже

def solver(matrix):
    return [[v if all(col) else 'x' for v, col in zip(row, zip(*matrix[:idx]))] for idx, row in enumerate(matrix, 1)]
person Dillon Davis    schedule 10.01.2019
comment
Вау, это отлично работает! Я раньше не использовал zip, не могли бы вы объяснить, что он здесь делает? Я только кратко использовал понимание списка, я не знал, что вы можете сделать понимание списка внутри понимания списка. Однако понимание «внешнего» списка для mxx меня немного сбивает с толку. Кроме того, v if v else 'x' автоматически проверяет › 0 целых чисел? откуда он знает, что заменить на «x»? - person cexcelc; 11.01.2019
comment
Конечно, я отредактирую свой пост, чтобы разбить его построчно. - person Dillon Davis; 11.01.2019
comment
Вау, большое спасибо за объяснение. Мне придется возиться с кодом, чтобы я мог лучше понять визуально - person cexcelc; 11.01.2019
comment
как это можно изменить, чтобы включить сценарий, в котором все значения в «столбце» меняются на «x», если есть ноль «выше»? Например, следующий пример: [[1, 1, 1, 0], [0, 5, 0, 1], [2, 1, 3, 10]]. Ваше вышеприведенное решение изменяет третий индекс во втором списке на «x», но затем оставляет третий индекс в списке 3 равным 10. - person cexcelc; 11.01.2019
comment
Я думаю, как-то вы должны перебирать возможные смещения? mx[2:] в методе zip сравнивает 3-й индекс с 1-м. Если мы предположим, что максимальное количество списков = 5, можем ли мы как-то выполнить этот цикл? - person cexcelc; 11.01.2019
comment
@cexcelc Я обновил ответ абзацем внизу + изменил код для обработки этого случая. Первоначально я неправильно понял ваш вопрос как пометку "X"s для элементов непосредственно ниже 0s. - person Dillon Davis; 11.01.2019
comment
вау, еще раз спасибо. Мне нужно будет изучить ваше использование символа '*' и метода all(), чтобы лучше понять, что они делают. - person cexcelc; 11.01.2019
comment
@cexcelc * распаковывает итерируемый объект и передает каждый элемент в качестве аргумента по отдельности. Поэтому я передаю каждую строку в качестве аргумента для zip, эффективно транспонируя матрицу. И all() просто утверждает, если ни один из элементов не является ложным. его дополнение, any(), утверждает, если хотя бы одно из них не является ложным. - person Dillon Davis; 11.01.2019