|
Pers.narod.ru. PHP. Статьи. LDE (Linear Diofant Equation) - сервис для решения линейных диофантовых уравнений |
Сколько есть способов набрать определённую денежную сумму монетами нескольких заданных номиналов? Можно ли придумать другие месяцы для года из 365 дней? Как распределить 20 человек по 3 участкам работ и сколько есть способов это сделать?
На эти и множество других подобных вопросов можно ответить, решив линейное диофантово уравнение вида
a1x1+a2x2+...+anxn=k,
(1)
где ai - заданные коэффициенты, k - правая часть уравнения, та самая денежная сумма
или продолжительность года, а xi - неизвестные величины.
О диофантовых уравнениях на сайте уже упоминалось вот здесь, там же вы можете прочитать об их неразрешимости в общем виде.
Для решения уравнения (1) с ненулевыми положительными целыми коэффициентами ai и целым k>1
я сейчас написал небольшой сервис, так как нужно было посмотреть "в работе" подобную задачу.
Целочисленные положительные коэффициенты ai вводятся в форму через пробел или запятую.
Нули или значения ai>k исключаются из данных, коэффициенты ai автоматически сортируются по возрастанию.
Очевидно, что решать с ai=0 или ai>k было бы бессмысленно, это означало бы лишь то, что в решение всегда входит 0 объектов какого-то вида.
Решение менее, чем с двумя различными коэффициентами ai также не выполняется, ведь с одним коэффициентом решение всегда тривиально.
С коэффициентами ai<0 решение теоретически возможно, но здесь отключено, так как алгоритм основан на предположении, что все числа положительны. Да и задач таких большинство.
Скрипт автоматически сортирует коэффициенты ai по возрастанию (от перестановки мест слагаемых сумма не меняется, а так удобнее).
Значение k также должно быть целым и k>1, иначе решать нечего. При необходимости k корректируется в заданных скриптом пределах от 2 до 99999. Ограничение сверху связано с желанием, чтобы
время на перебор не оказалось слишком большим. Да, метод решения -
перебор, вот весь алгоритм для корректных данных (PHP):
//Число коэффицентов равно n, такова же размерность массивов $a, $b, $c
//Массив $b заполнен по правилу:
for ($i=0; $i<$n; $i++) $b[$i]=(int)($k/$a[$i]);
//Массив $c заполнен нулями
do { //Начало цикла перебора
$s=0;
for ($i=0; $i<$n; $i++) $s+=$a[$i]*$c[$i];
if ($s==$k) { //Вывод очередного решения c[i]
}
$found = false;
for ($i=$n-1; $i>-1; $i--) {
if ($c[$i]==$b[$i]) for ($j=$i; $j<$n; $j++) $c[$j]=0;
else { $c[$i]++; break; }
}
if ($i<0) $found = true;
} while (!$found); //Конец цикла перебора
Исходник сервиса, всё в одном файле (2 Кб)
Вот примеры использования сервиса для "глубоко практических" задач :)
Возможные комбинации месяцев из 28-31 дней для года из 365 дней (полные результаты)
Способы набрать рубль монетами в 1, 5, 10 и 50 копеек (результаты без нулей)
Способы разбить 20 человек на двойки и тройки людей (только ответы x[i])
Способы 3 человекам разойтись по 3 пивбарам :)
Как видно из URL-адресов этих примеров, обращаться к сервису можно и через командную строку (методом GET), в этом случае параметры таковы:
a - список коэффициентов через запятую;
k - значение из правой части уравнения;
r=1 - чтобы сразу решилось при вызове, иначе параметра r не надо
v=1 или v=2 или v=3 - способ вывода, то есть, решения целиком, решения без нулей или только ответы x[i]|
|