@webymax

Как задать метод перебора всех возможных сочетаний элементов?

Есть 10 цилиндров одинакового радиуса, но разной высоты. Как, составляя цилиндры один на другой, распределить их по трём группам, чтобы общая высота групп (т.е. по самой высокой группе) была минимально возможной?
Цилиндры и их высоты занесены в массив array(1 => 100, 2 => 150, 3 => 160 ...).
Не знаю как подступиться к перебору массива для решения задачи...
  • Вопрос задан
  • 594 просмотра
Решения вопроса 1
@BorisKorobkov Куратор тега PHP
Web developer
Про ИМВВ написано в соседнем ответе.
Как перебирать: в тупом варианте - все возможные комбинации, для 10 объектов это реально.
В оптимизированном варианте - отсортировать по высоте и сначала ставить самые высокие.
Ответ написан
Пригласить эксперта
Ответы на вопрос 2
@Vitsliputsli
Возьмите суммарную высоту всех цилиндров, поделите на 3 - это будет идеальная минимально возможная высота (ИМВВ).
Далее просто перебирайте все варианты по принципу, кидаем в первую группу пока не превысит ИМВВ, потом во вторую пока не превысит ИМВВ. И так для всех возможных комбинаций, соответственно фиксируя высоту самой высокой группы.
Ответ написан
@webymax Автор вопроса
Не знал, как подступиться к проблеме, но благодаря вашим советам решил задачу, по крайней мере, полным перебором :) Спасибо!
Правда, явно требуется оптимизация, т.к. если цилиндров 9, уже начинает тормозить. Но как вклинить хотя бы один из советов Boris Korobkov...

// факториал числа $n - кол-во перестановок элементов массива
function factorial($n) {
    if ($n <= 1) 
        return 1; 
    return $n * factorial($n - 1);
}

// возвращает перестановку элементов в массиве по ее порядковому номеру (код Лехмера)
function permutation($arr, $arr_keys, $arr_size, $index) {
    for ($i = 0; $i < $arr_size; $i ++) {
        $item = $index % ($arr_size - $i);
        $index = floor($index / ($arr_size - $i));
        $permutation[$arr_keys[$item]] = $arr[$item];
        array_splice($arr, $item, 1);
        array_splice($arr_keys, $item, 1);
    }
    return $permutation;
}

// распределение элементов по группам
function divide_into_groups($num_groups, $arr, $arr_size, $ideal_height) {
    $arr_keys = array_keys($arr); // для сохранения ключей
    $arr = array_combine(range(0, $arr_size - 1), $arr); // переводим ключи в числа по порядку для оптимизации цикла ниже: "for ($j = $start..."
    
    $groups = []; // многомерный массив распределения элементов по группам
    $heights = []; // высоты групп
    $start = 0;
    for ($i = 0; $i < $num_groups; $i ++) { // перебираем группы
        for ($j = $start; $j < $arr_size; $j ++) { // перебираем элементы
            $start = $j + 1; // последний перебранный элемент
            $groups[$i][$arr_keys[$j]] = $arr[$j]; // дополняем многомерный массив
            $heights[$i] = !isset($heights[$i]) ? $arr[$j] : $heights[$i] + $arr[$j]; // увеличиваем высоту группы
            if ($heights[$i] >= $ideal_height) // достигли ИМВВ, идём в следующую группу
                break;
        }
    }
    
    return [$groups, max($heights)];
}



$num_groups   = 3; // кол-во групп
$arr          = array(100, 110, 150, 160, 180, 185, 200, 205, 300); // высоты (просто из головы)
$arr_keys     = array_keys($arr); // вспомогательный массив для сохранения ключей
$arr_size     = count($arr); // длина массива
$num_variants = factorial($arr_size); // количество перестановок элементов массива
$ideal_height = max(array_sum($arr) / $num_groups, max($arr), min($arr)); // ИМВВ

$min_groups_height = array_sum($arr); // минимально возможная общая высота групп (начинаем просто с суммарной высоты всех элементов)
$optimal_variant = []; // оптимальный вариант распределения по группам

for ($i = 0; $i < $num_variants; $i ++) {
    // очередная перестановка элементов в массиве
    $permutation = permutation($arr, $arr_keys, $arr_size, $i);
    
    // распределение элементов по группам
    list($groups_arr, $groups_height) = divide_into_groups($num_groups, $permutation, $arr_size, $ideal_height);
    
    // если этот вариант лучше других, сохраняем его в качестве оптимального на текущий момент
    if ($min_groups_height > $groups_height) {
        $min_groups_height = $groups_height;
        $optimal_variant = $groups_arr;
    }
}

echo 'ИМВВ - '.$ideal_height.'<br /><br />';
echo 'Оптимальный вариант распределения элементов: <pre>'.print_r($optimal_variant, true).'</pre>';
echo 'Минимально возможная общая высота групп - '.$min_groups_height;
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы