Вычисление и удаление двух наименьших количеств в списке
У меня есть программа для зоомагазина. У меня есть вложенный список, где каждый столбец соответствует животному, а числа в столбцах показывают номер этого животного в каждой клетке:
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. Начиная сверху:
Цикл в начале бессмысленен и запутан:
smallest=[] for animal in l: smallest.append(min(l)) break
Вы начинаете перебирать
l
, но никогда не используйте переменнуюanimal
в цикле: уже красный флаг. Потом добавляешьmin(l)
в список и немедленно выйти из цикла. Цикл повторяется только один раз и не имеет смысла. Ни один не делает списокsmallest
,Вы можете переписать весь блок как
smallest = min(l)
что бы сделатьsmallest
одинint
объект.Пункт № 1 означает, что буквально нет причин для следующего цикла (
for animal in smallest:
) или:smallest
Это был список из одного элемента раньше, и теперь он указывает непосредственно на сам элемент.Вложенный цикл (
while len(l)>3:
) тоже лишнее. Когда вы точно знаете, что хотите выполнить операцию только один раз, она не должна быть в цикле. Эта одержимость петлями очень вредна для здоровья:)Удаление наименьшего элемента удаляет отображение от индекса к животному и наоборот. Например, вы начинаете с
l[0] == dogs
,l[1] == hamsters
, Это означает, что если покупатель купит всех собак, вы получите индекс0
теперь представляет хомяков вместо. Вам нужно найти решение, которое не имеет этой проблемы (показано ниже).Вам не нужен огромный набор
elif
Заявления, чтобы сделать выбор в первую очередь. Есть гораздо лучшие способы удаления правильного элемента. Концептуально проще всего найти индекс минимума и просто избавиться от элемента там.Вычисление индекса будет включать изменение
smallest
от удержания наименьшего значенияl
к индексу наименьшего значения. Это может быть сделано что-то вродеsmallest_index = min(range(len(l)), key=l.__getitem__)
( отсюда). Это представление в основном говорит, найти наименьший индекс в списке (range(len(l))
), но на основе значения этого индекса, а не самого индекса (key=l.__getitem__
означает, что минимум будет рассчитан на основеl[index]
вместо простоindex
).Линия
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
Обратите внимание, что я поместил функциональный код в цикл здесь. Цикл здесь имеет смысл: покупатель покупает животное с наименьшим ненулевым счетом два раза. Оба раза это одна и та же операция, и клиент может сделать это более двух раз, что делает что-либо, кроме петли, громоздким, чтобы иметь дело с ним.