Ticket #7295: assign_nodes_hungarian.patch

File assign_nodes_hungarian.patch, 22.1 KB (added by joshdoe, 14 years ago)

assign nodes using Hungarian algorithm

  • src/utilsplugin2/dumbutils/ReplaceGeometryUtils.java

     
    255255        commands.addAll(tagResolutionCommands);
    256256       
    257257        // Prepare a list of nodes that are not used anywhere except in the way
    258         Collection<Node> nodePool = getUnimportantNodes(subjectWay);
     258        List<Node> nodePool = getUnimportantNodes(subjectWay);
    259259
    260260        // And the same for geometry, list nodes that can be freely deleted
    261         Set<Node> geometryPool = new HashSet<Node>();
     261        List<Node> geometryPool = new LinkedList<Node>();
    262262        for( Node node : referenceWay.getNodes() ) {
    263263            List<OsmPrimitive> referrers = node.getReferrers();
    264264            if( node.isNew() && !node.isDeleted() && referrers.size() == 1
    265265                    && referrers.get(0).equals(referenceWay) && !subjectWay.containsNode(node)
    266                     && !hasInterestingKey(node))
     266                    && !hasInterestingKey(node) && !geometryPool.contains(node))
    267267                geometryPool.add(node);
    268268        }
    269269
     270//        // Find new nodes that are closest to the old ones, remove matching old ones from the pool
     271//        Map<Node, Node> nodeAssoc = new HashMap<Node, Node>();
     272//        for( Node n : geometryPool ) {
     273//            Node nearest = findNearestNode(n, nodePool);
     274//            if( nearest != null ) {
     275//                nodeAssoc.put(n, nearest);
     276//                nodePool.remove(nearest);
     277//            }
     278//        }
     279
    270280        // Find new nodes that are closest to the old ones, remove matching old ones from the pool
     281        // Assign node moves with least overall distance moved
     282        int maxLen = Math.max(geometryPool.size(), nodePool.size());
     283        double cost[][] = new double[maxLen][maxLen];
     284        for (int i = 0; i < maxLen; i++) {
     285            for (int j = 0; j < maxLen; j++) {
     286                cost[i][j] = Double.MAX_VALUE;
     287            }
     288        }
     289        double maxDistance = Double.parseDouble(Main.pref.get("utilsplugin2.replace-geometry.max-distance", "1"));
     290        for (int i = 0; i < geometryPool.size(); i++) {
     291            for (int j = 0; j < nodePool.size(); j++) {
     292                double d = geometryPool.get(i).getCoor().distance(nodePool.get(j).getCoor());
     293                if (d > maxDistance)
     294                    cost[i][j] = Double.MAX_VALUE;
     295                else
     296                    cost[i][j] = d;
     297            }
     298        }
     299        int[][] assignment = HungarianAlgorithm.hgAlgorithm(cost, "min");
    271300        Map<Node, Node> nodeAssoc = new HashMap<Node, Node>();
    272         for( Node n : geometryPool ) {
    273             Node nearest = findNearestNode(n, nodePool);
    274             if( nearest != null ) {
    275                 nodeAssoc.put(n, nearest);
    276                 nodePool.remove(nearest);
     301        for (int i = 0; i < maxLen; i++) {
     302            int gIdx = assignment[i][0];
     303            int nIdx = assignment[i][1];
     304            if (cost[gIdx][nIdx] != Double.MAX_VALUE) {
     305                nodeAssoc.put(geometryPool.get(gIdx), nodePool.get(nIdx));
    277306            }
    278307        }
     308        for (Node n : nodeAssoc.values()) {
     309            nodePool.remove(n);
     310        }
    279311
    280312        // Now that we have replacement list, move all unused new nodes to nodePool (and delete them afterwards)
    281313        for( Node n : geometryPool )
     
    318350     * @param way
    319351     * @return
    320352     */
    321     protected static Collection<Node> getUnimportantNodes(Way way) {
    322         Set<Node> nodePool = new HashSet<Node>();
     353    protected static List<Node> getUnimportantNodes(Way way) {
     354        List<Node> nodePool = new LinkedList<Node>();
    323355        for (Node n : way.getNodes()) {
    324356            List<OsmPrimitive> referrers = n.getReferrers();
    325357            if (!n.isDeleted() && referrers.size() == 1 && referrers.get(0).equals(way)
    326                     && !hasInterestingKey(n)) {
     358                    && !hasInterestingKey(n) && !nodePool.contains(n)) {
    327359                nodePool.add(n);
    328360            }
    329361        }
  • src/utilsplugin2/dumbutils/HungarianAlgorithm.java

     
     1/*
     2 * Created on Apr 25, 2005
     3 *
     4 * Munkres-Kuhn (Hungarian) Algorithm Clean Version: 0.11
     5 *
     6 * Konstantinos A. Nedas                     
     7 * Department of Spatial Information Science & Engineering
     8 * University of Maine, Orono, ME 04469-5711, USA
     9 * kostas@spatial.maine.edu
     10 * http://www.spatial.maine.edu/~kostas       
     11 *
     12 * This Java class implements the Hungarian algorithm [a.k.a Munkres' algorithm,
     13 * a.k.a. Kuhn algorithm, a.k.a. Assignment problem, a.k.a. Marriage problem,
     14 * a.k.a. Maximum Weighted Maximum Cardinality Bipartite Matching].
     15 *
     16 * [It can be used as a method call from within any main (or other function).]
     17 * It takes 2 arguments:
     18 * a. A 2-D array (could be rectangular or square).
     19 * b. A string ("min" or "max") specifying whether you want the min or max assignment.
     20 * [It returns an assignment matrix[array.length][2] that contains the row and col of
     21 * the elements (in the original inputted array) that make up the optimum assignment.]
     22 * 
     23 * [This version contains only scarce comments. If you want to understand the
     24 * inner workings of the algorithm, get the tutorial version of the algorithm
     25 * from the same website you got this one (http://www.spatial.maine.edu/~kostas/dev/soft/munkres.htm)]
     26 *
     27 * Any comments, corrections, or additions would be much appreciated.
     28 * Credit due to professor Bob Pilgrim for providing an online copy of the
     29 * pseudocode for this algorithm (http://216.249.163.93/bob.pilgrim/445/munkres.html)
     30 *
     31 * Feel free to redistribute this source code, as long as this header--with
     32 * the exception of sections in brackets--remains as part of the file.
     33 *
     34 * Requirements: JDK 1.5.0_01 or better.
     35 * [Created in Eclipse 3.1M6 (www.eclipse.org).]
     36 *
     37 */
     38
     39package utilsplugin2.dumbutils;
     40
     41import static java.lang.Math.*;
     42import java.util.*;
     43
     44public class HungarianAlgorithm {
     45
     46        //********************************//
     47        //METHODS FOR CONSOLE INPUT-OUTPUT//
     48        //********************************//
     49       
     50        public static int readInput(String prompt)      //Reads input,returns double.
     51        {
     52                Scanner in = new Scanner(System.in);
     53                System.out.print(prompt);
     54                int input = in.nextInt();
     55                return input;
     56        }
     57        public static void printTime(double time)       //Formats time output.
     58        {
     59                String timeElapsed = "";
     60                int days = (int)floor(time)/(24 * 3600);
     61                int hours = (int)floor(time%(24*3600))/(3600);
     62                int minutes = (int)floor((time%3600)/60);
     63                int seconds = (int)round(time%60);
     64               
     65                if (days > 0)
     66                        timeElapsed = Integer.toString(days) + "d:";
     67                if (hours > 0)
     68                        timeElapsed = timeElapsed + Integer.toString(hours) + "h:";
     69                if (minutes > 0)
     70                        timeElapsed = timeElapsed + Integer.toString(minutes) + "m:";
     71               
     72                timeElapsed = timeElapsed + Integer.toString(seconds) + "s";
     73                System.out.print("\nTotal time required: " + timeElapsed + "\n\n");
     74        }
     75       
     76        //*******************************************//
     77        //METHODS THAT PERFORM ARRAY-PROCESSING TASKS//
     78        //*******************************************//
     79       
     80        public static void generateRandomArray  //Generates random 2-D array.
     81        (double[][] array, String randomMethod)
     82        {
     83                Random generator = new Random();
     84                for (int i=0; i<array.length; i++)
     85                {
     86                        for (int j=0; j<array[i].length; j++)
     87                        {
     88                                if (randomMethod.equals("random"))
     89                                        {array[i][j] = generator.nextDouble();}
     90                                if (randomMethod.equals("gaussian"))
     91                                {
     92                                                array[i][j] = generator.nextGaussian()/4;               //range length to 1.
     93                                                if (array[i][j] > 0.5) {array[i][j] = 0.5;}             //eliminate outliers.
     94                                                if (array[i][j] < -0.5) {array[i][j] = -0.5;}   //eliminate outliers.
     95                                                array[i][j] = array[i][j] + 0.5;                                //make elements positive.
     96                                }
     97                        }
     98                }                       
     99        }
     100        public static double findLargest                //Finds the largest element in a positive array.
     101        (double[][] array)
     102        //works for arrays where all values are >= 0.
     103        {
     104                double largest = 0;
     105                for (int i=0; i<array.length; i++)
     106                {
     107                        for (int j=0; j<array[i].length; j++)
     108                        {
     109                                if (array[i][j] > largest)
     110                                {
     111                                        largest = array[i][j];
     112                                }
     113                        }
     114                }
     115                       
     116                return largest;
     117        }
     118        public static double[][] transpose              //Transposes a double[][] array.
     119        (double[][] array)     
     120        {
     121                double[][] transposedArray = new double[array[0].length][array.length];
     122                for (int i=0; i<transposedArray.length; i++)
     123                {
     124                        for (int j=0; j<transposedArray[i].length; j++)
     125                        {transposedArray[i][j] = array[j][i];}
     126                }
     127                return transposedArray;
     128        }
     129        public static double[][] copyOf                 //Copies all elements of an array to a new array.
     130        (double[][] original)   
     131        {
     132                double[][] copy = new double[original.length][original[0].length];
     133                for (int i=0; i<original.length; i++)
     134                {
     135                        //Need to do it this way, otherwise it copies only memory location
     136                        System.arraycopy(original[i], 0, copy[i], 0, original[i].length);
     137                }
     138               
     139                return copy;
     140        }
     141       
     142        //**********************************//
     143        //METHODS OF THE HUNGARIAN ALGORITHM//
     144        //**********************************//
     145       
     146        public static int[][] hgAlgorithm (double[][] array, String sumType)
     147        {
     148                double[][] cost = copyOf(array);        //Create the cost matrix
     149               
     150                if (sumType.equalsIgnoreCase("max"))    //Then array is weight array. Must change to cost.
     151                {
     152                        double maxWeight = findLargest(cost);
     153                        for (int i=0; i<cost.length; i++)               //Generate cost by subtracting.
     154                        {
     155                                for (int j=0; j<cost[i].length; j++)
     156                                {
     157                                        cost [i][j] = (maxWeight - cost [i][j]);
     158                                }
     159                        }
     160                }
     161                double maxCost = findLargest(cost);             //Find largest cost matrix element (needed for step 6).
     162               
     163                int[][] mask = new int[cost.length][cost[0].length];    //The mask array.
     164                int[] rowCover = new int[cost.length];                                  //The row covering vector.
     165                int[] colCover = new int[cost[0].length];                               //The column covering vector.
     166                int[] zero_RC = new int[2];                                                             //Position of last zero from Step 4.
     167                int step = 1;                                                                                   
     168                boolean done = false;
     169                while (done == false)   //main execution loop
     170                {
     171                        switch (step)
     172                    {
     173                                case 1:
     174                                        step = hg_step1(step, cost);     
     175                            break;
     176                        case 2:
     177                            step = hg_step2(step, cost, mask, rowCover, colCover);
     178                                        break;
     179                        case 3:
     180                            step = hg_step3(step, mask, colCover);
     181                                        break;
     182                        case 4:
     183                            step = hg_step4(step, cost, mask, rowCover, colCover, zero_RC);
     184                                        break;
     185                        case 5:
     186                                        step = hg_step5(step, mask, rowCover, colCover, zero_RC);
     187                                        break;
     188                        case 6:
     189                                step = hg_step6(step, cost, rowCover, colCover, maxCost);
     190                                        break;
     191                            case 7:
     192                            done=true;
     193                            break;
     194                    }
     195                }//end while
     196               
     197                int[][] assignment = new int[array.length][2];  //Create the returned array.
     198                for (int i=0; i<mask.length; i++)
     199                {
     200                        for (int j=0; j<mask[i].length; j++)
     201                        {
     202                                if (mask[i][j] == 1)
     203                                {
     204                                        assignment[i][0] = i;
     205                                        assignment[i][1] = j;
     206                                }
     207                        }
     208                }
     209               
     210                //If you want to return the min or max sum, in your own main method
     211                //instead of the assignment array, then use the following code:
     212                /*
     213                double sum = 0;
     214                for (int i=0; i<assignment.length; i++)
     215                {
     216                        sum = sum + array[assignment[i][0]][assignment[i][1]];
     217                }
     218                return sum;
     219                */
     220                //Of course you must also change the header of the method to:
     221                //public static double hgAlgorithm (double[][] array, String sumType)
     222               
     223                return assignment;
     224        }
     225        public static int hg_step1(int step, double[][] cost)
     226        {
     227                //What STEP 1 does:
     228                //For each row of the cost matrix, find the smallest element
     229                //and subtract it from from every other element in its row.
     230           
     231                double minval;
     232               
     233                for (int i=0; i<cost.length; i++)       
     234                {                                                                       
     235                    minval=cost[i][0];
     236                    for (int j=0; j<cost[i].length; j++)        //1st inner loop finds min val in row.
     237                    {
     238                        if (minval>cost[i][j])
     239                        {
     240                            minval=cost[i][j];
     241                        }
     242                        }
     243                        for (int j=0; j<cost[i].length; j++)    //2nd inner loop subtracts it.
     244                    {
     245                        cost[i][j]=cost[i][j]-minval;
     246                    }
     247                }
     248                                   
     249                step=2;
     250                return step;
     251        }
     252        public static int hg_step2(int step, double[][] cost, int[][] mask, int[]rowCover, int[] colCover)
     253        {
     254                //What STEP 2 does:
     255                //Marks uncovered zeros as starred and covers their row and column.
     256               
     257                for (int i=0; i<cost.length; i++)
     258            {
     259                for (int j=0; j<cost[i].length; j++)
     260                {
     261                    if ((cost[i][j]==0) && (colCover[j]==0) && (rowCover[i]==0))
     262                    {
     263                        mask[i][j]=1;
     264                                        colCover[j]=1;
     265                        rowCover[i]=1;
     266                                }
     267                }
     268            }
     269                                                       
     270                clearCovers(rowCover, colCover);        //Reset cover vectors.
     271                           
     272                step=3;
     273                return step;
     274        }
     275        public static int hg_step3(int step, int[][] mask, int[] colCover)
     276        {
     277                //What STEP 3 does:
     278                //Cover columns of starred zeros. Check if all columns are covered.
     279               
     280                for (int i=0; i<mask.length; i++)       //Cover columns of starred zeros.
     281            {
     282                for (int j=0; j<mask[i].length; j++)
     283                {
     284                    if (mask[i][j] == 1)
     285                    {
     286                        colCover[j]=1;
     287                                }
     288                }
     289            }
     290           
     291                int count=0;                                           
     292                for (int j=0; j<colCover.length; j++)   //Check if all columns are covered.
     293            {
     294                count=count+colCover[j];
     295            }
     296               
     297                if (count>=mask.length) //Should be cost.length but ok, because mask has same dimensions.       
     298            {
     299                        step=7;
     300                }
     301            else
     302                {
     303                        step=4;
     304                }
     305               
     306                return step;
     307        }
     308        public static int hg_step4(int step, double[][] cost, int[][] mask, int[] rowCover, int[] colCover, int[] zero_RC)
     309        {
     310                //What STEP 4 does:
     311                //Find an uncovered zero in cost and prime it (if none go to step 6). Check for star in same row:
     312                //if yes, cover the row and uncover the star's column. Repeat until no uncovered zeros are left
     313                //and go to step 6. If not, save location of primed zero and go to step 5.
     314               
     315                int[] row_col = new int[2];     //Holds row and col of uncovered zero.
     316                boolean done = false;
     317                while (done == false)
     318                {
     319                        row_col = findUncoveredZero(row_col, cost, rowCover, colCover);
     320                        if (row_col[0] == -1)
     321                        {
     322                                done = true;
     323                                step = 6;
     324                        }
     325                        else
     326                        {
     327                                mask[row_col[0]][row_col[1]] = 2;       //Prime the found uncovered zero.
     328                               
     329                                boolean starInRow = false;
     330                                for (int j=0; j<mask[row_col[0]].length; j++)
     331                                {
     332                                        if (mask[row_col[0]][j]==1)             //If there is a star in the same row...
     333                                        {
     334                                                starInRow = true;
     335                                                row_col[1] = j;         //remember its column.
     336                                        }
     337                                }
     338                                                       
     339                                if (starInRow==true)   
     340                                {
     341                                        rowCover[row_col[0]] = 1;       //Cover the star's row.
     342                                        colCover[row_col[1]] = 0;       //Uncover its column.
     343                                }
     344                                else
     345                                {
     346                                        zero_RC[0] = row_col[0];        //Save row of primed zero.
     347                                        zero_RC[1] = row_col[1];        //Save column of primed zero.
     348                                        done = true;
     349                                        step = 5;
     350                                }
     351                        }
     352                }
     353               
     354                return step;
     355        }
     356        public static int[] findUncoveredZero   //Aux 1 for hg_step4.
     357        (int[] row_col, double[][] cost, int[] rowCover, int[] colCover)
     358        {
     359                row_col[0] = -1;        //Just a check value. Not a real index.
     360                row_col[1] = 0;
     361               
     362                int i = 0; boolean done = false;
     363                while (done == false)
     364                {
     365                        int j = 0;
     366                        while (j < cost[i].length)
     367                        {
     368                                if (cost[i][j]==0 && rowCover[i]==0 && colCover[j]==0)
     369                                {
     370                                        row_col[0] = i;
     371                                        row_col[1] = j;
     372                                        done = true;
     373                                }
     374                                j = j+1;
     375                        }//end inner while
     376                        i=i+1;
     377                        if (i >= cost.length)
     378                        {
     379                                done = true;
     380                        }
     381                }//end outer while
     382               
     383                return row_col;
     384        }
     385        public static int hg_step5(int step, int[][] mask, int[] rowCover, int[] colCover, int[] zero_RC)
     386        {
     387                //What STEP 5 does:     
     388                //Construct series of alternating primes and stars. Start with prime from step 4.
     389                //Take star in the same column. Next take prime in the same row as the star. Finish
     390                //at a prime with no star in its column. Unstar all stars and star the primes of the
     391                //series. Erasy any other primes. Reset covers. Go to step 3.
     392               
     393                int count = 0;                                                                                          //Counts rows of the path matrix.
     394                int[][] path = new int[(mask[0].length*mask.length)][2];        //Path matrix (stores row and col).
     395                path[count][0] = zero_RC[0];                                                            //Row of last prime.
     396                path[count][1] = zero_RC[1];                                                            //Column of last prime.
     397               
     398                boolean done = false;
     399                while (done == false)
     400                {
     401                        int r = findStarInCol(mask, path[count][1]);
     402                        if (r>=0)
     403                        {
     404                                count = count+1;
     405                                path[count][0] = r;                                     //Row of starred zero.
     406                                path[count][1] = path[count-1][1];      //Column of starred zero.
     407                        }
     408                        else
     409                        {
     410                                done = true;
     411                        }
     412                       
     413                        if (done == false)
     414                        {
     415                                int c = findPrimeInRow(mask, path[count][0]);
     416                                count = count+1;
     417                                path[count][0] = path [count-1][0];     //Row of primed zero.
     418                                path[count][1] = c;                                     //Col of primed zero.
     419                        }
     420                }//end while
     421               
     422                convertPath(mask, path, count);
     423                clearCovers(rowCover, colCover);
     424                erasePrimes(mask);
     425               
     426                step = 3;
     427                return step;
     428               
     429        }
     430        public static int findStarInCol                 //Aux 1 for hg_step5.
     431        (int[][] mask, int col)
     432        {
     433                int r=-1;       //Again this is a check value.
     434                for (int i=0; i<mask.length; i++)
     435                {
     436                        if (mask[i][col]==1)
     437                        {
     438                                r = i;
     439                        }
     440                }
     441                               
     442                return r;
     443        }
     444        public static int findPrimeInRow                //Aux 2 for hg_step5.
     445        (int[][] mask, int row)
     446        {
     447                int c = -1;
     448                for (int j=0; j<mask[row].length; j++)
     449                {
     450                        if (mask[row][j]==2)
     451                        {
     452                                c = j;
     453                        }
     454                }
     455               
     456                return c;
     457        }
     458        public static void convertPath                  //Aux 3 for hg_step5.
     459        (int[][] mask, int[][] path, int count)
     460        {
     461                for (int i=0; i<=count; i++)
     462                {
     463                        if (mask[(path[i][0])][(path[i][1])]==1)
     464                        {
     465                                mask[(path[i][0])][(path[i][1])] = 0;
     466                        }
     467                        else
     468                        {
     469                                mask[(path[i][0])][(path[i][1])] = 1;
     470                        }
     471                }
     472        }
     473        public static void erasePrimes                  //Aux 4 for hg_step5.
     474        (int[][] mask)
     475        {
     476                for (int i=0; i<mask.length; i++)
     477                {
     478                        for (int j=0; j<mask[i].length; j++)
     479                        {
     480                                if (mask[i][j]==2)
     481                                {
     482                                        mask[i][j] = 0;
     483                                }
     484                        }
     485                }
     486        }
     487        public static void clearCovers                  //Aux 5 for hg_step5 (and not only).
     488        (int[] rowCover, int[] colCover)
     489        {
     490                for (int i=0; i<rowCover.length; i++)
     491                {
     492                        rowCover[i] = 0;
     493                }
     494                for (int j=0; j<colCover.length; j++)
     495                {
     496                        colCover[j] = 0;
     497                }
     498        }
     499        public static int hg_step6(int step, double[][] cost, int[] rowCover, int[] colCover, double maxCost)
     500        {
     501                //What STEP 6 does:
     502                //Find smallest uncovered value in cost: a. Add it to every element of covered rows
     503                //b. Subtract it from every element of uncovered columns. Go to step 4.
     504               
     505                double minval = findSmallest(cost, rowCover, colCover, maxCost);
     506               
     507                for (int i=0; i<rowCover.length; i++)
     508                {
     509                        for (int j=0; j<colCover.length; j++)
     510                        {
     511                                if (rowCover[i]==1)
     512                                {
     513                                        cost[i][j] = cost[i][j] + minval;
     514                                }
     515                                if (colCover[j]==0)
     516                                {
     517                                        cost[i][j] = cost[i][j] - minval;
     518                                }
     519                        }
     520                }
     521                       
     522                step = 4;
     523                return step;
     524        }
     525        public static double findSmallest               //Aux 1 for hg_step6.
     526        (double[][] cost, int[] rowCover, int[] colCover, double maxCost)
     527        {
     528                double minval = maxCost;                                //There cannot be a larger cost than this.
     529                for (int i=0; i<cost.length; i++)               //Now find the smallest uncovered value.
     530                {
     531                        for (int j=0; j<cost[i].length; j++)
     532                        {
     533                                if (rowCover[i]==0 && colCover[j]==0 && (minval > cost[i][j]))
     534                                {
     535                                        minval = cost[i][j];
     536                                }
     537                        }
     538                }
     539               
     540                return minval;
     541        }
     542       
     543        //***********//
     544        //MAIN METHOD//
     545        //***********//
     546       
     547        public static void main(String[] args)
     548        {
     549                //Below enter "max" or "min" to find maximum sum or minimum sum assignment.
     550                String sumType = "max";         
     551                               
     552                //Hard-coded example.
     553                //double[][] array =
     554                //{
     555                //              {1, 2, 3},
     556                //              {2, 4, 6},
     557                //              {3, 6, 9}
     558                //};
     559               
     560                //<UNCOMMENT> BELOW AND COMMENT BLOCK ABOVE TO USE A RANDOMLY GENERATED MATRIX
     561                int numOfRows = readInput("How many rows for the matrix? ");
     562                int numOfCols = readInput("How many columns for the matrix? ");
     563                double[][] array = new double[numOfRows][numOfCols];
     564                generateRandomArray(array, "random");   //All elements within [0,1].
     565                //</UNCOMMENT>
     566               
     567                if (array.length > array[0].length)
     568                {
     569                        System.out.println("Array transposed (because rows>columns).\n");       //Cols must be >= Rows.
     570                        array = transpose(array);
     571                }
     572                               
     573                //<COMMENT> TO AVOID PRINTING THE MATRIX FOR WHICH THE ASSIGNMENT IS CALCULATED
     574                System.out.println("\n(Printing out only 2 decimals)\n");
     575                System.out.println("The matrix is:");
     576                for (int i=0; i<array.length; i++)
     577                {
     578                        for (int j=0; j<array[i].length; j++)
     579                                {System.out.printf("%.2f\t", array[i][j]);}
     580                        System.out.println();
     581                }
     582                System.out.println();
     583                //</COMMENT>*/
     584               
     585                double startTime = System.nanoTime();   
     586                int[][] assignment = new int[array.length][2];
     587                assignment = hgAlgorithm(array, sumType);       //Call Hungarian algorithm.
     588                double endTime = System.nanoTime();
     589                                               
     590                System.out.println("The winning assignment (" + sumType + " sum) is:\n");       
     591                double sum = 0;
     592                for (int i=0; i<assignment.length; i++)
     593                {
     594                        //<COMMENT> to avoid printing the elements that make up the assignment
     595                        System.out.printf("array(%d,%d) = %.2f\n", (assignment[i][0]+1), (assignment[i][1]+1),
     596                                        array[assignment[i][0]][assignment[i][1]]);
     597                        sum = sum + array[assignment[i][0]][assignment[i][1]];
     598                        //</COMMENT>
     599                }
     600               
     601                System.out.printf("\nThe %s is: %.2f\n", sumType, sum);
     602                printTime((endTime - startTime)/1000000000.0);
     603               
     604        }
     605}