Вычисление и удаление двух наименьших количеств в списке

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

cages=[[1,5,2,3],[2,1,1,5],[0,4,1,1]]

Итак, у меня есть список l, содержащий 4 переменные, которые содержат количество каждого животного, рассчитанное с помощью суммы:

    dogs=sum(i[0] for i in cages)
    hamsters=sum(i[1] for i in cages)
    cats=sum(i[2] for i in cages)
    birds=sum(i[3] for i in cages)

    l=[dogs,hamsters,cats,birds]

Таким образом, переменные равны: собаки =3, хомяки =10, кошки =4, птицы =9

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

smallest=[]
for animal in l:
  smallest.append(min(l))
  break

for animal in smallest:
  while len(l)>3:
    if animal==dogs:
        l=[hamsters,cats,birds]

    elif animal==hamsters:
        l=[dogs,cats,birds]

    elif animal==cats:
        l=[dogs,hamsters,birds]

    elif animal==birds:
        l=[dogs,hamsters,cats]

Затем я опорожняю самых маленьких, чтобы найти новое самое маленькое животное:

smallest.clear()

Затем я пытаюсь сделать то же самое для второго маленького животного:

smallest=[]
for animal in l:
    smallest.append(min(l))
    break

for animal in smallest:
    while len(l)>2:

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

if animal(deleted before)==dogs and animal(being deleted now)==cats:
  l=[hamsters,birds]

elif animal(deleted before)==dogs and animal(being deleted now)==hamsters:
  l=[cats,birds]

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

1 ответ

Решение

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

Тем не менее, есть также ряд вещей, которые должны быть исправлены в вашем коде. Я хотел бы подробно остановиться на этих проблемах, прежде чем показывать решение, так как ваша цель - лучше изучить Python. Начиная сверху:

  1. Цикл в начале бессмысленен и запутан:

    smallest=[]
    for animal in l:
       smallest.append(min(l))
       break
    

    Вы начинаете перебирать l, но никогда не используйте переменную animal в цикле: уже красный флаг. Потом добавляешь min(l) в список и немедленно выйти из цикла. Цикл повторяется только один раз и не имеет смысла. Ни один не делает список smallest,

    Вы можете переписать весь блок как smallest = min(l)что бы сделать smallest один int объект.

  2. Пункт № 1 означает, что буквально нет причин для следующего цикла (for animal in smallest:) или: smallest Это был список из одного элемента раньше, и теперь он указывает непосредственно на сам элемент.

  3. Вложенный цикл (while len(l)>3:) тоже лишнее. Когда вы точно знаете, что хотите выполнить операцию только один раз, она не должна быть в цикле. Эта одержимость петлями очень вредна для здоровья:)

  4. Удаление наименьшего элемента удаляет отображение от индекса к животному и наоборот. Например, вы начинаете с l[0] == dogs, l[1] == hamsters, Это означает, что если покупатель купит всех собак, вы получите индекс 0 теперь представляет хомяков вместо. Вам нужно найти решение, которое не имеет этой проблемы (показано ниже).

  5. Вам не нужен огромный набор elif Заявления, чтобы сделать выбор в первую очередь. Есть гораздо лучшие способы удаления правильного элемента. Концептуально проще всего найти индекс минимума и просто избавиться от элемента там.

    Вычисление индекса будет включать изменение smallest от удержания наименьшего значения l к индексу наименьшего значения. Это может быть сделано что-то вроде smallest_index = min(range(len(l)), key=l.__getitem__) ( отсюда). Это представление в основном говорит, найти наименьший индекс в списке (range(len(l))), но на основе значения этого индекса, а не самого индекса (key=l.__getitem__ означает, что минимум будет рассчитан на основе l[index] вместо просто index).

  6. Линия smallest.clear() не служит никакой цели вообще. Вы сбрасываете smallest незамедлительно после.

Теперь перейдем к актуальному решению. Главное, что нужно помнить, когда вы программируете: "Что я пытаюсь моделировать / решить здесь?". Когда покупатель покупает всех ваших собак, зоомагазин теряет способность продавать собак? Вероятно, нет, но на данный момент их нет в наличии. Способ моделирования, который заключается не в том, чтобы удалить элемент целиком, а в том, чтобы установить его на ноль, как вы делаете это в клетке. Это решит 90% ваших проблем. Оставшиеся 10% просто следуют вашей цели как можно более прямо, вместо того, чтобы ставить петли повсюду.

Теперь первая часть вашего кода будет выглядеть примерно так:

smallest_index = min(range(len(l)), key=l.__getitem__)
l[smallest_index] = 0

Вот и все. Минимальный элемент удален. Это не сразу решает проблему того, как найти второй наименьший элемент, так как теперь у вас есть ноль в вашем списке. Но что, если у вас никогда не было собак с самого начала? Там будет ноль в l[0] и мы не будем обращаться с этим правильно. Поэтому нам нужно написать новую версию ключа поиска, чтобы получить smallest_index который проверяет, является ли ключ ненулевым, убивая обеих птиц одним камнем:

def search_key(index):
    return (l[index] == 0), l[index]
indices = range(len(l))

smallest_index = min(indices, key=search_key)
l[smallest_index] = 0
second_smallest_index = min(indices, key=search_key)
l[second_smallest_index] = 0

Вот как это работает. Если ваш ключ поиска является кортежем, элементы ключа будут сравниваться по порядку. Я использую этот факт здесь, чтобы вернуть кортеж, который содержит True в первом элементе, если и только если элемент массива равен нулю. Сейчас True == 1 а также False == 0 в Python. Это означает, что нулевые элементы помещаются в конец порядка сортировки, и все ненулевые элементы рассматриваются первыми. Теперь, когда вы установите нулевое число первых наименьших животных, повторный поиск приведет к следующему наименьшему ненулевому числу. И, как вы можете видеть, установка элементов в ноль вместо сокращения списка означает, что индексы соответствуют типу животных. Вы можете не иметь собак в магазине, но l[0] всегда будет количество собак независимо.

Если вы проверите это на своем примере:

labels = ['dogs', 'hamsters', 'cats', 'birds']
cages = [[1, 5, 2, 3],
         [2, 1, 1, 5],
         [0, 4, 1, 1]]
indices = range(len(labels))
l = [sum(cage[index] for cage in cages) for index in indices]

def search_key(index):
    return (l[index] == 0), l[index]

for _ in range(2):
    smallest_index = min(indices, key=search_key)
    print('Customer bought {} {}'.format(l[smallest_index], labels[smallest_index]))
    l[smallest_index] = 0

Ты получаешь:

Customer bought 3 dogs
Customer bought 4 cats

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

Другие вопросы по тегам