diff --git a/best/Main.java b/best/Main.java new file mode 100644 index 0000000..2344403 --- /dev/null +++ b/best/Main.java @@ -0,0 +1,375 @@ +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +public class Main { + // Initalizing variables + public static int POPULATIONSIZE = 0; + public static int TOURNAMENTSIZE = 0; + public static double MUTATIONRATE = 0; + // Threshold of number of mutable positions for easy level sudoku + public static int EASYTHRESHOLD = 60; + public static int HARDTHRESHOLD = 70; + // List to store the population of chromosomes + List population = new ArrayList<>(); + Random random = new Random(); + + public static void main(String[] args) { + // Base Sudoku matrix (input matrix) + int[][] baseSudoku = new int[9][9]; + // List to track positions in Sudoku that are mutable + List mutablePositions = new ArrayList<>(); + BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); + + try { + // Reading the Sudoku matrix from the console input + for (int i = 0; i < 9; i++) { + // Split the input by space + String[] tokens = reader.readLine().split(" "); + for (int j = 0; j < 9; j++) { + if (tokens[j].equals("-")) { + // Empty cells are marked as 0 + baseSudoku[i][j] = 0; + // Add mutable positions (i, j) to the list + mutablePositions.add(new int[]{i, j}); + } else { + // Set fixed value from the input + baseSudoku[i][j] = Integer.parseInt(tokens[j]); + } + } + } + } catch (IOException e) { + // If an error occurs during input reading, print the error and stop the program + System.err.println("Error reading input: " + e.getMessage()); + return; + } + + Main mainInstance = new Main(); // Create instance of Main class + + // Choosing variables for different sudoku difficulties + if (mutablePositions.size() < EASYTHRESHOLD) { // Easy sudoku + POPULATIONSIZE = 100; + TOURNAMENTSIZE = 5; + MUTATIONRATE = 0.05; + } else if (mutablePositions.size() < HARDTHRESHOLD) { // Hard sudoku + POPULATIONSIZE = 100; + TOURNAMENTSIZE = 5; + MUTATIONRATE = 0.057; + } + else { // Ultra-hard sudoku + POPULATIONSIZE = 20000; + TOURNAMENTSIZE = 4; + MUTATIONRATE = 0.032; + } + + // Generate initial population of 100 chromosomes + mainInstance.generateInitialChromosomes(POPULATIONSIZE, baseSudoku, mutablePositions); + + Chromosome bestSolution = null; + + int generation = 0; // Track the number of generations + while (true) { + // Evaluate the fitness of each chromosome in the population + mainInstance.evaluatePopulation(); + + List newPopulation = new ArrayList<>(); + for (int i = 0; i < mainInstance.population.size() / 2; i++) { + // Select parents using tournament selection + List parents = mainInstance.tournamentSelection(TOURNAMENTSIZE); + // Perform crossover to create two children from the selected parents + Chromosome child1 = mainInstance.crossoverBySubgrids(parents.get(0), parents.get(1)); + Chromosome child2 = mainInstance.crossoverBySubgrids(parents.get(1), parents.get(0)); + + // Apply mutation to both children + mainInstance.mutateChromosome(child1, MUTATIONRATE); + mainInstance.mutateChromosome(child2, MUTATIONRATE); + + // Add both children to the new population + newPopulation.add(child1); + newPopulation.add(child2); + } + + // Replace the old population with the new population + mainInstance.population = newPopulation; + + // Get the best chromosome from the current population + bestSolution = mainInstance.getBestChromosome(); + generation++; + + // If the best solution found has a fitness of 0, print it and end the program + if (bestSolution.getFitness() == 0) { + bestSolution.printChromosome(false); + return; + } + } + } + + // Generate initial population of chromosomes + public void generateInitialChromosomes(int numberOfChromosomes, int[][] baseSudoku, List mutablePositions) { + for (int i = 0; i < numberOfChromosomes; i++) { + // Create a copy of the base Sudoku + int[][] sudoku = copyMatrix(baseSudoku); + // Randomly fill mutable positions + for (int[] pos : mutablePositions) { + int row = pos[0]; + int col = pos[1]; + sudoku[row][col] = random.nextInt(9) + 1; + } + // Create a new chromosome with the generated Sudoku and mutable positions + Chromosome chromosome = new Chromosome(sudoku, new ArrayList<>(mutablePositions)); + chromosome.evaluateFitness(); // Evaluate its fitness + population.add(chromosome); // Add to the population + } + } + + // Create a deep copy of a matrix + private int[][] copyMatrix(int[][] original) { + int[][] copy = new int[original.length][original[0].length]; + for (int i = 0; i < original.length; i++) { + System.arraycopy(original[i], 0, copy[i], 0, original[i].length); + } + return copy; + } + + + public List tournamentSelection(int tournamentSize) { + List selectedParents = new ArrayList<>(); + for (int i = 0; i < 2; i++) { + List tournament = new ArrayList<>(); + // Randomly select chromosomes for the tournament + for (int j = 0; j < tournamentSize; j++) { + Chromosome randomChromosome = population.get(random.nextInt(population.size())); + tournament.add(randomChromosome); + } + // Determine the best chromosome in the tournament based on fitness + Chromosome best = tournament.get(0); + for (Chromosome chromosome : tournament) { + if (chromosome.getFitness() < best.getFitness()) { + best = chromosome; + } + } + // Add the best chromosome to the list of selected parents + selectedParents.add(best); + } + return selectedParents; + } + + public Chromosome crossoverBySubgrids(Chromosome parent1, Chromosome parent2) { + int[][] childSudoku = new int[9][9]; + + // Copy the entire Sudoku grid from parent1 to the child + for (int row = 0; row < 9; row++) { + System.arraycopy(parent1.getSudoku()[row], 0, childSudoku[row], 0, 9); + } + + // Determine the number of subgrids to swap from parent2 to child (1-5) + int numSubgridsToSwap = random.nextInt(5) + 1; + List selectedSubgrids = new ArrayList<>(); + while (selectedSubgrids.size() < numSubgridsToSwap) { + int subgridIndex = random.nextInt(9); + if (!selectedSubgrids.contains(subgridIndex)) { + selectedSubgrids.add(subgridIndex); + } + } + + // Swap the selected subgrids from parent2 into the child + for (int subgrid : selectedSubgrids) { + int rowStart = (subgrid / 3) * 3; + int colStart = (subgrid % 3) * 3; + for (int row = rowStart; row < rowStart + 3; row++) { + for (int col = colStart; col < colStart + 3; col++) { + childSudoku[row][col] = parent2.getSudoku()[row][col]; + } + } + } + + // Create a new chromosome with the resulting child Sudoku and evaluate its fitness + Chromosome child = new Chromosome(childSudoku, parent1.getMutablePositions()); + child.evaluateFitness(); + return child; + } + + public void printPopulation(boolean printMutPos) { + System.out.println("Generated Population:"); + int count = 1; + // Print each chromosome's Sudoku and fitness value + for (Chromosome chromosome : population) { + System.out.println("Chromosome " + count + ":"); + chromosome.printChromosome(printMutPos); + System.out.println("Fitness: " + chromosome.getFitness()); + System.out.println(); + count++; + } + } + + public void mutateChromosome(Chromosome chromosome, double mutationRate) { + int[][] sudoku = chromosome.getSudoku(); + List mutablePositions = chromosome.getMutablePositions(); + + // Mutate each mutable position with a probability defined by mutationRate + for (int[] pos : mutablePositions) { + if (random.nextDouble() < mutationRate) { + int row = pos[0]; + int col = pos[1]; + int newValue = random.nextInt(9) + 1; // Assign a new value between 1 and 9 + sudoku[row][col] = newValue; + } + } + + // Update the chromosome's Sudoku and recalculate its fitness + chromosome.setSudoku(sudoku); + chromosome.evaluateFitness(); + } + + public void evaluatePopulation() { + // Evaluate the fitness of each chromosome in the population + for (Chromosome chromosome : population) { + chromosome.evaluateFitness(); + } + } + + public Chromosome getBestChromosome() { + // Find and return the chromosome with the best (lowest) fitness in the population + Chromosome best = population.get(0); + for (Chromosome chromosome : population) { + if (chromosome.getFitness() < best.getFitness()) { + best = chromosome; + } + } + return best; + } + + // Chromosome class representing an individual solution + public class Chromosome { + private int[][] sudoku; // Sudoku grid representing the chromosome + private List mutablePositions; // Positions that can be changed (mutable) + private int fitness; // Fitness value representing the number of conflicts + + // Constructor for initializing a Chromosome with a Sudoku grid and mutable positions + public Chromosome(int[][] sudoku, List mutablePositions) { + this.sudoku = sudoku; + this.mutablePositions = mutablePositions; + } + + public int[][] getSudoku() { + return sudoku; + } + + public void setSudoku(int[][] sudoku) { + this.sudoku = sudoku; + } + + public List getMutablePositions() { + return mutablePositions; + } + + public int getFitness() { + return fitness; + } + + // Evaluate the fitness of the Sudoku by counting the number of row, column, and subgrid violations + public void evaluateFitness() { + fitness = countRowViolations() + countColumnViolations() + countSubgridViolations(); + } + + private int countRowViolations() { + int violations = 0; + // Iterate through each row to count conflicts + for (int i = 0; i < 9; i++) { + boolean[] present = new boolean[10]; + for (int j = 0; j < 9; j++) { + int value = sudoku[i][j]; + if (value != 0) { + if (present[value]) { + violations++; // Increment violations if the value is already seen + } else { + present[value] = true; // Mark the value as seen + } + } + } + } + return violations; + } + private int countColumnViolations() { + int violations = 0; + // Loop through each column + for (int j = 0; j < 9; j++) { + boolean[] present = new boolean[10]; // Track numbers present in the column + for (int i = 0; i < 9; i++) { + int value = sudoku[i][j]; + if (value != 0) { + // If the number is already present, increment the violations count + if (present[value]) { + violations++; + } else { + // Mark the number as present + present[value] = true; + } + } + } + } + return violations; + } + + private int countSubgridViolations() { + int violations = 0; + // Loop through each 3x3 subgrid + for (int gridRow = 0; gridRow < 3; gridRow++) { + for (int gridCol = 0; gridCol < 3; gridCol++) { + boolean[] present = new boolean[10]; // Track numbers present in the subgrid + // Loop through cells in the 3x3 subgrid + for (int row = gridRow * 3; row < gridRow * 3 + 3; row++) { + for (int col = gridCol * 3; col < gridCol * 3 + 3; col++) { + int value = sudoku[row][col]; + if (value != 0) { + // If the number is already present, increment the violations count + if (present[value]) { + violations++; + } else { + // Mark the number as present + present[value] = true; + } + } + } + } + } + } + return violations; + } + + public void printChromosome(boolean printMutPos) { + // Print the Sudoku matrix + for (int[] row : sudoku) { + for (int j = 0; j < row.length; j++) { + System.out.print(row[j]); + if (j < row.length - 1) { + System.out.print(" "); + } + } + System.out.println(); + } + // If requested, print mutable positions + if (printMutPos) { + System.out.println("Mutable Positions:"); + for (int i = 0; i < mutablePositions.size(); i++) { + int[] pos = mutablePositions.get(i); + System.out.print("(" + pos[0] + ", " + pos[1] + ")"); + if (i < mutablePositions.size() - 1) { + System.out.print(" "); + } + } + System.out.println(); + } + } + } +} + + + + + + + diff --git a/other/Main.java b/other/Main.java index 06e48f9..82ab558 100644 --- a/other/Main.java +++ b/other/Main.java @@ -69,12 +69,24 @@ public class Main { mainInstance.generateInitialChromosomes(POPULATIONSIZE, baseSudoku, mutablePositions); Chromosome bestSolution = null; + List fitnessValues = new ArrayList<>(); + + int stuckCounter = 0; + int tryUnstuckCount = 0; + boolean unstucking = false; + + int INITIAL_POPULATIONSIZE = POPULATIONSIZE; + int INITIAL_TOURNAMENTSIZE = TOURNAMENTSIZE; + double INITIAL_MUTATIONRATE = MUTATIONRATE; int generation = 0; // Track the number of generations while (true) { // Evaluate the fitness of each chromosome in the population mainInstance.evaluatePopulation(); + bestSolution = mainInstance.getBestChromosome(); + fitnessValues.add(bestSolution.getFitness()); + List newPopulation = new ArrayList<>(); for (int i = 0; i < mainInstance.population.size() / 2; i++) { // Select parents using tournament selection @@ -104,6 +116,38 @@ public class Main { bestSolution.printChromosome(false); return; } + + //change arguments when algorithm is stuck + if (fitnessValues.size() > 2) { + if (fitnessValues.get(fitnessValues.size()-1) == fitnessValues.get(fitnessValues.size()-2)){ + stuckCounter++; + //System.out.println("StuckCount: " + String.valueOf(stuckCounter)); + if (stuckCounter > 50){ + if (MUTATIONRATE < 1){ + unstucking = true; + System.out.println(MUTATIONRATE); + System.out.println(POPULATIONSIZE); + MUTATIONRATE = MUTATIONRATE * 1.1 + 0.01; + POPULATIONSIZE /= 2; + } + stuckCounter = 0; + } + } else{ + stuckCounter = 0; + } + } + if (unstucking) { + //System.out.println("Unstucking"); + tryUnstuckCount++; + if (tryUnstuckCount > 100){ + unstucking = false; + tryUnstuckCount = 0; + POPULATIONSIZE = INITIAL_POPULATIONSIZE; + TOURNAMENTSIZE = INITIAL_TOURNAMENTSIZE; + MUTATIONRATE = INITIAL_MUTATIONRATE; + } + } + //System.out.println("UnstuckCount: " + String.valueOf(tryUnstuckCount)); } } diff --git a/statistical/demo/src/main/java/com/example/Main.java b/statistical/demo/src/main/java/com/example/Main.java index edf5245..6b10dd6 100644 --- a/statistical/demo/src/main/java/com/example/Main.java +++ b/statistical/demo/src/main/java/com/example/Main.java @@ -68,7 +68,7 @@ public class Main { else { // Ultra-hard sudoku POPULATIONSIZE = 100000; TOURNAMENTSIZE = 10; - MUTATIONRATE = 0.1; + MUTATIONRATE = 0.0; } // Generate initial population of chromosomes @@ -78,6 +78,17 @@ public class Main { List fitnessValues = new ArrayList<>(); int generation = 0; // Track the number of generations + int stuckCounter = 0; + int tryUnstuckCount = 0; + boolean unstucking = false; + + int INITIAL_POPULATIONSIZE = POPULATIONSIZE; + int INITIAL_TOURNAMENTSIZE = TOURNAMENTSIZE; + double INITIAL_MUTATIONRATE = MUTATIONRATE; + + + + while (true) { // Evaluate the fitness of each chromosome in the population mainInstance.evaluatePopulation(); @@ -112,13 +123,46 @@ public class Main { if (generation % 10 == 0) { mainInstance.plotFitness(fitnessValues); } - + // If the best solution found has a fitness of 0, print it and end the program if (bestSolution.getFitness() == 0) { bestSolution.printChromosome(false); return; } - } + /* + // Check if algorithm is stuck + if (fitnessValues.size() > 2) { + if (fitnessValues.get(fitnessValues.size()-1) == fitnessValues.get(fitnessValues.size()-2)){ + stuckCounter++; + System.out.println("StuckCount: " + stuckCounter); + if (stuckCounter > 20){ + if (MUTATIONRATE < 1){ + unstucking = true; + System.out.println(MUTATIONRATE); + System.out.println(POPULATIONSIZE); + MUTATIONRATE = MUTATIONRATE * 1.1 + 0.01; + POPULATIONSIZE /= 2; + TOURNAMENTSIZE /= 2; + } + stuckCounter = 0; + } + } else { + stuckCounter = 0; + } + } + if (unstucking) { + System.out.println("Unstucking"); + tryUnstuckCount++; + if (tryUnstuckCount > 100){ + unstucking = false; + tryUnstuckCount = 0; + POPULATIONSIZE = INITIAL_POPULATIONSIZE; + TOURNAMENTSIZE = INITIAL_TOURNAMENTSIZE; + MUTATIONRATE = INITIAL_MUTATIONRATE; + } + } + System.out.println("UnstuckCount: " + tryUnstuckCount); + }*/ } // Generate initial population of chromosomes diff --git a/statistical/demo/target/classes/com/example/Main$Chromosome.class b/statistical/demo/target/classes/com/example/Main$Chromosome.class index 815de1c..b054448 100644 Binary files a/statistical/demo/target/classes/com/example/Main$Chromosome.class and b/statistical/demo/target/classes/com/example/Main$Chromosome.class differ diff --git a/statistical/demo/target/classes/com/example/Main.class b/statistical/demo/target/classes/com/example/Main.class index f46315d..b64674b 100644 Binary files a/statistical/demo/target/classes/com/example/Main.class and b/statistical/demo/target/classes/com/example/Main.class differ