За последние 24 часа нас посетили 17724 программиста и 1714 роботов. Сейчас ищут 924 программиста ...

Транспортная задача. Метод минимального элемента.

Тема в разделе "PHP для новичков", создана пользователем mattxs, 20 апр 2015.

  1. mattxs

    mattxs Новичок

    С нами с:
    8 мар 2015
    Сообщения:
    13
    Симпатии:
    0
    Добрый вечер, дорогие форумчане!
    Программирую алгоритм транспортной задачи методом минимального элемента, чтобы получить первый опорный план и перейти к методу потенциалов.
    Столкнулся с проблемой:
    Никак не могу понять, почему не ищутся правильно индексы минимального элемента. Они (инлдексы0 вначале находят правильный элемент, а потом идут на (0;0), и всегда (0;0), хотя функция по их поиску работает правильно.
    Вот код:
    Код (PHP):
    1. // Матрица тарифов
    2. $c = array(
    3.         array(2,5,2),
    4.         array(4,1,5),
    5.         array(3,6,8)
    6.         );
    7.  
    8. // Запасы груза
    9. $m = array(90,400,110);
    10.  
    11. // Потребности в грузе
    12. $n = array(140,300,160);
    13.  
    14.  
    15. function MinTarif($c,$m, $n)
    16.     {
    17.         
    18.         $X = array();
    19.           
    20.       
    21.        
    22.         for ($i=0; $i < count($m); $i++) { 
    23.             for ($j=0; $j < count($n); $j++) { 
    24.                 $X[$i][$j]=-1;
    25.             }
    26.         }
    27.         
    28.         $k=0;
    29.         $t=0; 
    30.                 
    31.      
    32.                     for ($i=0; $i < count($c); $i++)
    33.                         {
    34.                             for ($j=0; $j < count($c[0]); $j++)
    35.                                 {
    36.  
    37.                                     $k = getIndexi($c);
    38.                                     $t = getIndexj($c);
    39.  
    40.                                         $minPost = min($m[$k],$n[$t]); // Минимальная поставка в строке или столбце
    41.     
    42.                                         $X[$k][$t] = $minPost; // Загружаем поставку
    43.                                                             
    44.  
    45.                                            if($minPost == $m[$k]) { $n[$t] -= $m[$k]; $m[$k]=0; $c = ZamenaStroki($c,$k); }
    46.  
    47.                                         if($minPost == $n[$t]) { $m[$k] -= $n[$t]; $n[$t]=0; $c = ZamenaStolbca($c,$t); }
    48.                               }
    49.                   
    50.                         }
    51.  
    52.         return $X;
    53.  
    54.     }
    В функции я передаю матрицу тарифов $c, массив с запасами груза $n, и массив с потребностями $n.
    Результат, соответственно, я записываю в Матрицу поставок $X.
    Иду по матрице тарифов, ищу и запоминаю индексы минимального элемента с помощью функции getIndexi() и getIndexj() соответственно.
    Вот эти функции:
    Код (PHP):
    1. function getMin($arr)
    2.      {
    3.          static $res = array();
    4.  
    5.          foreach ($arr as $k => $v) {
    6.              if(is_array($v))
    7.                  getMin($v);
    8.              else{
    9.                  if($v != 0)
    10.                      $res[] = $v;
    11.              }
    12.          }
    13.          return !empty($res) ? min($res) : 0;
    14.      }
    15.        
    16.  
    17.     function getIndexi($ab)
    18.     {
    19.         $k=0;
    20.         for ($i=0; $i < count($ab); $i++) { 
    21.             for ($j=0; $j < count($ab[0]); $j++) { 
    22.                 if($ab[$i][$j] == getMin($ab))
    23.                     $k = $i;
    24.             }
    25.         }
    26.         return $k;
    27.     }
    28.  
    29.     function getIndexj($ab)
    30.     {
    31.         $t=0;
    32.         for ($i=0; $i < count($ab); $i++) { 
    33.             for ($j=0; $j < count($ab[0]); $j++) { 
    34.                 if($ab[$i][$j] == getMin($ab))
    35.                     $t = $j;
    36.             }
    37.         }
    38.         return $t;
    39.     }
    Функция getMin() возвращает минимальный элемент двумерного массива. Причем пропуская нули. То есть если в матрице есть нули, то функция работает как continue в цикле for. (P. S. я писал функцию поиска мин элемента в двумерном массиве сам, она пропускала нули, но если первый элемент матрицы был бы 0, она брала его за минимум, так как я стартовал с первого элемента, но не суть).

    Далее в самой MinTarif() я ищу минимальную поставку в массивах $m и $n, и записываю её в матрицу погрузок $X. После этого я проверяю, какая была поставка минимальна и, в соответствии с этим, зануляю элементы массива, а так же заменю нулями строку или столбец матрицы $c (в зависимости, чьи потребности были удовлетворены) функциями ZamenaStroki() и ZamenaStolbca() соответственно.
    Вот они:
    Код (PHP):
    1. function ZamenaStroki($c, $stroka)
    2.     {
    3.         for ($i=0; $i < count($c[0]); $i++) { 
    4.                 $c[$stroka][$i] = 0;
    5.         }
    6.         
    7.         return $c;
    8.     }
    9.  
    10.  
    11.     function ZamenaStolbca($c,$stolbec)
    12.     {
    13.         for ($i=0; $i < count($c); $i++) 
    14.         {
    15.             $c[$i][$stolbec] = 0;
    16.         }
    17.         return $c;
    18.     }
    Проблема вот в чем: первый элемент ищется нормально. Всё зануляется, всё работает. А дальше, индексы минимального элемента принимается (0;0), т.е. первый элемент. И всегда последующий становится первым элементом...
    Возможно проблема в цикле или ещё в чём-то. Если можете, подскажжите, пожалуйста. Заранее спасибо!
    ==============================================
    Кратко о методе минимального элемента в транспортной задаче:
    В матрице тарифов ищется минимальный элемент запоминаются его индексы $k, $t. Затем в матрицу погрузок вносится минимальный элемент из массивов запасов и потребностей min($m[$k],$n[$t]). В массиве $m[k] (или $n[$t]) этот минимальный элемент зануляется путём вычитания из него большего, а матрице тарифов вычёркивается строка (или столбец), считая, что потребности поставщика (или получателя) удовлетворены, т.е.$m[k]=0 (или $n[k]=0). И далее идёт до тех пор, пока не будут удовлетворены все потребности, т.е. массив $m и $n должны быть будут пустыми.
     
  2. mkramer

    mkramer Суперстар
    Команда форума Модератор

    С нами с:
    20 июн 2012
    Сообщения:
    8.600
    Симпатии:
    1.764
    А почему бы не научить getMin возвращать сразу индексы, зачем столько функций городить? getMin в нынешнем варианте мне совсем не нравится, ещё и рекурсивная на кой-то хрен... Чем плоха та, что мы в прошлом посту делали? Смотрите, как красиво её можно переделать под задачу:
    http://phpfiddle.org/lite/code/yx2w-idjr (Здесь можно убедиться, что работает)

    Код (PHP):
    1. <?php
    2. $ab = array(
    3.             array(5,0,0),
    4.             array(0,4,0),
    5.             array(0,2,0)
    6.         );
    7. function MinElem($arr)
    8.     {
    9.         $min = array ("value"=>$arr[0][0], "col"=>0, "row"=>0);
    10.         
    11.         
    12.         for ($i=0; $i < count($arr); $i++)
    13.             {
    14.                 for ($j=0; $j < count($arr[0]); $j++)
    15.                     {
    16.                         if($arr[$i][$j]==0) continue;
    17.  
    18.                         if($min["value"]>$arr[$i][$j] || $min["value"] == 0 && $arr[$i][$j] != 0) {
    19.                             $min["value"] = $arr[$i][$j];
    20.                             $min["col"]   = $j;
    21.                             $min["row"]   = $i;
    22.                         }
    23.                     }
    24.             }
    25.         return $min;
    26.     }
    27.  
    28. var_export(MinElem($ab));
    29. ?>
    Добавлено спустя 1 минуту 41 секунду:
    Правда, я по привычке написал первым столбец, а вторым строку, но думаю, вы поймёте. если $min = MinElem($ab), то минимальный элемент это $ab[$min["row"]][$min["col"]]

    Добавлено спустя 2 минуты 29 секунд:
    Вот это, к сожалению, для меня китайский язык, так что что дальше вы делаете неверно, не могу сказать.

    Добавлено спустя 7 минут 37 секунд:
    А что мы должны получить на выходе? И что значит вычёркивается строка или столбец? Типа, если $m[$k] < $n[$t], то строка, а если наоборот, то столбец? Вы, кстати, не вычёркиваете, а зануляете строку или столбец. Хотя может в вашем случае это одно и тоже.
     
  3. mkramer

    mkramer Суперстар
    Команда форума Модератор

    С нами с:
    20 июн 2012
    Сообщения:
    8.600
    Симпатии:
    1.764
    Реализовал ради интереса алгоритм. Ответ получился для ваших данных
    Код (Text):
    1. $X = array ( 0 => array ( 0 => 90, 1 => -1, 2 => -1, ), 1 => array ( 0 => -1, 1 => 300, 2 => 100, ), 2 => array ( 0 => 50, 1 => -1, 2 => 60, ), )
    Верно?
    Но кажется мне, что для некоторых исходных данных алгоритм может зациклится. Если интересно, реализацию кинуть могу
     
  4. mattxs

    mattxs Новичок

    С нами с:
    8 мар 2015
    Сообщения:
    13
    Симпатии:
    0
    Абсолютно верный ответ. Можете, пожалуйста, написать, как сделали?

    Добавлено спустя 19 минут 30 секунд:
    На выходе должна быть матрица загрузок $X. Строка или столбец вычёркивается (если решать на листочке от руки), когда удовлетворены потребности поставщиков или потребителей, т.е. ищем мин элемент в матрице тарифов, загружаем мин поставку min($m[$k], $n[$t]); в $X и если $m[$k] < $n[$t], тогда из $n[$t] вычитается $m[$k], $m[$k] становится равным нулю и значит, что удовлетворена потребность поставщика и вычеркивается (или зануляется) строка в матрице тарифов, соответствующая индексу $k, иначе если $m[$k] > $n[$t], то так же вычитается из большего меньшее вычёркивается столбец, соответствующий $t. и так до тех пор, пока массивы $m и $n не будут пустыми. Поставщики ($m) это строки, потребители ($n) это столбцы.
     
  5. mkramer

    mkramer Суперстар
    Команда форума Модератор

    С нами с:
    20 июн 2012
    Сообщения:
    8.600
    Симпатии:
    1.764
    Код (PHP):
    1. <?php
    2.  
    3. function MinElem($arr) {
    4.     $min = array ("value" => $arr[0][0], "col" => 0, "row" => 0);
    5.  
    6.  
    7.     for ($i = 0; $i < count($arr); $i++) {
    8.         for ($j = 0; $j < count($arr[0]); $j++) {
    9.             if ($arr[$i][$j] == 0)
    10.                 continue;
    11.  
    12.             if ($min["value"] > $arr[$i][$j] || $min["value"] == 0 && $arr[$i][$j] != 0) {
    13.                 $min["value"] = $arr[$i][$j];
    14.                 $min["col"]   = $j;
    15.                 $min["row"]   = $i;
    16.             }
    17.         }
    18.     }
    19.     return $min;
    20. }
    21.  
    22. function nullRow(&$a, $row) {
    23.     for ($i = 0; $i < count($a[$row]); $i++) $a[$row][$i] = 0;
    24. }
    25.  
    26. function nullCol(&$a, $col) {
    27.     for ($i = 0; $i < count($a); $i++) $a[$i][$col] = 0;
    28. }
    29.  
    30. function isNull($a) {
    31.     foreach ($a as $v) {
    32.         if ($v)
    33.             return false;
    34.     }
    35.     return true;
    36. }
    37.  
    38. // Матрица тарифов
    39. $c = array (
    40.     array (2, 5, 2),
    41.     array (4, 1, 5),
    42.     array (3, 6, 8)
    43. );
    44. // ПОставка
    45. $X = array (array (-1, -1, -1), array (-1, -1, -1), array (-1, -1, -1));
    46.  
    47. // Запасы груза
    48. $m = array (90, 400, 110);
    49.  
    50. // Потребности в грузе
    51. $n = array (140, 300, 160);
    52.  
    53. while (!isNull($m) || !isNull($n)) {
    54.     $min = MinElem($c);
    55.  
    56.     $k = $min["row"];
    57.     $t = $min["col"];
    58.  
    59.     $mm = min($m[$k], $n[$t]);
    60.  
    61.     $X[$k][$t] = $mm;
    62.  
    63.     if ($m[$k] == $mm) {
    64.         $n[$t] -= $m[$k];
    65.         $m[$k] = 0;
    66.         nullRow($c, $k);
    67.     }
    68.     else {
    69.         $m[$k] -= $n[$t];
    70.         $n[$t] = 0;
    71.         nullCol($c, $t);
    72.     }
    73. }
    74.  
    75. ?>
    Единственное, у вас и в алгоритме не учтён один момент - а если исходные данные таковы, что матрица $c обнулится раньше, чем массивы $m и $n, что тогда? Пока он зациклится. А так сам алгоритм сделан как вы описали. Может, конечно, можно это всё оптимизировать, но пока так.
     
  6. mattxs

    mattxs Новичок

    С нами с:
    8 мар 2015
    Сообщения:
    13
    Симпатии:
    0
    Спасибо, большое за помощь! Матрица $c не сможет обнулиться быстрее, потому что в ней должны присутствовать элементы больше нуля.Она в любом случае обнуляется только,когда обнуляется $m или $n. Спасибо большое ещё раз! Самая лёгкая часть программы сделана) Осталась самая сложная - метод потенциалов. Но без труда ничего не получится)
     
  7. mkramer

    mkramer Суперстар
    Команда форума Модератор

    С нами с:
    20 июн 2012
    Сообщения:
    8.600
    Симпатии:
    1.764
    Ну к примеру я пробовал ставить вместо 90 в $m 120, и произошёл как раз такой случай.
     
  8. mattxs

    mattxs Новичок

    С нами с:
    8 мар 2015
    Сообщения:
    13
    Симпатии:
    0
    Аааа, понял о чём вы. Нет, такого случая не будет. Так как если вместо 90 будет 120, то сумма элементов $m будет больше суммы $n, значит транспортная задача считается открытой и её нужно привести к закрытой форме. Для этого вводится фиктивный поставщик (или потребитель), т.е. добавляется в массив с меньшей суммой та разность, которая получается при вычитании из большего. И задача снова сводится к закрытой и снова будет всё работать))
    То есть сумма массивов всегда равна. И сумма элементов, которые получаются на выходе в матрице $X (кроме -1) тоже равна сумме массива $m или $n)
    Так что ваш алгоритм 100% рабочий! Спасибо еще раз за помощь! (ЛС).
     
  9. mkramer

    mkramer Суперстар
    Команда форума Модератор

    С нами с:
    20 июн 2012
    Сообщения:
    8.600
    Симпатии:
    1.764
    Алгоритм-то ваш, я дословно перевёл то, что вы написали, на php