Решение уравнения с целочисленным делением без перебора
Недавно на Тостере прозвучал вопрос, который не на шутку меня задел. Корнями он уходит в задачу, которую я приведу здесь в немного другой интерпретации:
Сдали как-то программисты проект в срок и получили премии. На радостях, прямо в ПН скинулись и на все деньги купили пива. В тот же день всё выпили, а во ВТ решили продолжить, но денег больше не осталось. Тогда они сдали бутылки, добавили вчерашнюю сдачу и снова затарились на все, как и вчера. То же проделали в СР и ЧТ. А в ПТ денег оказалось ровно на одну бутылку. Задумались — вспомнили цену одной бутылки, почём у них принимали тару (цены были без копеек), а сколько было денег изначально, никто назвать не смог. Проект был масштабный, премии большие — так что перебором не стоит. Каким же был минимальный бюджет в ПН, чтобы события сложились именно таким образом?
Рассуждая над ней следующим образом
я пришёл к уравнению, которое в абстрактном виде выгядит так (1):
Пытаясь найти подход без перебора к решению такого рода уравнений я потратил не один час, но в итоге нашёл поистине чудесное решение, но поля книги слишком узки ;)
Без иллюзий на счёт первенства в этом вопросе, хочу лишь поделиться удовольствием, полученным в процессе. Если кто знает альтернативный метод или название этого, просветите меня; подобных мне призываю к обсуждению, а нетерпеливых — приглашаю под кат.
Рассмотрим полученное уравнение в таком виде (2):
поскольку правая часть может принимать только целочисленные значения, всё выражение имеет смысл лишь тогда, когда в левой части числитель кратен знаменателю. Из этого очевидно, что x мог бы иметь значения начиная с величины с и дальше с шагом a.
Тогда я рассмотрел уравнение (1) в виде двух функций и . При каком аргументе они пересекутся, таким и будет решение. Для примера я выбрал небольшие значения параметров таким образом, чтобы отобразить картину, как можно лучше. Итак, пусть a = 3, b = 7, c = 9.
В силу ступенчатого характера второй функции графики y1 и y2 пересекаются в двух точках: при x1 = 12 и x2 = 15, но согласно условию, нас интересует именно первое значение, как меньшее. Так вот, для того, чтобы без перебора определить область пересечения, я ввёл третью функцию — это просто прямая, которая ограничивает функцию y2 снизу и имеет уравнение .
Теперь остаётся найти точку пересечения двух прямых (y1 и y3) и скорректировать ответ из ограничения на искомый x. Ведь исходя из условия, он может принимать только те значения, при которых соблюдается условие кратности числителя знаменателю в уравнении (2), т.е. , где n — некий натуральный множитель. Для этого решим простое уравнение и, если полученный корень не будет соответствовать нашим требованиям, сместим его до ближайшего подходящего. Поскольку вспомогательная функция y3 имеет положительный наклон, а все значения y2 находятся выше неё, то корректировку корня следует производить всегда в большую сторону. Так, в нашем случае, прямые пересекаются при x = 11.25 (чёрная точка на графике), и ближайшем большим значением, удовлетворяющим условие, будет 12 (красная точка), что и является ответом.
Поскольку в вопросе на Тостере стоял тег Python, ниже я приведу скрипт для решения этой задачи с использованием функции, для нахождения бюджета текущего дня на основании бюджета дня следующего. Применяем функцию нужное количество раз и, вуаля, получаем ответ!
def this_day_budget(next_day_budget):
a = bottle_price - tare_return_price
b = bottle_price
c = next_day_budget
x = (a - a*b + b*c)/(b - a)
if (x - c) % a: # value does not match the increment
x = ((x-c)//a + 1) * a + c
return x
bottle_price = 7
tare_return_price = 4
party_duration_days = 5
last_day_budget = bottle_price
for days_to_party_end in range(party_duration_days):
if days_to_party_end == 0:
current_budget = last_day_budget
else:
current_budget = this_day_budget(current_budget)
print('first day budget was - %d' % current_budget)
Вместо заключения:
задачей в данном примере были обусловлены как значения параметров , так и само уравнение ; предлагаемый подход с небольшиими изменеиями применим и в других подобных случаях — целью публикации было лишь описание самого принципа без выведения универсального решения для общего случая — поэтому не судите строго (код на Python в т.ч.), и приятной всем пятницы!