PHP - Трудный математический расчет
Описание проекта
Проект подсчитывает, сколько пищи должно иметь лошадь, это основано на огромном числе переменных. Пользователь вводит информацию о каждой лошади и предпочтительных кормах. Каждый корм имеет несколько видов витаминов и питательных веществ.
1 лошадь использует несколько типов фидов.
Что у нас
У нас есть минимальные и максимальные значения для каждого питательного вещества, которое требуется конкретной лошади.
У нас есть контент для одного или нескольких разных типов кормов, это может быть около 20-30 различных питательных веществ и витаминов на корм. У нас также есть цена и мы хотим использовать это, чтобы сэкономить деньги.
(Расчет только для одной лошади в то время)
Пример
Мы используем A, B и C для представления питательных веществ.
Лошадь: (A, B, C) MIN (30,7,9) MAX (35,9,17)
Feed 1 содержит: (A, B, C) ЗНАЧЕНИЯ (16,2,3)
Подача 2 содержит: (A, B, C) ЗНАЧЕНИЯ (0,4,9)
Рабочим решением будет 2 * Feed1 и 1 * Feed2.
Проблема
Я хочу, чтобы система вычислила идеальный баланс на основе минимальных/максимальных значений для каждого питательного вещества и по-прежнему удерживала цену как можно ниже.
Рабочее решение
Если я сначала вычислил максимально возможную сумму для каждого фида, можно будет рандомизировать, пока это не сработает. И тогда пользователь сможет изменить количество, если оно не идеально.
<?php
function randomizerLoop(){
foreach($feeds as $feed){
$max['A'] = floor($horse_max['A']/$feed['A']);
$max['B'] = floor($horse_max['B']/$feed['B']);
$max['C'] = floor($horse_max['C']/$feed['C']);
$maxRand = MIN($max['A'], $max['B'], $max['C']);
$amounts[$feed['id']] = rand(0, $maxRand);
}
return $amounts;
}
?>
Этот код будет продолжать пытаться, пока он не получит рабочий баланс, вместо того, чтобы использовать некоторый классный расчет, чтобы найти баланс с первой попытки.
Мне просто нужно понять, как его решить без rand()
.
Дополнительная информация
Каждый пользователь сможет добавить бесконечное количество лошадей, но сможет рассчитывать только на одну лошадь в данный момент (на данный момент).
Также можно будет добавить бесконечное количество фидов (определенных пользователем), и каждый фид может иметь 20-30 переменных. В зависимости от решения это, вероятно, потребует предела подачи для каждого автоматического расчета.
Было бы много комбинаций, если мы используем 20 разных каналов, 20-30 переменных для каждого фида, а также когда мы определяем сумму в int вместо просто логических.
Ответы
Ответ 1
Что вы хотите сделать, это попробовать каждую комбинацию каждого фида. Предположим, что существует 5 типов фидов. Вы уже знаете, как вычислить максимум каждого типа подачи. Итак, я предполагаю, что у вас есть что-то вроде этого:
$feeds_cost = array of costs for each feed
$feeds_ingredient_A = array of how much ingredient A each feed has
$feeds_ingredient_B = array of how much ingredient B each feed has
$feeds_ingredient_C = array of how much ingredient C each feed has
$feeds_max = array of maximum quantity of each feed allowed
Теперь, для 5 типов, вы хотите попробовать количество {0,0,0,0,0}, {0,0,0,0,1}, {0,0,0,0,2}... {$ feeds_max [0], $feeds_max [1], $feeds_max [2], $feeds_max [3], $feeds_max [4]}. Для каждого набора количеств вам необходимо: 1. Обеспечить, чтобы количество соответствовало минимальному требованию. 2. Если он соответствует минимальному требованию, рассчитать стоимость.
Итак, на данный момент у вас есть стоимость для каждого количества, которое соответствует минимальным требованиям, но не превосходит максимальное требование. Вам не нужно хранить все из них. Сохраняйте две переменные: $best_quantities (массив отсчетов каждого фида) и $best_cost. Если количество имеет более дешевую стоимость при выполнении требований, вы заменяете $best_quantities и $best_cost.
Все тривиально, за исключением того, что они проходят через все величины. Это не очень сложно. Вы поддерживаете индекс, который вы увеличиваете. Первоначально он равен нулю, но он поднимается до самого высокого индекса подачи (4, если у вас есть 5 каналов). Функция приращения:
function increment_counts($feeds_max, $quantities, $index)
{
if($index>sizeof($quantities)) return null;
$quantities[$index]++;
if($quantities[$index] > $feeds_max[$index])
{
$quantities[$index]=0;
return increment_counts($feeds_max, $quantities, $index+1);
}
return $quantities;
}
Предполагая, что я правильно напечатал это с верхней части головы, он будет подсчитывать количество. Вы продолжаете называть его, увеличивая индекс 0 и заменяя свое текущее количество на возвращаемое значение. Когда он вернет null, вы закончите.
Я уверен, что сделал хотя бы один надзор, но именно так я бы справился с этой проблемой.
Ответ 2
Это проблема linear programming.
MIN MAX
A 30 35
B 7 9
C 9 17
A B C
Feed1 16 2 3
Feed2 0 4 9
Пусть еда содержит х количество кормов1 и у число feed2. По вашему вопросу:
30<16*x+0*y<35
=> 30<16x<35
используйте ceil()
, чтобы разделить 30 на 16, что даст u x (что равно 2). После этого у вас есть 3 < 4y < 5, аналогично используйте ceil()
, и вы получите y = 1.
Теперь, думая о вас,
Для большого количества каналов будет сложно вычислить так много уравнений.
Мы должны использовать матрицы для упрощения. Мартикс для приведенных выше уравнений будет: -
A * B = C
|16 0| | x | | 30 |
|2 4| | y | = | 7 |
|3 9| | 9 |
Теперь используйте Lapack:: pseudoInverse(), чтобы вычислить обратную матрицу A и умножить ее на C.
Затем просто сравните значение x и y в матрице B с ответом и используйте ceil().
Ответ 3
Я бы сосредоточил внимание на значениях MIN, чтобы минимизировать затраты. В любом случае, вся точка предлагаемого подхода соответствует определенным целям, которые могут быть как можно более переменными (например, принудительные питательные вещества E и G, чтобы получить, по крайней мере, половинное значение между MIN и MAX).
Основная структура образована тремя петлями: основной, проходящей через все питательные вещества; и два внутренних, которые пытаются использовать все возможные комбинации среди каналов.
NUTRIENT A
Trying feed1 until reaching minimum (because feed2 is zero).
Best so far: 2*feed1=32A.
NUTRIENT B
Starting from 2*feed1=4B, +feed2 reaches minimum.
Best so far: 2*feed1+feed2=8B.
NUTRIENT C
Starting from 2*feed1+feed2=15C which is fine already.
Best so far: 2*feed1+feed2=15C.
Более продвинутая версия этого подхода вернется к проверке (например, в тех случаях, когда цель достигается с первого момента, например, что происходит с питательным веществом C), возможна ли лучшая альтернатива. В любом случае сложность такой реализации и количества комбинаций будет заметно выше.
Я думаю, что это довольно гибкий и точный подход. Реализация алгоритма для базовой версии не слишком сложна (но не слишком проста, поэтому я не написал ее здесь) и должен обеспечивать достаточно хорошую точность и скорость. После тестирования и получения информации о его производительности вы можете начать думать о дальнейшем его улучшении (например, путем повторного анализа определенного случая или учета целей, отличных от MIN).