2

I've tried this myself and have looked everywhere for an answer and can't seem to find a suitable workaround. I've found many answers on this site before so I've signed up to ask my question. I have a program that loops through 6 users and assign them to a position. Users: Alpha, bravo, charlie, delta, echo and frank. Positions: 1,2,3,4,5,6. I'm generating random numbers to assign the user to their position. I have one rule that I am checking for; no user can be assigned to the same position twice. Because of this rule and the random assignment of the users to stations, sometimes the only user left for position 6 is a user that has already been assigned to position 6 in a previous rotation. When this happens my program crashes and rightfully so. How do I overcome this? I've had instances where the program will make the right choices and I have 6 successful rotations. I would like to have 6 successful rotations every time. I have spent two week trying to figure this out. Any help I can get would be greatly appreciated. Thank you. I've pasted my code below. I'm using Java.

import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import java.awt.Color;
import java.awt.CardLayout;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.JButton;


public class test extends JFrame implements ActionListener {

    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    private JPanel contentPane;

    /**
     * Launch the application.
     */
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    test frame = new test();
                    frame.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    /**
     * Create the frame.
     */



    //TODO  
    int randnum[] = new int [7];
    int numOfRotations = 1;
    String[] String_CurrentOperator = new String[7];
    Random rand_opers = new Random();

    List<Integer> ArrayList_UsedRandNums = new ArrayList<>();
    List<List<String>> ArrayList_MainOperatorHistory = new ArrayList<List<String>>();
    List<String> ArrayList_AllOpers = new ArrayList<>();
    List<String> ArrayList_UsedOpers = new ArrayList<>();
    List<String> ArrayList_OperatorHistory1 = new ArrayList<>(); 
    List<String> ArrayList_OperatorHistory2 = new ArrayList<>(); 
    List<String> ArrayList_OperatorHistory3 = new ArrayList<>(); 
    List<String> ArrayList_OperatorHistory4 = new ArrayList<>(); 
    List<String> ArrayList_OperatorHistory5 = new ArrayList<>(); 
    List<String> ArrayList_OperatorHistory6 = new ArrayList<>(); 


    public test() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setBounds(100, 100, 450, 300);
        contentPane = new JPanel();
        contentPane.setBackground(Color.DARK_GRAY);
        contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
        setContentPane(contentPane);
        contentPane.setLayout(new CardLayout(0, 0));

        JButton btnShuffle = new JButton("SHUFFLE");
        contentPane.add(btnShuffle, "name_253561263644851");
        btnShuffle.addActionListener(this);

        ArrayList_AllOpers.add("POSITION ZERO");
        ArrayList_AllOpers.add("Alpha");
        ArrayList_AllOpers.add("Bravo");
        ArrayList_AllOpers.add("Charlie");
        ArrayList_AllOpers.add("Delta");
        ArrayList_AllOpers.add("Echo");
        ArrayList_AllOpers.add("Frank");

        ArrayList_MainOperatorHistory.add(ArrayList_OperatorHistory1);
        ArrayList_MainOperatorHistory.add(ArrayList_OperatorHistory1);
        ArrayList_MainOperatorHistory.add(ArrayList_OperatorHistory2);
        ArrayList_MainOperatorHistory.add(ArrayList_OperatorHistory3);
        ArrayList_MainOperatorHistory.add(ArrayList_OperatorHistory4);
        ArrayList_MainOperatorHistory.add(ArrayList_OperatorHistory5);
        ArrayList_MainOperatorHistory.add(ArrayList_OperatorHistory6);
    }


    public void actionPerformed(ActionEvent arg0) {

        /*******************************
         *****ASSIGN ALL OPERATORS******
         *******************************/
        System.out.println("THIS IS ROTATION "+numOfRotations);
        genRandoms();

        for(int i=1; i<6+1; i++){
            do{
                //randnum[i] = rand_opers.nextInt(6)+1;
                String_CurrentOperator[i] = ArrayList_AllOpers.get(randnum[i]);
            }
            while(ArrayList_UsedOpers.contains(String_CurrentOperator[i]) || ArrayList_MainOperatorHistory.get(i).contains(String_CurrentOperator[i]));

            ArrayList_UsedOpers.add(String_CurrentOperator[i]); //add to used names thus far
        }


        for(int i=1; i<6+1; i++){
            System.out.println(String_CurrentOperator[i]);                       //Prints the name of the operator working on the station               
            ArrayList_MainOperatorHistory.get(i).add(String_CurrentOperator[i]); //adds operator to list of all users who have been assigned to this station
        }

        //Perform cleanup actions for next iteration of the loop
        numOfRotations++;               //increment the rotation count by 1
        ArrayList_UsedRandNums.clear(); //clear the list of randum numbers used
        ArrayList_UsedOpers.clear();    //clear the list of assigned operators
        System.out.println("");
    }   


    public void genRandoms(){
        for(int i=1; i<6+1; i++){
            do{
                randnum[i] = rand_opers.nextInt(6)+1;
            }
            while (ArrayList_UsedRandNums.contains(randnum[i]));

            ArrayList_UsedRandNums.add(randnum[i]);   //add randnum[i] to list of random numbers used thus far
        }
    }
}
weston
  • 54,145
  • 21
  • 145
  • 203
theonedgo
  • 21
  • 2
  • 1
    There are two variants of `shuffle` in `Collections`. Could you just use that? http://docs.oracle.com/javase/6/docs/api/java/util/Collections.html – Brick Feb 15 '17 at 20:09
  • What is `ArrayList_AllOpers.add("POSITION ZERO");`? It looks like you're trying to avoid the zero-based nature of arrays. Don't, you need to embrace it. – weston Feb 15 '17 at 20:15
  • Wow all these responses already! Amazing. I will look through and try them and report back. Thanks a lot! And weston you are correct. I deliberately tried to avoid the zero-based nature this time. I tend to embrace it more often than not though. – theonedgo Feb 15 '17 at 22:32

3 Answers3

1

The problem can be reformulated as follows:

You have a 6x6 grid/matrix:

   1 2 3 4 5 6
r1 A B C D E F
r2 F A B C D E
r3 E F A B C D
r4 D E F A B C
r5 C D E F A B
r6 B C D E F A

with the columns being the seat positions, the rows being the rotations and the cell values being Alpha, Beta, Charlie,... You want to shuffle this grid randomly without violating the constraint that in each row and column a value occurs only once.

This is similar to shuffling a Sudoku grid.

This task is not too trivial. Here is an answer that gives a solution using backtracking: How to Generate a -complete- sudoku board

A simple solution could be to randomly swap rows and columns of the initial grid.

Here is my attempt for a hopefully bugfree solution featuring backtracking: The rows are initially shuffled using the Fisher-Yates algorithm. Then the grid is validated top-down using backtracking.

public static int[][] fillGrid(Random random, int size){
    int[][] grid = new int[size][size];

    do{
        for(int row = 0; row < size; row++){
            grid[row] = IntStream.rangeClosed(1, size).toArray();
            shuffleArray(grid[row], random); //randomize row                
        }
    }while(!validate(grid, 0, 0)); //defensive: reroll if failed to validate (should not happen)

    return grid;
}

private static boolean validate(int[][] grid, int row, int col){        
    if(col == grid.length){     
        if(row == grid.length-1){
            //end of grid reached -> whole grid is valid
            return true;
        }
        //end of valid row reached -> validate next row         
        return validate(grid, row + 1, 0);
    }       
    //search for a value that makes the whole row valid
    for(int i = col; i < grid.length; i++){
        swap(grid[row], i, col); //try next value

        //check if the current column is valid
        boolean validCol = true;
        for(int j = 0; j < row; j++){
            if(grid[j][col] == grid[row][col]){
                validCol = false;
                break;
            }
        }

        if (validCol && validate(grid, row, col + 1)){
            //the whole row is valid
            return true;
        }   
        swap(grid[row], i, col); //value does not fit -> swap back
    }   
    //the row is invalid        
    return false;
}

private static void shuffleArray(int[] array, Random random){
    for (int i1 = array.length - 1; i1 > 0; i1--){
      int i2 = random.nextInt(i1 + 1);
      swap(array, i1, i2);
    }
}

private static void swap(int[] array, int i1, int i2){
    int temp = array[i1];
    array[i1] = array[i2];
    array[i2] = temp;
}

The method call fillGrid(new Random(), 6) will give you a shuffled grid that fulfills the constraints. You can access the values with [numOfRotations-1][i-1] in each iteration inside your for-loop. No ArrayList_UsedOpers or ArrayList_MainOperatorHistory used needed.

Community
  • 1
  • 1
Calculator
  • 2,769
  • 1
  • 13
  • 18
  • I like it, but why is the simple solution low quality? – weston Feb 16 '17 at 01:24
  • 1
    @weston Applying the swapping method on sudokus results in a badly randomized grid. However, this seems to be only the case, because of the additional contraint for the 3x3 boxes. So, it is indeed probably a good solution for this simpler kind of problem. For now, I made an attempt using backtracking. – Calculator Feb 16 '17 at 02:52
  • Wow Calculator! Thank you for you hard work, this looks like the solution I'll have to use, I was hoping I just couldn't see the solution and hoping it was only a couple lines of code to fix lol, but backtracking in this case is the best approach. Thank you again for your post. – theonedgo Feb 16 '17 at 21:54
0

I think that your logic seems correct. However, the arrays are zero based, so that's why you have the problem. Try to change your two for loops like this:

for(int i=0; i<6; i++){
JFPicard
  • 5,029
  • 3
  • 19
  • 43
0

I've come up with this solution that has worked every time so far. Thank you for all the feedback. It might not be the most efficient code, but certainly works as expected. I've included some tweaks to allow for calculations of up to 10 rotations. It really struggles with that one though. Hopefully someone can find this useful.

import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import java.awt.Color;
import java.awt.CardLayout;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.JButton;


public class test extends JFrame implements ActionListener {

    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    private JPanel contentPane;

    /**
     * Launch the application.
     */
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    test frame = new test();
                    frame.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    /**
     * Create the frame.
     */



    //TODO  
    int numOfStations = 6;
    boolean cleared[] = new boolean[21];
    boolean finalCleared = false;
    int randnum[] = new int [numOfStations+1];
    int numOfRotations = 1;
    String[] String_CurrentOperator = new String[numOfStations+1];
    Random rand_opers = new Random();

    List<Integer> ArrayList_UsedRandNums = new ArrayList<>();
    List<List<String>> ArrayList_MainOperatorHistory = new ArrayList<List<String>>();
    List<String> ArrayList_AllOpers = new ArrayList<>();
    List<String> ArrayList_CurrentRotationOperators = new ArrayList<>();
    List<String> ArrayList_UsedOpers = new ArrayList<>();
    List<String> ArrayList_OperatorHistory1 = new ArrayList<>(); 
    List<String> ArrayList_OperatorHistory2 = new ArrayList<>(); 
    List<String> ArrayList_OperatorHistory3 = new ArrayList<>(); 
    List<String> ArrayList_OperatorHistory4 = new ArrayList<>(); 
    List<String> ArrayList_OperatorHistory5 = new ArrayList<>(); 
    List<String> ArrayList_OperatorHistory6 = new ArrayList<>(); 
    List<String> ArrayList_OperatorHistory7 = new ArrayList<>(); 
    List<String> ArrayList_OperatorHistory8 = new ArrayList<>(); 
    List<String> ArrayList_OperatorHistory9 = new ArrayList<>(); 
    List<String> ArrayList_OperatorHistory10 = new ArrayList<>(); 


    public test() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setBounds(100, 100, 450, 300);
        contentPane = new JPanel();
        contentPane.setBackground(Color.DARK_GRAY);
        contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
        setContentPane(contentPane);
        contentPane.setLayout(new CardLayout(0, 0));

        JButton btnShuffle = new JButton("SHUFFLE");
        contentPane.add(btnShuffle, "name_253561263644851");
        btnShuffle.addActionListener(this);

        ArrayList_AllOpers.add("POSITION ZERO");
        ArrayList_AllOpers.add("A");
        ArrayList_AllOpers.add("B");
        ArrayList_AllOpers.add("C");
        ArrayList_AllOpers.add("D");
        ArrayList_AllOpers.add("E");
        ArrayList_AllOpers.add("F");
        ArrayList_AllOpers.add("Greek");
        ArrayList_AllOpers.add("Hospital");
        ArrayList_AllOpers.add("Indigo");
        ArrayList_AllOpers.add("Juliette");

        ArrayList_MainOperatorHistory.add(ArrayList_OperatorHistory1);
        ArrayList_MainOperatorHistory.add(ArrayList_OperatorHistory1);
        ArrayList_MainOperatorHistory.add(ArrayList_OperatorHistory2);
        ArrayList_MainOperatorHistory.add(ArrayList_OperatorHistory3);
        ArrayList_MainOperatorHistory.add(ArrayList_OperatorHistory4);
        ArrayList_MainOperatorHistory.add(ArrayList_OperatorHistory5);
        ArrayList_MainOperatorHistory.add(ArrayList_OperatorHistory6);
        ArrayList_MainOperatorHistory.add(ArrayList_OperatorHistory7);
        ArrayList_MainOperatorHistory.add(ArrayList_OperatorHistory8);
        ArrayList_MainOperatorHistory.add(ArrayList_OperatorHistory9);
        ArrayList_MainOperatorHistory.add(ArrayList_OperatorHistory10);
    }


    public void actionPerformed(ActionEvent arg0) {
        finalCleared = false;
        for(int j=1; j<numOfStations+1; j++){       
            cleared[j]=false;           
        }

        for(int j=numOfStations+1; j<21; j++){  
            cleared[j]=true;            
        }

        /*******************************
         *****ASSIGN ALL OPERATORS******
         *******************************/
        //System.out.println("THIS IS ROTATION "+numOfRotations);

        for(int i=1; i<1+1; i++){
            do{
                //clears the list of operators used during last rotation                
                ArrayList_CurrentRotationOperators.clear();

                //Assign the operators
                for(int j=1; j<numOfStations+1; j++){       
                    String_CurrentOperator[j] = ArrayList_AllOpers.get(randnum[j]);     
                }

                //1. Ensure assigned operators or unique else fail finalCleared check
                //2. If finalCleared is true then exit do and for loops
                for(int j=1; j<numOfStations+1; j++){       
                    if (ArrayList_MainOperatorHistory.get(j).contains(String_CurrentOperator[j]) || ArrayList_CurrentRotationOperators.contains(String_CurrentOperator[j])){
                        cleared[j]=false;
                    }
                    else{
                        cleared[j]=true;
                        ArrayList_CurrentRotationOperators.add(String_CurrentOperator[j]);
                    }           
                }


                if (cleared[1]==true && cleared[2]==true && cleared[3]==true && cleared[4]==true && cleared[5]==true && cleared[6]==true 
                        && cleared[7]==true && cleared[8]==true && cleared[9]==true && cleared[10]==true){
                    finalCleared=true;
                }
                else{
                    genRandoms();
                }
            }
            while(finalCleared == false);
        }

        //1. Prints the name of the operator working on the station
        //2.  Adds operator to list of all users who have been assigned to station
        for(int i=1; i<numOfStations+1; i++){
            System.out.print(String_CurrentOperator[i]+" ");                                      
            ArrayList_MainOperatorHistory.get(i).add(String_CurrentOperator[i]);
        }

        //Perform cleanup actions for next iteration of the loop
        //1.  increment the rotation count by 1
        numOfRotations++;              
        System.out.println("");
    }   



    /*********************
     **genRandoms Method**
     *********************/
    public void genRandoms(){
        for(int i=1; i<numOfStations+1; i++){
            randnum[i] = rand_opers.nextInt(numOfStations)+1;
        }
    }
}
theonedgo
  • 21
  • 2