minimalspanningtree.java

来自「经典的货郎担问题解决办法」· Java 代码 · 共 764 行 · 第 1/2 页

JAVA
764
字号
/*** This code was written from scratch by Kent Paul Dolan, the greedy MST** creation part from memory of published algorithms, probably due to** Preparata and Shamos's _Computational Geometry_.  See accompanying** file TravellerDoc.html for status for your use.*/package com.well.www.user.xanthian.java.seeders;import com.coyotegulch.genetic.*;import com.coyotegulch.tools.*;import com.well.www.user.xanthian.java.structures.*;import com.well.www.user.xanthian.java.tools.*;import com.well.www.user.xanthian.java.ui.*;public class MinimalSpanningTree{  private int         []  m_minimalSpanningTree = null;  private int             m_genomeLength;  private int             m_treeSize;  private long            VIEWING_DELAY = 100;  private static double   m_floor = Double.MAX_VALUE;  private MersenneTwister m_mt = null;  private TravellerCanvas m_mstCanvas = null;/*** FIXME This is trashy, MinimalSpanningTree has no business knowing** about something as complex as a TravellerWorld, but to fix it requires** ripping the city location information out of TravellerWorld and into** a public class; live with it for now.*/  public MinimalSpanningTree( TravellerWorld world )  {    m_mt = MersenneTwister.getTwister();    m_genomeLength           = ValuatorControls.getNumberOfCities();    m_treeSize               = ( m_genomeLength * 2 ) - 2;    m_minimalSpanningTree    = new int     [m_treeSize];    m_mstCanvas              = new TravellerCanvas();    boolean [] isIn          = new boolean [m_genomeLength];    boolean [] attached      = new boolean [m_treeSize];    int     [] nodeRepeats   = new int     [m_genomeLength];    double  [] toNearest     = new double  [m_genomeLength];    int     [] myNearest     = new int     [m_genomeLength];    int       nodesFilled    = 0;    double floor             = 0.0D;    m_mstCanvas.setup( "Minimal Spanning Tree" );    for (int i = 0; i < m_treeSize; i++)    {      m_minimalSpanningTree[i] = -1;      attached[i]              = false;    }    for (int i = 0; i < m_genomeLength; i++)    {      isIn[i] = false;      toNearest[i] = Double.MAX_VALUE;      myNearest[i] = -1;      nodeRepeats[i] = 0;    }/*** Add the shortest possible edge, as the minimum which could possibly** be added to an MST to make a circuit (in the circular layout case).** Though, obviously this would not be the omitted edge, something at** least the length of the Nth smallest would be.  [It's more** complicated than _that_, too; lots of short edges could be crowded** out by shorter ones that share a node.] FIXME LATER Hmmm.  Worth** doing?  Maybe not.  A tighter floor would use this Nth smallest edge** instead, I just don't want to sort a list of distances of length N*N,** though maintaining a heap of length N shortest edges seen so far, and** taking it would be doable.*/    m_floor = Double.MAX_VALUE;    for (int i = 0; i < m_genomeLength; i++)    {      // Method world.getDistance() returns all off-diagonal lengths      // twice, but we only need to look at them once, here, so      // do a triangular walk to save effort.      for (int j = 0; j <= i ; j++)      {        double temp = world.getDistance(i, j);        if ( temp < m_floor ) { m_floor = temp; }      }    }/*** Seed the minimal spanning tree with a single node. It doesn't matter** where we start, so start with node 0.*/    m_minimalSpanningTree[0] = 0;    attached[0] = true;    isIn[0] = true;    nodeRepeats[0]++;    nodesFilled++;/*** Display start state.*/    m_mstCanvas.redraw();    m_mstCanvas.drawNodes    (      world.getCityDrawAtLocations(),      ValuatorControls.getNumberOfCities(),      world.CITY_X,      world.CITY_Y,      TravellerColors.COLOR_CITY    );    m_mstCanvas.drawImage();    while ( nodesFilled < m_treeSize )    {      int    bestInnie   = -1;      int    bestOutie   = -1;      int    addAfter    = nodesFilled;      double bestDistance = Double.MAX_VALUE;      for (int i = (nodesFilled - 1); i >= 0; i--)      {        int currentNode = m_minimalSpanningTree[i];        int otherNode   = myNearest[currentNode];        double otherDistance = toNearest[currentNode];        // Do we need to create or refresh a nearest neighbor entry?        // This better be a lazy disjunctive, or we access outside array!        if ( ( otherNode == -1 ) || isIn[ otherNode ] )        {          otherDistance = Double.MAX_VALUE;          otherNode     = -1;          for ( int j = 0; j < m_genomeLength; j++ )          {            if ( ( j != currentNode ) && ( ! isIn[j] ) )            {              // if ( distances[currentNode][j] < otherDistance )              double temp = world.getDistance( currentNode, j );              if ( temp < otherDistance )              {                otherDistance = temp;                otherNode     = j;              }            }          }          // FIXME create an exception to throw so that we can          // FIXME cater for algorithm failure here!          if (otherNode == -1) { kablooie("otherNode == -1"); }          myNearest[currentNode] = otherNode;          toNearest[currentNode] = otherDistance;        }        if ( toNearest[currentNode] < bestDistance )        {          bestDistance = toNearest[currentNode];          bestInnie = currentNode;          bestOutie = otherNode;          addAfter = i;        }      }/*** Roll data two slots toward end of MST array, to make room for two** added nodes.*/      for ( int i = (nodesFilled - 1); i > addAfter; i-- )      {/*** Special case the very last copy, so that we can avoid a redundant** copy of node zero, and make the MST one element shorter.*/        if ( i + 2 < m_treeSize )        {          m_minimalSpanningTree[i + 2] = m_minimalSpanningTree[i];          attached[i+2] = attached[i];        }      }      // FIXME another place to check for failure!      // bestInnie must equal m_minimalSpanningTree[addAfter];      // The idea is to add an edge out to the new node      // and then add an edge back to the connecting node,      // to keep the graph tree-shaped, and so that the edge      // to the former succeeding node is preserved.      if (bestInnie != m_minimalSpanningTree[addAfter])      { kablooie("bestInnie != m_minimalSpanningTree[addAfter]"); }      m_minimalSpanningTree[addAfter + 1] = bestOutie;      m_minimalSpanningTree[addAfter + 2] = bestInnie;      isIn[ bestOutie ] = true;      attached[addAfter + 1] = true;      attached[addAfter + 2] = true;      nodeRepeats[bestOutie]++;      nodeRepeats[bestInnie]++;      if ( nodesFilled + 2 < m_treeSize )      {        nodesFilled += 2;      }      else      {        nodesFilled += 1;      }/*** Display progress.*/    m_mstCanvas.clearPlayfield();    m_mstCanvas.drawEdges    (      m_minimalSpanningTree,      nodesFilled,      world.getCityDrawAtLocations(),      world.CITY_X,      world.CITY_Y,      TravellerColors.COLOR_SEED_ROUTE,      true // closed path    );    m_mstCanvas.drawNodes    (      world.getCityDrawAtLocations(),      ValuatorControls.getNumberOfCities(),      world.CITY_X,      world.CITY_Y,      TravellerColors.COLOR_CITY    );    m_mstCanvas.drawImage();/*** Add length of newly added edge to "mst length + shortest edge" floor** value.*/      m_floor += bestDistance;    }    m_mstCanvas.clearPlayfield();  }  public void closeWindow()  {    if ( m_mstCanvas != null )    {      m_mstCanvas.windowClose();      m_mstCanvas = null;    }  }  public static double getFloor() { return m_floor; }/*** Implement a greedy deriver of a genome from the MST; greedy in the** sense that it removes replicated codon names in order of the one** currently providing the greatest improvement in fitness, until only a** permutation genome's worth are left.  Maintaining the current** removable-node fitness increments would be easy, if they were just** kept in an array, but that would imply at least N*N performance,** hopeless for the eventual problem size goals of Traveller.  At the** cost of great code complexity and debugging time, instead I have** implemented a Heap With Deletion, into which fitness improvement** values and associated tag information can be pushed and from which** the next node to remove can be popped.  The killer is that when a** node is removed, the two nodes to which it was connected need their** fitness increments revised, which means finding them wherever they** may be in the heap, removing them from the heap, then pushing them** back on the heap with revised fitness increment values.  Keeping all** the pointers, counters, flags, and other foofooraw needed to do this,** correct, is mind-bogglingly complex. Beelz, my brane hurts!** ** Since this algorithm has no randomness, it makes no particular sense** to put more than one of these genomes in the Traveller population, so** TravellerWorld caters by calling this routine for the only first** MST-derived genome, and the non-greedy, partially random-driven,** MST-derived genome generator twin to this function for the rest.*/  public int [] getGreedilyDerivedNodeList( TravellerWorld world )  {    boolean DB = false;    if ( CheckBoxControls.getState(CheckBoxControls.CBC_DEBUG_PRINTOUTS) )    {      DB = true;    }    int nodeRepeats[] = new int[m_genomeLength];    for ( int i = 0; i < m_genomeLength; i++ )    {      nodeRepeats[i] = 0;    }    boolean attached[] = new boolean[m_treeSize];    for (int i = 0; i < m_treeSize; i++)    {      attached[i] = true;      nodeRepeats[ m_minimalSpanningTree[i] ]++;    }/*** Compute for each repeated node the gain if that repetition were** removed.  Our intention always, of course, is to remove all but one** of the MSTs repeated nodes to make a valid genome; here we try to** remove all but the least useful-to-remove one.*/    HeapWithDelete fitnessGains =      new HeapWithDelete( HeapWithDelete.POPS_LARGEST );    IntDoublePairSortedOnDouble fitnessChange[] =      new IntDoublePairSortedOnDouble[m_treeSize];    for ( int i = 0; i < m_treeSize; i++ )    {      if ( nodeRepeats[m_minimalSpanningTree[i]] > 1 )      {        double fitnessToBeGained =          fitnessGained          (            ( i - 1 + m_treeSize ) % m_treeSize,            i,            ( i + 1 ) % m_treeSize,            world          );        fitnessChange[i] =          new IntDoublePairSortedOnDouble( i, fitnessToBeGained );        fitnessGains.push( (ContentComparable) new IntDoublePairSortedOnDouble( fitnessChange[i] ) );      }      else      {        fitnessChange[i] =          new IntDoublePairSortedOnDouble( i, Double.MIN_VALUE );        // and do _not_ push it on the heap.      }    }    if (DB)    {      System.out.println( "m_minimalSpanningTree at start of greedily:\r\n" + Debugging.dump( m_minimalSpanningTree ) );      System.out.println( "attached at start of greedily:\r\n" + Debugging.dump( attached ) );      System.out.println( "fitnessChange at start of greedily:\r\n" + Debugging.dump( fitnessChange ) );      System.out.println( "nodeRepeats at start of greedily:\r\n" + Debugging.dump( nodeRepeats ) );    }    int nodesLeft = m_treeSize;    while ( nodesLeft > m_genomeLength && ( ! fitnessGains.isEmpty() ) )    {      if (DB)      {        System.out.println( "m_minimalSpanningTree at top of greedily main while loop:\r\n" + Debugging.dump( m_minimalSpanningTree ) );        System.out.println( "attached at top of greedily main while loop:\r\n" + Debugging.dump( attached ) );        System.out.println( "fitnessChange at top of greedily main while loop:\r\n" + Debugging.dump( fitnessChange ) );        System.out.println( "nodeRepeats at top of greedily main while loop:\r\n" + Debugging.dump( nodeRepeats ) );      }      IntDoublePairSortedOnDouble pair =        (IntDoublePairSortedOnDouble) fitnessGains.pop();      if (DB) { System.out.println( pair.toString() + " popped to detach in greedily" ); }      int currentTreeNode = pair.getInt();      int currentCodonName = m_minimalSpanningTree[currentTreeNode];      if ( nodeRepeats[currentCodonName] > 1 )      {/*** We have a _lot_ of work to do here; mark the node unattached, do** bookkeeping on the repeat and nodesLeft counts, and find this node's** predecessor and successor nodes and redo their fitness gains and** reinsert them in the fitness heap.*/

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?