1<?php 2// (c) Copyright by authors of the Tiki Wiki CMS Groupware Project 3// 4// All Rights Reserved. See copyright.txt for details and a complete list of authors. 5// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details. 6// $Id$ 7 8/** 9 * This class implements the Dijkstra algorithm for finding the shortest path 10 * through a graph. 11 * 12 * For details on this algorithm, see: 13 * http://en.wikipedia.org/wiki/Dijkstra%27s_algorithm 14 * 15 * For details on how to use the class, see the unit test file 16 * ShortestPathFinderTest.php. 17 */ 18 19 20class Multilingual_Aligner_ShortestPathFinder 21{ 22 var $visited = []; 23 var $distance = []; 24 var $previousNode = []; 25 var $startnode = null; 26 var $map = []; 27 var $infiniteDistance = 0; 28 var $numberOfNodes = 0; 29 var $bestPath = 0; 30 var $matrixWidth = 0; 31 var $shortestPathes = []; 32 var $nodes = []; 33 34 function __construct(&$ourMap, $infiniteDistance) 35 { 36 $this -> infiniteDistance = $infiniteDistance; 37 $this -> map = &$ourMap; 38 $this -> bestPath = 0; 39 40 $this->nodes = $this->_nodesInMmatrix($ourMap); 41 $this -> numberOfNodes = count($this->nodes); 42 } 43 44 function _nodesInMmatrix($distanceMatrix) 45 { 46 $nodes = []; 47 foreach (array_keys($distanceMatrix) as $originNode) { 48 if (! in_array($originNode, $nodes)) { 49 array_push($nodes, $originNode); 50 } 51 52 $distancesFromThisNode = $distanceMatrix[$originNode]; 53 foreach (array_keys($distancesFromThisNode) as $destinationNode) { 54 if (! in_array($destinationNode, $nodes)) { 55 array_push($nodes, $destinationNode); 56 } 57 } 58 } 59 sort($nodes); 60 61 // echo "-- _nodesInMmatrix: outputting nodes:\n"; var_dump($nodes); 62 63 return $nodes; 64 } 65 66 function computeShortestPathes($start, $to = null) 67 { 68 $this -> startnode = $start; 69 foreach ($this->nodes as $currNode) { 70 if ($currNode == $this -> startnode) { 71 $this -> visited[$currNode] = true; 72 $this -> distance[$currNode] = 0; 73 } else { 74 $this -> visited[$currNode] = false; 75 $this -> distance[$currNode] = isset($this -> map[$this -> startnode][$currNode]) 76 ? $this -> map[$this -> startnode][$currNode] 77 : $this -> infiniteDistance; 78 } 79 $this -> previousNode[$currNode] = $this -> startnode; 80 } 81 82 $maxTries = $this -> numberOfNodes; 83 84 // echo "-- computeShortestPathes: \$maxTries=$maxTries\n"; 85 86 $tries = 0; 87 while (in_array(false, $this -> visited, true) && $tries <= $maxTries) { 88 // echo "-- computeShortestPathes: \$tries=$tries\n\$this->distance=\n";var_dump($this->distance);echo "\n\$this->previousNode=\n";var_dump($this->previousNode);print "\n"; 89 $this -> bestPath = $this->findBestPath($this->distance, array_keys($this -> visited, false, true)); 90 if ($to !== null && $this -> bestPath === $to) { 91 break; 92 } 93 $this -> updateDistanceAndPrevious($this -> bestPath); 94 $this -> visited[$this -> bestPath] = true; 95 $tries++; 96 } 97 $this -> shortestPathes = $this->getShortestPathesInfo(); 98 99 return $this -> shortestPathes; 100 } 101 102 function findBestPath($ourDistance, $ourNodesLeft) 103 { 104 // echo "-- findBestPath: \ourDistance=\n";var_dump($ourDistance);echo "\n\$ourNodesLeft=\n";var_dump($ourNodesLeft); 105 $bestPath = $this -> infiniteDistance; 106 $bestNode = null; 107 foreach ($ourNodesLeft as $currNode) { 108 // echo "-- findBestPath: processing node: $currNode, \$ourDistance[\$currNode]=$ourDistance[$currNode], \$bestPath=$bestPath\n"; 109 if ($ourDistance[$currNode] < $bestPath) { 110 $bestPath = $ourDistance[$currNode]; 111 $bestNode = $currNode; 112 } 113 } 114 // echo "-- findBestPath: upon exit, \$bestNode=$bestNode, \ourDistance=\n";var_dump($ourDistance);echo "\n\$ourNodesLeft=\n";var_dump($ourNodesLeft); 115 return $bestNode; 116 } 117 118 function updateDistanceAndPrevious($obp) 119 { 120 // echo "-- updateDistanceAndPrevious: \$obp=$obp\n"; 121 foreach ($this->nodes as $currNode) { 122 // echo "-- updateDistanceAndPrevious: processing \$currNode=$currNode\n"; 123 if ((isset($this->map[$obp][$currNode])) 124 && (! ($this->map[$obp][$currNode] == $this->infiniteDistance) || ($this->map[$obp][$currNode] == 0 )) 125 && (($this->distance[$obp] + $this->map[$obp][$currNode]) < $this -> distance[$currNode]) 126 ) { 127 // echo "-- updateDistanceAndPrevious: found shorter rout to \$currNode, through \$obp.\n"; 128 $this -> distance[$currNode] = $this -> distance[$obp] + $this -> map[$obp][$currNode]; 129 $this -> previousNode[$currNode] = $obp; 130 } 131 } 132 133 // echo "-- updateDistanceAndPrevious: upon exit, \$this->distance=\n";var_dump($this->distance);echo "\n\$this->previousNode=\n";var_dump($this->previousNode); echo"\n"; 134 } 135 136 function shortestPathTo($destination_node_num) 137 { 138 return $this->shortestPathes[$destination_node_num]; 139 } 140 141 function shortestDistanceTo($destination_node_num) 142 { 143 return $this->distance[$destination_node_num]; 144 } 145 146 function printMap(&$map) 147 { 148 $placeholder = ' %' . strlen($this -> infiniteDistance) . 'd'; 149 $foo = ''; 150 for ($i = 0, $im = count($map); $i < $im; $i++) { 151 for ($k = 0, $m = $im; $k < $m; $k++) { 152 $foo .= sprintf($placeholder, isset($map[$i][$k]) ? $map[$i][$k] : $this -> infiniteDistance); 153 } 154 $foo .= "\n"; 155 } 156 return $foo; 157 } 158 159 function getShortestPathesInfo($to = null) 160 { 161 $ourShortestPath = []; 162 foreach ($this->nodes as $aNode) { 163 if ($to !== null && $to !== $aNode) { 164 continue; 165 } 166 $ourShortestPath[$aNode] = []; 167 $endNode = null; 168 $currNode = $aNode; 169 $ourShortestPath[$aNode][] = $aNode; 170 while ($endNode === null || $endNode != $this -> startnode) { 171 $ourShortestPath[$aNode][] = $this -> previousNode[$currNode]; 172 $endNode = $this -> previousNode[$currNode]; 173 $currNode = $this -> previousNode[$currNode]; 174 } 175 $ourShortestPath[$aNode] = array_reverse($ourShortestPath[$aNode]); 176 if ($to === null || $to === $aNode) { 177 if ($to === $aNode) { 178 break; 179 } 180 } 181 } 182 return $ourShortestPath; 183 } 184 185 function getResults($to = null) 186 { 187 $ourShortestPath = []; 188 $foo = ''; 189 foreach ($this->nodes as $aNode) { 190 if ($to !== null && $to !== $aNode) { 191 continue; 192 } 193 $ourShortestPath[$aNode] = []; 194 $endNode = null; 195 $currNode = $aNode; 196 $ourShortestPath[$aNode][] = $aNode; 197 while ($endNode === null || $endNode != $this -> startnode) { 198 $ourShortestPath[$aNode][] = $this -> previousNode[$currNode]; 199 $endNode = $this -> previousNode[$currNode]; 200 $currNode = $this -> previousNode[$currNode]; 201 } 202 $ourShortestPath[$aNode] = array_reverse($ourShortestPath[$aNode]); 203 if ($to === null || $to === $aNode) { 204 if ($this -> distance[$aNode] >= $this -> infiniteDistance) { 205 $foo .= sprintf("no route from %d to %d. \n", $this -> startnode, $aNode); 206 } else { 207 $foo .= sprintf( 208 '%d => %d = %d [%d]: (%s).' . "\n", 209 $this -> startnode, 210 $aNode, 211 $this -> distance[$aNode], 212 count($ourShortestPath[$aNode]), 213 implode('-', $ourShortestPath[$aNode]) 214 ); 215 } 216 $foo .= str_repeat('-', 20) . "\n"; 217 if ($to === $aNode) { 218 break; 219 } 220 } 221 } 222 return $foo; 223 } 224} 225