├── .gitignore ├── README.md ├── matrix.txt └── src └── com └── knok16 ├── GeneticFramework ├── FitnessFunction.java └── GeneticEngine.java └── test ├── Main.java └── MyFitnessFunction.java /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | 3 | # Mobile Tools for Java (J2ME) 4 | .mtj.tmp/ 5 | 6 | # Package Files # 7 | *.jar 8 | *.war 9 | *.ear 10 | 11 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 12 | hs_err_pid* 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # genetic-algorithm-example 2 | 3 | Source code for http://habrahabr.ru/post/138091/ article. 4 | 5 | Language: ru. 6 | -------------------------------------------------------------------------------- /src/com/knok16/GeneticFramework/FitnessFunction.java: -------------------------------------------------------------------------------- 1 | package com.knok16.GeneticFramework; 2 | 3 | public interface FitnessFunction { 4 | int getArity(); 5 | long run(long[] genom); 6 | } 7 | -------------------------------------------------------------------------------- /src/com/knok16/GeneticFramework/GeneticEngine.java: -------------------------------------------------------------------------------- 1 | package com.knok16.GeneticFramework; 2 | 3 | import java.util.Random; 4 | 5 | public class GeneticEngine{ 6 | 7 | public long timeToSelection = 0; 8 | public long timeToCrossing = 0; 9 | public long timeToMutate = 0; 10 | public long timeToFF = 0; 11 | 12 | public static final double CHANCE_TO_FULLNESS = 0.999d; 13 | public static final SelectionType DEFAULT_SELECTION_TYPE = SelectionType.TOURNEY; 14 | public static final CrossingType DEFAULT_CROSSING_TYPE = CrossingType.ONE_POINT_RECOMBINATION; 15 | public static final boolean DEFAULT_USE_MUTATION = true; 16 | public static final long DEFAULT_GENERATION_COUNT = 10000L; 17 | 18 | public static final int OCTET_LENGTH = 64; // for long 19 | public static final int MASK_FOR_MOD = OCTET_LENGTH - 1; 20 | public static final int SHIFT_FOR_DIVISION; 21 | static { 22 | int shiftForDivision = 0; 23 | int tmp = OCTET_LENGTH; 24 | while (tmp > 1) { 25 | tmp >>= 1; 26 | shiftForDivision++; 27 | } 28 | SHIFT_FOR_DIVISION = shiftForDivision; 29 | } 30 | 31 | public enum SelectionType { 32 | TOURNEY, ROULETTE_WHEEL 33 | } 34 | 35 | public enum CrossingType { 36 | ONE_POINT_RECOMBINATION, TWO_POINT_RECOMBINATION, ELEMENTWISE_RECOMBINATION, ONE_ELEMENT_EXCHANGE 37 | } 38 | 39 | private FitnessFunction fitnessFunction; 40 | private int genomLength; 41 | private int sizeOfArray; 42 | private long generationCount; 43 | private int individualCount; 44 | private SelectionType selectionType; 45 | private CrossingType crossingType; 46 | private boolean useMutation; 47 | private double mutationPercent; 48 | private long[][] genomListParents; 49 | private long[][] genomListOffsprings; 50 | private long[] actual; 51 | private long[] fitnessFunctionResult; 52 | private long currentGeneration = 0; 53 | 54 | private Random random = new Random(System.currentTimeMillis()); 55 | 56 | public GeneticEngine(FitnessFunction fitnessFunction) { 57 | this.fitnessFunction = fitnessFunction; 58 | this.genomLength = fitnessFunction.getArity(); 59 | this.sizeOfArray = (int) Math.ceil((double) this.genomLength / OCTET_LENGTH); 60 | this.generationCount = DEFAULT_GENERATION_COUNT; 61 | this.individualCount = (int) (1 + Math.log(1 / Math.pow(1 - CHANCE_TO_FULLNESS, 1 / genomLength)) / Math.log(2)); 62 | this.selectionType = DEFAULT_SELECTION_TYPE; 63 | this.crossingType = DEFAULT_CROSSING_TYPE; 64 | this.useMutation = DEFAULT_USE_MUTATION; 65 | this.mutationPercent = genomLength * (1 - Math.pow((1 - 10 * Math.pow((1 / 2), (genomLength - 1))),(1 / genomLength))); 66 | } 67 | 68 | // Main loop 69 | public long[] run() { 70 | //Preparing structuress 71 | this.genomListParents = new long[this.individualCount][]; 72 | this.genomListOffsprings = new long[this.individualCount][]; 73 | this.fitnessFunctionResult = new long[this.individualCount]; 74 | this.actual = new long[this.individualCount]; 75 | for (int i = 0; i < this.individualCount; i++) { 76 | this.actual[i] = -1; 77 | } 78 | 79 | //Generate 1st generation 80 | this.generateFirstGeneration(); 81 | 82 | while (this.currentGeneration < this.generationCount) { 83 | 84 | this.selection(); 85 | this.crossing(); 86 | if (this.useMutation) { 87 | this.mutation(); 88 | } 89 | 90 | long[][] tmp = this.genomListParents; 91 | this.genomListParents = this.genomListOffsprings; 92 | this.genomListOffsprings = tmp; 93 | 94 | this.currentGeneration++; 95 | } 96 | 97 | long bestFitnessFunctionResult = 0; 98 | long[] bestGenom = null; 99 | for (long[] genom : this.genomListParents) { 100 | long fitnessFunctionResult = this.fitnessFunction.run(genom); 101 | if (bestFitnessFunctionResult <= fitnessFunctionResult) { 102 | bestGenom = genom; 103 | bestFitnessFunctionResult = fitnessFunctionResult; 104 | } 105 | } 106 | 107 | return bestGenom; 108 | } 109 | 110 | // Generate First Generation 111 | private void generateFirstGeneration() { 112 | for (int i = 0; i < this.individualCount; i++) { 113 | this.genomListParents[i] = this.generateGenom(); 114 | } 115 | } 116 | 117 | // Generate Genom - Generate 1 genom 118 | private long[] generateGenom() { 119 | long[] result = new long[this.sizeOfArray]; 120 | for (int i = 0; i < this.sizeOfArray; i++) { 121 | result[i] = this.random.nextLong(); 122 | } 123 | return result; 124 | } 125 | 126 | // Selection - Select genoms for crossing 127 | private void selection(){ 128 | long old = System.currentTimeMillis(); // time 129 | 130 | switch (selectionType) { 131 | case ROULETTE_WHEEL:{ 132 | 133 | float[] wheel = new float[this.individualCount]; 134 | wheel[0] = this.getFitnessFunctionResult(0); 135 | for (int i=1;i> 1; 148 | if (wheel[c] fr2 ? this.genomListParents[index1].clone() : this.genomListParents[index2].clone(); 171 | } 172 | break; 173 | } 174 | default: 175 | throw new UnsupportedOperationException(); 176 | } 177 | 178 | this.timeToSelection += (System.currentTimeMillis() - old); // time 179 | } 180 | 181 | // Crossing - Crossing all genom in generation 182 | private void crossing() { 183 | long old = System.currentTimeMillis(); // time 184 | 185 | for (int i = 0; i < individualCount / 2; i++) { 186 | int index1 = i << 1; 187 | int index2 = index1 | 1; 188 | cross(this.genomListOffsprings[index1], this.genomListOffsprings[index2]); 189 | } 190 | 191 | this.timeToCrossing += (System.currentTimeMillis() - old); // time 192 | } 193 | 194 | // Get Fitness Function Result [with cache] 195 | private long getFitnessFunctionResult(int genomNumber) { 196 | if (this.actual[genomNumber] != this.currentGeneration) { 197 | this.fitnessFunctionResult[genomNumber] = this.fitnessFunction.run(this.genomListParents[genomNumber]); 198 | this.actual[genomNumber] = this.currentGeneration; 199 | } 200 | return this.fitnessFunctionResult[genomNumber]; 201 | } 202 | 203 | // Cross - Crossing 2 genom 204 | private void cross(long[] genom1, long[] genom2) { 205 | switch (crossingType) { 206 | case ONE_POINT_RECOMBINATION:{ 207 | int index = this.random.nextInt(this.genomLength); 208 | int outerOffset = index >> SHIFT_FOR_DIVISION; 209 | int innerOffset = OCTET_LENGTH - (index & MASK_FOR_MOD); 210 | long tmp = 0; 211 | 212 | if (innerOffset < 63) { 213 | long mask = 1L << (innerOffset + 1) - 1; 214 | long swapMask = (genom1[outerOffset] ^ genom2[outerOffset]) & mask; 215 | genom1[outerOffset] ^= swapMask; 216 | genom2[outerOffset] ^= swapMask; 217 | outerOffset++; 218 | } 219 | for (int i=outerOffset;i> SHIFT_FOR_DIVISION; 233 | int startInnerOffset = OCTET_LENGTH - (startIndex & MASK_FOR_MOD); 234 | int endOuterOffset = endIndex >> SHIFT_FOR_DIVISION; 235 | int endInnerOffset = OCTET_LENGTH - (endIndex & MASK_FOR_MOD); 236 | long tmp = 0; 237 | 238 | if (startInnerOffset < OCTET_LENGTH-1) { 239 | long mask = 1L << (startInnerOffset + 1) - 1; 240 | long swapMask = (genom1[startOuterOffset] ^ genom2[startOuterOffset]) & mask; 241 | genom1[startOuterOffset] ^= swapMask; 242 | genom2[startOuterOffset] ^= swapMask; 243 | startOuterOffset++; 244 | } 245 | for (int i=startOuterOffset;i<=endOuterOffset;i++){ 246 | tmp = genom1[i]; 247 | genom1[i] = genom2[i]; 248 | genom2[i] = tmp; 249 | } 250 | if (endInnerOffset > 0) { 251 | long mask = 1L << endInnerOffset - 1; 252 | long swapMask = (genom1[endOuterOffset] ^ genom2[endOuterOffset]) & mask; 253 | genom1[endOuterOffset] ^= swapMask; 254 | genom2[endOuterOffset] ^= swapMask; 255 | } 256 | 257 | break; 258 | } 259 | case ELEMENTWISE_RECOMBINATION:{ 260 | for (int outerOffset = 0; outerOffset < this.sizeOfArray; outerOffset++) { 261 | long mask = this.random.nextLong(); 262 | long swapMask = (genom1[outerOffset] ^ genom2[outerOffset]) & mask; 263 | 264 | genom1[outerOffset] ^= swapMask; 265 | genom2[outerOffset] ^= swapMask; 266 | } 267 | break; 268 | } 269 | case ONE_ELEMENT_EXCHANGE:{ 270 | int index = this.random.nextInt(this.genomLength); 271 | int outerOffset = index >> SHIFT_FOR_DIVISION; 272 | int innerOffset = OCTET_LENGTH - (index & MASK_FOR_MOD); 273 | long mask = 1L << innerOffset; 274 | long swapMask = (genom1[outerOffset] ^ genom2[outerOffset]) & mask; 275 | 276 | genom1[outerOffset] ^= swapMask; 277 | genom2[outerOffset] ^= swapMask; 278 | break; 279 | } 280 | default: 281 | throw new UnsupportedOperationException(); 282 | } 283 | } 284 | 285 | // Mutation - Mutate all genom in generation 286 | private void mutation() { 287 | long old = System.currentTimeMillis(); // time 288 | 289 | for (long[] genom : this.genomListOffsprings) { 290 | if (random.nextDouble() <= mutationPercent) { 291 | mutate(genom); 292 | } 293 | } 294 | 295 | this.timeToMutate += (System.currentTimeMillis() - old); // time 296 | } 297 | 298 | // Mutate - Mutate 1 genom 299 | private void mutate(long[] genom) { 300 | int index = this.random.nextInt(this.genomLength); 301 | int outerOffset = index >> SHIFT_FOR_DIVISION; 302 | int innerOffset = (index & MASK_FOR_MOD); 303 | long mask = 1L << innerOffset; 304 | genom[outerOffset] ^= mask; 305 | } 306 | 307 | // Setters and Getters 308 | public long getGenerationCount() { 309 | return generationCount; 310 | } 311 | 312 | public void setGenerationCount(long generationCount) { 313 | this.generationCount = generationCount; 314 | } 315 | 316 | public int getIndividualCount() { 317 | return individualCount; 318 | } 319 | 320 | public void setIndividualCount(int individualCount) { 321 | this.individualCount = individualCount; 322 | } 323 | 324 | public SelectionType getSelectionType() { 325 | return selectionType; 326 | } 327 | 328 | public void setSelectionType(SelectionType selectionType) { 329 | this.selectionType = selectionType; 330 | } 331 | 332 | public CrossingType getCrossingType() { 333 | return crossingType; 334 | } 335 | 336 | public void setCrossingType(CrossingType crossingType) { 337 | this.crossingType = crossingType; 338 | } 339 | 340 | public boolean getUseMutation() { 341 | return useMutation; 342 | } 343 | 344 | public void setUseMutation(boolean useMutation) { 345 | this.useMutation = useMutation; 346 | } 347 | 348 | public double getMutationPercent() { 349 | return mutationPercent; 350 | } 351 | 352 | public void setMutationPercent(double mutationPercent) { 353 | this.mutationPercent = mutationPercent; 354 | } 355 | } -------------------------------------------------------------------------------- /src/com/knok16/test/Main.java: -------------------------------------------------------------------------------- 1 | package com.knok16.test; 2 | 3 | import java.io.IOException; 4 | 5 | import com.knok16.GeneticFramework.GeneticEngine; 6 | 7 | public class Main { 8 | 9 | public static void main(String[] args) throws IOException{ 10 | //MyFitnessFunction.generateRandomFile("matrix.txt", 256); 11 | MyFitnessFunction ff = new MyFitnessFunction("matrix.txt"); 12 | GeneticEngine ge = new GeneticEngine(ff); 13 | ge.setIndividualCount(100); 14 | ge.setGenerationCount(10000); 15 | ge.setSelectionType(GeneticEngine.SelectionType.TOURNEY); 16 | ge.setCrossingType(GeneticEngine.CrossingType.ELEMENTWISE_RECOMBINATION); 17 | ge.setUseMutation(true); 18 | ge.setMutationPercent(0.02d); 19 | 20 | long time = System.currentTimeMillis(); 21 | long[] better = ge.run(); 22 | 23 | long timeToFF = ge.timeToFF; 24 | long timeToSelection = ge.timeToSelection; 25 | long timeToCrossing = ge.timeToCrossing; 26 | long timeToMutate = ge.timeToMutate; 27 | 28 | System.out.println("Running:\t"+(System.currentTimeMillis()-time)/1000 + " secs"); 29 | System.out.println("FitnessFunc:\t"+timeToFF/1000 + " secs"); 30 | System.out.println(" - FF Prepare:\t"+ff.prepareTime/1000 + " secs"); 31 | System.out.println(" - FF QSort:\t"+ff.sortingTime/1000 + " secs"); 32 | System.out.println(" - FF Check: \t"+ff.checkTime/1000 + " secs"); 33 | System.out.println("Selection:\t"+(timeToSelection-timeToFF)/1000 + " secs"); 34 | System.out.println("Crossing:\t"+timeToCrossing/1000 + " secs"); 35 | System.out.println("Mutate: \t"+timeToMutate/1000 + " secs"); 36 | System.out.println(Long.MAX_VALUE-ff.run(better)); 37 | } 38 | 39 | private static void printLongInBin(long l, int last){ 40 | if (last>0){ 41 | int p = (int)(l & 1); 42 | printLongInBin(l>>1,--last); 43 | System.out.print(p); 44 | } 45 | } 46 | 47 | } -------------------------------------------------------------------------------- /src/com/knok16/test/MyFitnessFunction.java: -------------------------------------------------------------------------------- 1 | package com.knok16.test; 2 | 3 | import java.io.BufferedWriter; 4 | import java.io.FileNotFoundException; 5 | import java.io.FileReader; 6 | import java.io.FileWriter; 7 | import java.io.IOException; 8 | import java.util.Random; 9 | import java.util.Scanner; 10 | 11 | import com.knok16.GeneticFramework.FitnessFunction; 12 | 13 | public class MyFitnessFunction implements FitnessFunction { 14 | 15 | public long prepareTime = 0; 16 | public long checkTime = 0; 17 | public long sortingTime = 0; 18 | 19 | private static final int BIT_TO_INT = 8; 20 | private int pathLength = 0; 21 | private int[] path = null; 22 | private int[] seq = null; 23 | private int vertexCount; 24 | private int[][] matrix; 25 | 26 | 27 | public MyFitnessFunction(String filename) throws FileNotFoundException{ 28 | super(); 29 | Scanner in = new Scanner(new FileReader(filename)); 30 | this.vertexCount = in.nextInt(); 31 | this.matrix = new int[this.vertexCount][this.vertexCount]; 32 | for(int i=0;i>= 8; 67 | } 68 | } 69 | 70 | this.prepareTime += (System.currentTimeMillis()-old); //time 71 | old = System.currentTimeMillis(); //time 72 | 73 | qsort(this.path,this.seq,0,this.pathLength-1); 74 | 75 | this.sortingTime += (System.currentTimeMillis()-old); //time 76 | old = System.currentTimeMillis(); //time 77 | 78 | long pathLength = this.checkPath(this.seq); 79 | 80 | this.checkTime += (System.currentTimeMillis()-old); //time 81 | 82 | return (Long.MAX_VALUE-pathLength); 83 | } 84 | 85 | private void qsort(int[] arrayToSort, int[] arrayToMix,int l, int r){ 86 | int i = l; 87 | int j = r; 88 | int tmp = 0; 89 | int pivot = arrayToSort[(l+r)>>1]; 90 | 91 | while (i <= j) { 92 | while (arrayToSort[i] < pivot) {i+=1;} 93 | while (arrayToSort[j] > pivot) {j-=1;} 94 | 95 | if (i <= j) { 96 | tmp = arrayToSort[i]; 97 | arrayToSort[i] = arrayToSort[j]; 98 | arrayToSort[j] = tmp; 99 | tmp = arrayToMix[i]; 100 | arrayToMix[i] = arrayToMix[j]; 101 | arrayToMix[j] = tmp; 102 | i+=1; 103 | j-=1; 104 | } 105 | } 106 | if (l < j){ 107 | qsort(arrayToSort, arrayToMix, l, j); 108 | } 109 | if (i < r){ 110 | qsort(arrayToSort, arrayToMix, i, r); 111 | } 112 | } 113 | 114 | public long checkPath(int[] path){ 115 | 116 | long result = 0; 117 | int pathLength = path.length; 118 | int predVertex = path[0]; 119 | int nextVertex = 0; 120 | 121 | for (int i=1;i