📄 snowplowsqueezebox.java
字号:
/*** This code was written by Kent Paul Dolan, from scratch. So far as I** know, it is an original (though obvious) algorithm. See accompanying** file TravellerDoc.html for status for your use.*/package com.well.www.user.xanthian.java.genetic.reproducers.asexual;import com.coyotegulch.tools.*;import com.coyotegulch.genetic.*;import com.well.www.user.xanthian.java.genetic.*;import com.well.www.user.xanthian.java.tools.*;import com.well.www.user.xanthian.java.ui.*;public class SnowPlowSqueezebox implements AsexualReproducer{ private final static String m_progressDisplayName = "Mama's Squeezbox"; private static SmallDisplay m_progressDisplay = null; private static int SMALL_DISPLAY_WIDTH = 40;/*** Because we do up to 2^(M-1) reversals as well as M! permutations, we** cannot afford the computational burden of the global permute limit;** support a local one as well. Unlike Optimize Near A Point, we are** not helped particularly by favorable geometry as we approach the** solution, either, and for large genomes, our worst case is our usual** case.** ** FIXME Tune this limit; the algorithm grows immensely more powerful** with increased limit, but it also grows sloooooow!*/ private static final int LOCAL_PERMUTE_LIMIT = 6; private static boolean DB = false; private static boolean VDB = false; private static VisualDebugger m_vdb = null; public Chromosome reproduce(Chromosome parent) { try {/*** Debugging hook abbreviation. During development, turn on debugging** just for this class by setting this variable to true, here. When the** code is stable, set it to false here, and control debugging from the** checkbox controls panel, instead. This variable is global to this** class, so it controls debugging thoughout the class when set here at** the top of the entry method for the class.*/ DB = false; if (CheckBoxControls.getState(CheckBoxControls.CBC_DEBUG_PRINTOUTS)) { DB = true; System.out.println ( "Entered SnowPlowSqueezebox.reproduce( Chromosome parent)" ); }/*** Rename the input to a less burdensome type.*/ TravellerChromosome p = (TravellerChromosome) parent; TravellerChromosome child = algorithm( p ); child.setOriginator( "SnowPlowSqueezebox" ); child.checkValidity(); return (Chromosome) child; } catch (Exception e) { System.err.println ( "SnowPlowSqueezebox.reproduce() threw!" ); }/*** This code should never be reached, it is just here to pacify javac.*/ return parent; } private TravellerChromosome algorithm( TravellerChromosome parent ) { VDB = false; if ( CheckBoxControls.getState(CheckBoxControls.CBC_DEBUG_VISUAL_WINDOWS) ) { VDB = true; } if (VDB) { if ( m_vdb == null ) { m_vdb = new VisualDebugger( "SnowPlowSqueezebox" ); } } else { if ( m_vdb != null ) { m_vdb.closeWindow(); m_vdb = null; } } if (VDB) { m_vdb.toFront(); } if (m_progressDisplay != null) { m_progressDisplay.toFront(); } MersenneTwister mt = MersenneTwister.getTwister(); TravellerChromosome offspring = new TravellerChromosome( parent ); offspring.canonicalize(); double startingFitness = offspring.testFitness(); if (VDB) { m_vdb.setup( offspring ); } TravellerWorld world = parent.getWorld(); int genomeLength = ValuatorControls.getNumberOfCities(); int permuteSize = ( new PermutationController() ) .getAPermuteSize ( Math.min ( genomeLength - 1, LOCAL_PERMUTE_LIMIT ) ); int cleavageIndices[] = new int[permuteSize]; // not used for now, we need to work harder than this int unimprovingLoopsLimit = 2 * PermutationController.getCurrentPermuteLimit(); int unimprovingLoopsCount = 0; int improvementCount = 0; int stepCount = 0; for ( int cleavageSpan = genomeLength; cleavageSpan > permuteSize; cleavageSpan-- ) { pickCleavageIndices( cleavageIndices, cleavageSpan, mt ); updateProgressDisplay ( "cI " + Debugging.dump(cleavageIndices) + " cS " + cleavageSpan + " uC " + unimprovingLoopsCount + " iC " + improvementCount + " sC " + stepCount ); int failureCount = 0; int successCount = 0; while( failureCount < genomeLength ) { if ( bladeful( cleavageIndices, offspring, world, mt ) ) { stepCount++; failureCount = 0; successCount++; improvementCount++; updateProgressDisplay ( "cI " + Debugging.dump(cleavageIndices) + " cS " + cleavageSpan + " uC " + unimprovingLoopsCount + " iC " + improvementCount + " sC " + stepCount ); } else { stepCount++; failureCount++; } advanceBlade( cleavageIndices, genomeLength ); if (VDB) { m_vdb.step( offspring ); } } if ( successCount > 0 ) { unimprovingLoopsCount = 0; } else { unimprovingLoopsCount++; // if ( unimprovingLoopsCount > unimprovingLoopsLimit ) { break; } } } updateProgressDisplay ( "cI " + Debugging.dump(cleavageIndices) + " uC " + unimprovingLoopsCount + " iC " + improvementCount + " sC " + stepCount + " done" );/*** Who knows what order the result has? Better fix it.*/ offspring.canonicalize(); double finalFitness = offspring.testFitness();/*** We only change for the better, so if we haven't changed, we haven't** improved. Report back so that adaptive permutation high limit can** eventually be updated.*/ if ( Math.abs( finalFitness - startingFitness ) < TravellerStatus.LITTLE_FUZZ ) { PermutationController.reportFailure(); } else { PermutationController.reportSuccess(); } if (VDB) { m_vdb.done( parent, offspring ); }/* if (VDB) { m_vdb.closeWindow(); m_vdb = null; }*/ return offspring; } private boolean inList( int c, int list[] ) { for (int i = 0; i < list.length; i++) { if (c == list[i]) { return true; } } return false; } private int listIndex( int c, int list[] ) { for (int i = 0; i < list.length; i++) { if (c == list[i]) { return i; } } return -1; } private boolean bladeful ( int cleavageIndices[], TravellerChromosome mutant, TravellerWorld world, MersenneTwister mt ) { double fitnessAtStart = mutant.testFitness(); // System.out.println( fitnessAtStart + " fitness at start of bladeful" ); TravellerChromosome readOnlyVersion = new TravellerChromosome( mutant );/*** Insert here ye beef!*/ int permuteSize = cleavageIndices.length; int genomeLength = ValuatorControls.getNumberOfCities(); PermutationGenerator pg = new PermutationGenerator( permuteSize, false ); // pick cleavage points int sublistBeginCities[] = new int[permuteSize]; int sublistEndCities[] = new int[permuteSize]; boolean sublistFlippable[] = new boolean[permuteSize]; for (int i = 0; i < permuteSize; i++) { sublistBeginCities[i] = -1; sublistEndCities[i] = -1; sublistFlippable[i] = true; } // fill in auxiliary array information. For computing // relative fitness, we don't need the whole sublists, // the interior lengths don't change. We just need the // end points to connect to each other. for (int i = 0; i < permuteSize; i++) { sublistBeginCities[i] = mutant.getCity(cleavageIndices[i]); sublistEndCities[i] = mutant.getCity ( ( cleavageIndices[(i + 1) % permuteSize] - 1 + genomeLength ) % genomeLength ); // We need not bother to reverse single entry lists, // they look the same from either end! if ( sublistBeginCities[i] == sublistEndCities[i] ) { sublistFlippable[i] = false; } } int bestPermutation[] = new int[permuteSize]; boolean bestFlipped[] = new boolean[permuteSize]; // Choose the original configuration as the best found, // for a start. Create a needed power of two. int powerOfTwo = 1; for (int i = 0; i < permuteSize; i++) { bestPermutation[i] = i; bestFlipped[i] = false; powerOfTwo *= 2; } // We never need to flip some one of the sublists, // since a TSP circuit is invariant under reversal, // so back off by one power of two. powerOfTwo /= 2; // Do spooky bit twiddling magic to save unneeded // work in the flipping loop. int antiflipMask = 0; for (int i = 0; i < permuteSize; i++) { if (!sublistFlippable[i]) { antiflipMask |= ( 1 << i ) ; } } double bestFitness = Double.MAX_VALUE; Integer [] nextPermutation = null; while ( pg.morePermutations() ) { boolean currentFlips[] = new boolean[permuteSize]; try { nextPermutation = pg.getNext(); } catch (Exception e) { System.out.println ( "caught pg.getNext() throw in TravellerPermuteCitiesWithinASublist" ); } // Loop through the possible flips. for (int flipWord = 0; flipWord < powerOfTwo; flipWord++) { // Skip work for don't flipping care subset. if ( ( flipWord & antiflipMask ) == 0 ) { for (int i = 0; i < permuteSize; i++) { currentFlips[i] = ( ( flipWord & (1 << i) ) != 0 ); } double currentFitness = 0.0D; for (int i = 0; i < permuteSize; i++) { int nextIndex = ( i + 1 ) % permuteSize; currentFitness += world.getDistance ( ( currentFlips[i] ? sublistBeginCities[nextPermutation[i].intValue()] : sublistEndCities[nextPermutation[i].intValue()] ), ( currentFlips[nextIndex] ? sublistEndCities[nextPermutation[nextIndex].intValue()] : sublistBeginCities[nextPermutation[nextIndex].intValue()] ) ); } if (currentFitness < bestFitness) { bestFitness = currentFitness; // Notice that this time we are actually capturing the // permutation rather than what it indexes; we have a // bunch of work to do to construct the final product // mutant at the end of all this foolishness. for (int i = 0; i < permuteSize; i++) { bestPermutation[i] = nextPermutation[i].intValue(); bestFlipped[i] = currentFlips[i]; } } } } } // We are going to scribble on mutant, so use the local name of // the input parameter as an unclobbered data source for city names. // Starting at the beginning of the output chromosome, mutant, // write the sublists in their permuted order, flipped as needed. int writeToIndex = 0; for (int i = 0; i < permuteSize; i++) { int currentCleavageIndicesIndex = bestPermutation[i]; int nextCleavageIndicesIndex = ( currentCleavageIndicesIndex + 1 ) % permuteSize; int currentChromosomeIndex = cleavageIndices[currentCleavageIndicesIndex]; int nextChromosomeIndex = ( cleavageIndices[nextCleavageIndicesIndex] - 1 + genomeLength ) % genomeLength; if ( bestFlipped[i] ) { int j = nextChromosomeIndex; while ( true ) { mutant.setCity( writeToIndex, readOnlyVersion.getCity(j)); writeToIndex++; if ( j == currentChromosomeIndex ) { break; } j = ( j - 1 + genomeLength ) % genomeLength; } } else { int j = currentChromosomeIndex; while ( true ) { mutant.setCity( writeToIndex, readOnlyVersion.getCity(j)); writeToIndex++; if ( j == nextChromosomeIndex ) { break; } j = ( j + 1 ) % genomeLength; } } } mutant.canonicalize(); // System.out.println( mutant.toString() ); double fitnessAtEnd = mutant.testFitness(); // System.out.println( fitnessAtEnd + " fitness at end of bladeful " ); return ( ( fitnessAtStart - fitnessAtEnd ) > TravellerStatus.LITTLE_FUZZ ); } private void pickCleavageIndices ( int cleavageIndices[], int genomeLength, MersenneTwister mt ) { int permuteSize = cleavageIndices.length; for (int i = 0; i < permuteSize; i++) { cleavageIndices[i] = -1; } // fill cleavage points list with unique chromosome array indices for (int i = 0; i < permuteSize; i++) { int indexCandidate = mt.nextInt(genomeLength); while ( inList( indexCandidate, cleavageIndices ) ) { indexCandidate = mt.nextInt(genomeLength); } cleavageIndices[i] = indexCandidate; for (int j = i; j > 0; j--) { // Do a cheesy insertion sort, since this list has // a single digit length. if ( cleavageIndices[j] < cleavageIndices[j - 1] ) { int temp = cleavageIndices[j - 1]; cleavageIndices[j - 1] = cleavageIndices[j]; cleavageIndices[j] = temp; } } } } private void advanceBlade ( int cleavageIndices[], int genomeLength ) { for ( int i = 0; i < cleavageIndices.length; i++ ) { cleavageIndices[i] = ( (cleavageIndices[i] + 1 ) % genomeLength) ; } // System.out.println( Debugging.dump( cleavageIndices ) ); } private void updateProgressDisplay( String update ) { if ( CheckBoxControls.getState(CheckBoxControls.CBC_DEBUG_PROGRESS_COUNTERS) ) { if ( m_progressDisplay == null ) { m_progressDisplay = new SmallDisplay( m_progressDisplayName, SMALL_DISPLAY_WIDTH ); } m_progressDisplay.updateDisplay( update ); } else { if ( m_progressDisplay != null ) { m_progressDisplay.closeWindow(); m_progressDisplay = null; } } }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -