1

I have a 2-dimensional array which the values...

9999999999999999  3201  4584  4821  1628  1218  1786  4738  4897
3122  9999999999999999  1400  1638  1797  2756  3323  5310  5472
4523  1400  9999999999999999  237 3198  4156  4723  6711  6872
4760  1638  237 9999999999999999  3435  4394  4961  6948  7110
1324  1846  3247  3485  9999999999999999  958 1525  3931  4093
932 2854  4273  4510  1002  9999999999999999  567 4873  5034
1499  3422  4840  5078  1569  567 9999999999999999  5440  5602
5061  5359  6760  6998  4019  4959  5526  9999999999999999  161
5233  5531  6931  7169  4190  5130  5697  171 9999999999999999

Here's the same array in code:

array:9 [
  0 => array:9 [
    0 => 9999999999999999
    1 => 3122
    2 => 4523
    3 => 4760
    4 => 1324
    5 => 932
    6 => 1499
    7 => 5061
    8 => 5233
  ]
  1 => array:9 [
    0 => 3201
    1 => 9999999999999999
    2 => 1400
    3 => 1638
    4 => 1846
    5 => 2854
    6 => 3422
    7 => 5359
    8 => 5531
  ]
  2 => array:9 [
    0 => 4584
    1 => 1400
    2 => 9999999999999999
    3 => 237
    4 => 3247
    5 => 4273
    6 => 4840
    7 => 6760
    8 => 6931
  ]
  3 => array:9 [
    0 => 4821
    1 => 1638
    2 => 237
    3 => 9999999999999999
    4 => 3485
    5 => 4510
    6 => 5078
    7 => 6998
    8 => 7169
  ]
  4 => array:9 [
    0 => 1628
    1 => 1797
    2 => 3198
    3 => 3435
    4 => 9999999999999999
    5 => 1002
    6 => 1569
    7 => 4019
    8 => 4190
  ]
  5 => array:9 [
    0 => 1218
    1 => 2756
    2 => 4156
    3 => 4394
    4 => 958
    5 => 9999999999999999
    6 => 567
    7 => 4959
    8 => 5130
  ]
  6 => array:9 [
    0 => 1786
    1 => 3323
    2 => 4723
    3 => 4961
    4 => 1525
    5 => 567
    6 => 9999999999999999
    7 => 5526
    8 => 5697
  ]
  7 => array:9 [
    0 => 4738
    1 => 5310
    2 => 6711
    3 => 6948
    4 => 3931
    5 => 4873
    6 => 5440
    7 => 9999999999999999
    8 => 171
  ]
  8 => array:9 [
    0 => 4897
    1 => 5472
    2 => 6872
    3 => 7110
    4 => 4093
    5 => 5034
    6 => 5602
    7 => 161
    8 => 9999999999999999
  ]
]

What I'm trying to achieve is find the sorting order of this array in terms of closest. Each row contains the distance of a place to another place.

In other words, the array looks something like:

        China    India    USA    Japan
China     0        50     4000    2000
India     50       0      4100    2100
USA      4050     4120      2     3000
Japan    2010     1950    2997     0

As you can see, the value to self is 0, sometimes it's a small number like 2-10 (not really sure thou) because Google distance matrix API sometimes returns big values for same origin and destination too which is why I replaced it to 9999999999999999 throughout the diagonal in my original array above.

The goal is to get the sorting in terms of distance. The first entry is the starting point so in the case of the hypothetical array above it would be: [0 1 3 2] i.e. From China to India, then India to Japan, and Japan to the USA. My goal is to use array_multisort, in the end, to order just the keys with the place names so that it's ordered properly.

The code I came up with isn't working as I hoped for:

$order = [0];
$i = 0;
$nextItemToProcessIndex = 0;
foreach($tempMatrix as $key => $entry) {
    $tempMatrix[$key][$key] = PHP_INT_MAX;
}
while(!empty($tempMatrix)) {
    $closestItemIndex = array_search(min($tempMatrix[$nextItemToProcessIndex]), $tempMatrix[$nextItemToProcessIndex]);
    array_push($order, $closestItemIndex);
    $this->pull_item($tempMatrix, $nextItemToProcessIndex);
    $nextItemToProcessIndex = $closestItemIndex - 1;
    $i++;
}
dd($order);
...
...
public function pull_item(&$array, $offset) {
    array_walk($array, function (&$v) use ($offset) {
        array_splice($v, $offset, 1);
    });
    $row = array_splice($array, $offset, 1);
    return $row;
}
Saifur Rahman Mohsin
  • 929
  • 1
  • 11
  • 37

2 Answers2

0

If altering matrix column labels is ok for you - you can do sorting like that:

$dist = [
    "China" => ['China' =>    0, 'India' =>   50,  'USA' => 4000, 'Japan' => 2000],
    "India" => ['China' =>   50, 'India' =>    0,  'USA' => 4100, 'Japan' => 2100],
    "USA  " => ['China' => 4050, 'India' => 4120,  'USA' =>    2, 'Japan' => 3000],
    "Japan" => ['China' => 2010, 'India' => 1950,  'USA' => 2997, 'Japan' =>    0]
];

// sort columns by distance
foreach ($dist as $key => $value) {
    asort($dist[$key]);
}

// display distances matrix
foreach ($dist as $row_c => $data) {
    $row1 = "\t";
    $row2 = $row_c . " : ";
    foreach ($data as $col_c => $col_dist) {
        $row1 .= $col_c . "\t";
        $row2 .= $col_dist . "\t";
    }
    echo "{$row1}\n{$row2}\n\n";
}

Outputs:

        China   India   Japan   USA 
China : 0         50    2000    4000

        India   China   Japan   USA 
India : 0         50    2100    4100

        USA     Japan   China   India   
USA   : 2        3000   4050    4120

        Japan   India   China   USA 
Japan : 0        1950   2010    2997
Agnius Vasiliauskas
  • 10,935
  • 5
  • 50
  • 70
  • Thanks for writing this but I'm not trying to sort a matrix to have an ascending distance value. Kindly read the description and you will see that I'm trying to find the best order to go from one place to another. – Saifur Rahman Mohsin Oct 08 '18 at 12:05
  • Your description is not very clear. If by saying "best order" - you had in mind shortest path between two countries (or smallest amount of countries in between `from` and `to` points) - then you need something like `A*` - a path finding algorithm (sorting will not help you). Otherwise - define term "best order" - the post as you made it is not helpful at all – Agnius Vasiliauskas Oct 08 '18 at 15:38
0

I was able to figure out a solution to this on my own. Posting here if anyone else comes across the same issue:

$nextItemToProcess = 0;
$order = [0];

foreach(range(0, count($tempMatrix) - 2) as $i) {
    if(count(array_diff_key($tempMatrix[$nextItemToProcess], $order)) == 1) {
      $order = array_merge($order, array_diff(range(0, count($tempMatrix)-1), $order));
      break;
    }

    $diffArray = array_diff_key($tempMatrix[$nextItemToProcess], array_flip($order));
    $closestItemIndex = array_search(min($diffArray), $tempMatrix[$nextItemToProcess]);
    array_push($order, $closestItemIndex);
    $nextItemToProcess = $closestItemIndex;
}

$ascOrigins = [];
foreach($order as $i) {
    array_push($ascOrigins, $arrOrigins[$i]);
}

ascOrigins is the array in ascending order of place distances. I thought I could use php's array_multisort but it wasn't useful since here I'm sorting the keys of arr based on the values of order. (I know an array_flip could have done the trick but this works fine).

Saifur Rahman Mohsin
  • 929
  • 1
  • 11
  • 37