algorithm to find the largest area - java

................................
.XXXXXXXXXXXXXXX.....XXXXXXXXXX.
.X.....X.......X.....X........X.
.X.....X.......XXXXXXX........X.
.XXXXXXXXXXXX.................X.
.X....X.....X.................X.
.X....X.....XXXX..............X.
.XXXXXX........X..............X.
......X........X..............X.
......X........X..............X.
......X........X..............X.
......XXXXXXXXXXXXXXXXXXXXXXXXX.
................................
Looking for an algorithm to find the largest area. Here, "area" is defined as a number of dots (.) bounded by Xs.
private static void readFile(File inputFile) throws IOException {
Scanner fileScanner = new Scanner(inputFile);
Point previousPoint = null;
int rowCount = 0;
while(fileScanner.hasNext()){
String line = fileScanner.next();
String[] points = line.split(" ");
for(int columnCount=0;columnCount<points.length;columnCount++){
if(points[columnCount].equalsIgnoreCase("x")){
Point currentPoint = new Point();
currentPoint.setxValue(columnCount);
currentPoint.setyValue(rowCount);
}
}
rowCount++;
}
}
This is my first and struggling to move further.

This algorithm should work. You just need to implement it in Java.
Load the file into a char[][]. (1 char[] per line)
Loop through the char[][] (2 dimensionally)
upon finding a '.', perform flood fill, changing all '.' to ',', also incrementing a counter on every change.
At the end of flood fill, compare this counter with a globally set maximum. If it's higher, then set it as the new highest. (If the edges are not a proper boundary, then do not set this counter if you reached an edge during flood fill by setting a flag during 3)
Return the highest you set.
If you have any specific problems with the Java implementation, then let me know
Geobits:
Note: If you want to exclude the area "outside" any boxes, flood as
usual, but discard any area that hits the edge during the fill(skip
step 2.2 for that flood).
When doing the flood fill, you have 2 types of boundaries. A wall ('X'), and the edge of the array(which you need to explicitly check for to avoid OutOfBounds exceptions). If you hit an out of bounds, keep doing the fill, but set a flag so you know later to not consider the number you counted for the biggest box.

I was given this as assignment in an interview process and this is the compile and running code
import java.io.BufferedReader;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class FindArea {
public static void main(String[] args)
{
String fileName="C:\\map.txt";
FindArea area = new FindArea();
try{
FileReader inputFile = new FileReader(fileName);
BufferedReader bufferReader = new BufferedReader(inputFile);
char[][] twoArray= new char[100][100];
String line;
int i=0;
while ((line = bufferReader.readLine()) != null) {
twoArray[i] = line.toCharArray();
System.out.println(line);
i++;
}
bufferReader.close();
System.out.println("file read");
System.out.println("Max area: " + area.getMaxArea(twoArray));
} catch(Exception e) {
System.out.println("error : " + e.getMessage());
}
}
/**
* Get the maximum area from the given map
*
* #param charArray
* #return
*/
private int getMaxArea(char[][] charArray) {
HashMap<Integer, ArrayList<String>> numberOfBoxes = convertToBoxes(charArray);
numberOfBoxes = mergeOverlapAreas(numberOfBoxes);
int largeSize = 0;
for (Integer key : numberOfBoxes.keySet()) {
ArrayList<String> list = numberOfBoxes.get(key);
System.out.println("Key : " + key + " Size : " + list.size());
if (largeSize < list.size()) {
largeSize = list.size();
}
}
return largeSize;
}
/**
* Convert the 2d Array to HashMap
* Key being the count of boxes and
* Value being the list of indexes associations
*
* #param charArray
* #return
*/
private HashMap<Integer, ArrayList<String>> convertToBoxes(char[][] charArray) {
HashMap<Integer, ArrayList<String>> numberOfBoxes = new HashMap<Integer, ArrayList<String>>();
int boxes = 0;
for(int i=1; i<charArray.length; i++) {
for (int j=0; j<charArray[i].length; j++) {
if (charArray[i][j] == '.') {
boolean isExists = false;
for(Integer key : numberOfBoxes.keySet()) {
ArrayList<String> arrList = numberOfBoxes.get(key);
if(arrList != null) {
if(arrList.contains((i-1) + "-" + j) ||
arrList.contains(i + "-" + (j-1))) {
isExists = true;
arrList.add(i + "-" + j);
numberOfBoxes.put(key, arrList);
}
}
}
if (!isExists) {
ArrayList<String> list = new ArrayList<String>();
list.add(i + "-" + j);
numberOfBoxes.put(boxes, list);
boxes++;
}
}
}
}
return numberOfBoxes;
}
/**
* Check for the points exists in more than one area
* #param numberOfBoxes
* #return
*/
private HashMap<Integer, ArrayList<String>> mergeOverlapAreas( HashMap<Integer, ArrayList<String>> numberOfBoxes) {
for(Integer key : numberOfBoxes.keySet()) {
ArrayList<String> list1 = numberOfBoxes.get(key);
for (Integer key2 : numberOfBoxes.keySet()) {
if (key < key2) {
ArrayList<String> list2 = numberOfBoxes.get(key2);
Iterator<String> listIter = list2.iterator();
while(listIter.hasNext()) {
if (list1.contains(listIter.next())) {
list1.addAll(list2);
Set<String> noDuplicates = new HashSet<String>(list1);
numberOfBoxes.put(key, new ArrayList<String>(noDuplicates));
break;
}
}
}
}
}
return numberOfBoxes;
}
}

Here's an algorithm that's an alternative to flood fill. This method sweeps through the 2d array and whenever you encounter a node(pixel) that's outside to the left (right, top, bottom), it flags the current node as outside, ie if your neighbour is 'outside', you're marked 'outside' too.
The algorithm continues like this until there're no more updates. That means that all the nodes that are reachable from the 'outside' have been flagged. BTW, this is a very similar problem to level sets functions and updating them (where flood fill is also used). The nice this about this method is that it is ideal for parallelization.
1. Load 2D Symbol Array from File
2. hasupdates = false
3. Create 'isinside' bool array -> {
if(symbolarray[row][col] == '.' and row or col is at boundary)
isinside[row][col] = false
else
isinside[row][col] = true
}
4. do{
Do a sweep from left to right (for all rows) -> //This loop can be run parallely on all rows.
If (!isinside[row][col-1] and symbolarray[row][col] == '.'){
isinside[row][col] = false //mark current value as 'outside'
hasupdates = true
}
Do similar sweeps from right to left, top to bottom(all columns) and bottom to top.
}while(hasupdates)
5. Go through 'isinside' array and count the number of falses.
If you have huge files where you have to do this area calculation, you can have the sweeps along the rows and columns run parallely, because each row update (column update) is independent of the other updates.

Related

nearest neighbor algorithm copy element (city) to output array java

So I have a program written so far that reads in a csv file of cities and distances in the following format:
Alaska Mileage Chart,Anchorage,Anderson,Cantwell,
Anchorage,0,284,210,
Anderson,284,0,74,
Cantwell,210,74,0,
So the algorithm works and outputs the cities in the order they should be visited following the shortest path using the nearest neighbor algorithm always starting with Anchorage as the city of origin or starting city.
Using this data, the example output for the algorithm is: 1,3,2. I have ran this with a 27 element chart and had good results as well. I am using this small one for writing and debugging purposes.
Ideally the output I am looking for is the Name of the City and a cumulative milage.
Right now I am having working on trying to get the cities into an array that I can print out. Help with both parts would be appreciated or help keeping in mind that is the end goal is appreciated as well.
My thought was that ultimately I may want to create an array of {string, int}
so my output would look something like this..
Anchorage 0
Cantwell 210
Anderson 284
I am able to set the first element of the array to 1, but can not get the 2nd and 3rd element of the new output array to correct
This is the code I am having a problem with:
public class TSPNearestNeighbor {
private int numberOfNodes;
private Stack<Integer> stack;
public TSPNearestNeighbor()
{
stack = new Stack<>();
}
public void tsp(int adjacencyMatrix[][])
{
numberOfNodes = adjacencyMatrix[1].length;
// System.out.print(numberOfNodes);
// System.out.print(Arrays.deepToString(adjacencyMatrix));
int[] visited = new int[numberOfNodes];
// System.out.print(Arrays.toString(visited));
visited[1] = 1;
// System.out.print(Arrays.toString(visited));
stack.push(1);
int element, dst = 0, i;
int min = Integer.MAX_VALUE;
boolean minFlag = false;
System.out.print(1 + "\n");
//System.arraycopy(arr_cities, 0, arr_final, 0, 1); // Copies Anchorage to Pos 1 always
//System.out.print(Arrays.deepToString(arr_final)+ "\n");
while (!stack.isEmpty())
{
element = stack.peek();
i = 1;
min = Integer.MAX_VALUE;
while (i <= numberOfNodes-1)
{
if (adjacencyMatrix[element][i] > 1 && visited[i] == 0)
{
if (min > adjacencyMatrix[element][i])
{
min = adjacencyMatrix[element][i];
dst = i;
minFlag = true;
}
}
i++;
}
if (minFlag)
{
visited[dst] = 1;
stack.push(dst);
System.out.print(dst + "\n");
minFlag = false;
continue;
}
stack.pop();
}
}
Given the existing structure you are using, you can output the cities in the path using:
public void printCities(Stack<Integer> path, int[][] distances, List<String> names) {
int cumulativeDistance = 0;
int previous = -1;
for (int city: path) {
if (previous != -1)
cumulativeDistance += distances[previous][city];
System.out.println(names.get(city) + " " + cumulativeDistance);
previous = city;
}
}
I'd like to answer your question slightly indirectly. You are making life hard for yourself by using arrays of objects. They make the code difficult to read and are hard to access. Things would become easier if you create a City class with appropriate methods to help you with the output.
For example:
class City {
private final String name;
private final Map<City,Integer> connections = new HashMap<>();
public static addConnection(City from, City to, int distance) {
from.connections.put(to, distance);
to.connections.put(from, distance);
}
public int getDistanceTo(City other) {
if (connections.containsKey(other))
return connections.get(other);
else
throw new IllegalArgumentException("Non connection error");
}
}
I've left out constructor, getters, setters for clarity.
Now outputting your path becomes quite a bit simpler:
public void outputPath(List<City> cities) {
int cumulativeDistance = 0;
City previous = null;
for (City current: cities) {
if (previous != null)
cumulativeDistance += previous.getDistanceTo(current);
System.out.println(current.getName + " " + cumulativeDistance);
previous = current;
}
}

Implementing DFA minmization algorithm with JAVA ArrayList

I have implemented the DFA minimization algorithm with array lists, but it doesn't return the correct answer. If someone could point out which part of the algorithm I am missing, I would appreciate it.(And correction on whether I have used an efficient way to implement it).
The program is supposed to read data from a file and then work on it. But this function has nothing to do with those data. I have hard coded it to work.
The method that implements the algorithm is named (unreachableStates)
DEBUG I: So I went thorough the code, and found out that the problem is the loop that surround the expression | temp.add(transitionTable[j][i]) |. This will work for the first 2 iterations, but after that it will not consider all the states. Now the challenge is to fix it.
package dRegAut;
import java.io.*;
import java.util.ArrayList;
import java.util.Iterator;
public class dfamin {
// Global variables to hold data from the file
private int numStates,numAlphabets,numFinalStates;
private char alphabets[];
private boolean finalStates[];
private int [][] transitionTable;
/**
* #param args
* #throws IOException
* #throws Numberfor matException
*/
public static void main(String[] args) throws Numberfor matException, IOException {
int numStates,numAlphabets,numFinalStates;
char alphabets[];
boolean finalStates[];
int [][] transitionTable;
// Take file name and open a stream to read it
FileInputStream fileStream = new FileInputStream("/path/to/file/trace");
BufferedReader br = new BufferedReader(new InputStreamReader(fileStream));
// Store each line from the file
String line;
// Read each line from file
while((line = br.readLine()) != null){
// Read single spaced data from each line
String [] splittedLine = line.split(" ");
// Read numStates,numAlphabets from the line
numStates = Integer.parseInt(splittedLine[0]);
numAlphabets = Integer.parseInt(splittedLine[1]);
//for (int a=0;a<numAlphabets;a++){
//alphabets[a] = '0';
//}
transitionTable = new int[numStates][numAlphabets];
int tt= 2;
// Loop thorough the line and read transition table
for (int row=0;row<numStates;row++){
for (int col=0;col<numAlphabets;col++){
transitionTable[row][col] = Integer.parseInt(splittedLine[tt]);
tt++;
} // End of for -loop to go thorough alphabets
} // End of for -loop to go thorough states
// Read number of final states
numFinalStates = Integer.parseInt(splittedLine[2+numStates*numAlphabets]);
//System.out.println(numFinalStates);
// Read final states
int z=0;
finalStates = new boolean[numStates];
int start = 3+numStates*numAlphabets ;
int end = (3+(numStates*numAlphabets))+numFinalStates;
for (int fs=start;fs<end;fs++){
finalStates[ Integer.parseInt(splittedLine[fs])] = true;
//System.out.println(finalStates[z]);
z++;
} // End of for -loop to read all final states
dfamin x = new dfamin(numStates,numAlphabets,numFinalStates,finalStates,transitionTable);
//x.minimizer();
//System.out.println(x);
//System.out.println("======================");
int [][] ttt = {{1,2},{0,2},{1,0},{1,2}};
x.unreachableStates(4,2,ttt);
} // End of while-loop to read file
// Close the stream
br.close();
}
dfamin(int nS,int nA,int nFS,boolean fS[], int [][] tT){
numStates = nS;
numAlphabets = nA;
numFinalStates = nFS;
//alphabets = a;
finalStates = fS;
transitionTable = tT;
} // End of DFAMinimizer constructor
/*
* A method to minmize the dfa
*/
public void minimizer(){
} // End of minimizer method
/*
* A method to find unreachable states
*
*/
public void unreachableStates(int numStates, int numAlphabets, int [][] transitionTable){
// Initialize a list to hold temporary list of states in it
ArrayList<Integer> reachableStates =new ArrayList();
ArrayList<Integer> newStates = new ArrayList();
// Start from the state zero
reachableStates.add(0);
newStates.add(0);
// Temporary array to hold reachable states
ArrayList<Integer> temp = new ArrayList();
// Loop until there is data in newStates
do {
// Empty temp array
temp.clear();
for (int j=0;j<newStates.size();j++){
for (int i=0; i<numAlphabets;i++){
//System.out.printf("Alphabets:%d State:%ds ",i,j);
//System.out.printf("State:%d newStates:%d \n",j, newStates.get(j));
//System.out.printf("transitionTable: %d\n",transitionTable[j][i]);
temp.add(transitionTable[j][i]);
//System.out.printf("Temp[%d] = %d",i,temp.get(i));
//System.out.printf("Alphabets: %d", i);
} // End of for -loop to go thorough all characters
//System.out.printf("newStates: %d\n",newStates.get(j));
} // End of for -loop to go thorough all elements of the newStates array list
//System.out.printf("newStateSize: %d",newStates.size());
// Clear newStates list
newStates.clear();
//System.out.printf("Temp Size: %d", temp.size());
// Add the elements that are in temp, but are not in reachableStates to newStates
for (int z=0;z<temp.size();z++){
for (int z1=0; z1<reachableStates.size();z1++){
// if the state was already present, don't add
if (temp.get(z) == reachableStates.get(z1)){
break;
}
if (temp.get(z) != reachableStates.get(z1) && z1 == reachableStates.size()-1){
//System.out.printf("Temp:%d reachableStates:%d z:%d z1:%d \n",temp.get(z),reachableStates.get(z1),z,z1);
newStates.add(temp.get(z));
}
//System.out.printf("ReachableStates: %d ", reachableStates.get(z1));
} // End of for -loop to go thorough all reachableStates elements and check if a match
} // End of for -loop thorugh all temp states
//System.out.printf("NewStates Size after loop:%d \n",newStates.size());
if (!newStates.isEmpty()){
// Add the newStates elements to reachable states
for (int y=0;y<newStates.size();y++){
//System.out.printf("newStates:%d newStatesSize:%d in %d",newStates.get(y),newStates.size(),y);
reachableStates.add(newStates.get(y));
}
}
/*
//System.out.println();
System.out.printf("reachable states:");
for (int y=0;y<reachableStates.size();y++){
System.out.printf("%d",reachableStates.get(y));
}
System.out.printf("End!\n");
*/
} while(!newStates.isEmpty());
System.out.printf("Reachable sadfStates: ");
for (int w = 0;w<reachableStates.size()-1;w++){
System.out.printf(" %d ",reachableStates.get(w));
}
System.out.println();
} // End of unreachableStates method
}
After an hour of debugging, got it working. I just had to change the problem that I mentioned in DEBUG I (in question). I changed it as following:
transitionTable[newStates.get(j)][i]

How do I pass an array to a method in java?

Here is the program that I am trying to run:
/**
* Write a description of class mainGame here.
*
* #author Anthony Parsch
* #version 0.1.1
*/
//Import what I need to.
import java.io.*;
public class mainGame
{
/**
* Constructor for objects of class mainGame
*/
public static void main(String[] args)
{
// initialise instance variables
int xCoord = 10; //The map's max x coordinate +1
int yCoord = 10; //The map's max y coordinate +1
int playerX = 0; //The player's current x coordinate
int playerY = 0; //The player's current y coordinate
//Declare the arrays
String[][] map; //[x][y]The map
String[][] direc; //[x][y]Which directions that you can go
String[][] items; //[x][y]Where items are at
String[] inv; // Holds your inventory.
int[][] helpInt; //[x][y] All the other stuff in the
//Initalize the arrays
//---The player arrays
inv = new String[10]; //The player's inventory
inv[0] = "0";
inv = addItem(inv, "Blarg");//GET RID OF THIS LATER
//---The map arrays
map = new String[xCoord][yCoord]; //Descriptions
direc = new String[xCoord][yCoord]; //north y++,west x--,south y--,east x++
items = new String[xCoord][yCoord]; //The items within the world
//Declare the values of map
map[0][0] = "You wake up with the feel of cold metal on your back. The only other thing in this room is the door.";
map[0][1] = "You are now in a hallway with a door behind you and one either side. Forward is a continuation of the hallway.com";
//Declare the values of direc
direc[0][0] = "north";
direc[0][1] = "north, south, east, west";
print(inv[0]); //Check that the functions work
print(findDirec(direc, 0, 0));
}
/**
* Finds and displays the avaliable exits for a coordinate.
*
* #param map[][] The map array from which this method pulls the directions from.
* #param x The x value of the map
* #param y The y value of the map
* #return The string value of which way you can go
*/
static String findDirec(String[][] map, int x, int y){
//Pulls the directions
String match = map[x][y];
//Checks Directions
boolean nor = match.matches("(.*)north(.*)");
boolean sou = match.matches("(.*)south(.*)");
boolean wes = match.matches("(.*)west(.*)");
boolean eas = match.matches("(.*)east(.*)");
//Displays directions
String placeHolder = "You can go ";
if (nor == true){
placeHolder = placeHolder + "north, ";
} else if(sou == true) {
placeHolder = placeHolder + "south, ";
} else if(wes == true) {
placeHolder = placeHolder + "west, ";
} else if(eas == true) {
placeHolder = placeHolder + "east";
}
//---Checks if east is in the string, if not it removes the space and comma
if (eas == false){
StringBuffer soo = new StringBuffer(placeHolder);
soo.delete((placeHolder.length()-3), (placeHolder.length()-1));
placeHolder = soo.toString();
}
//Adds the ending period
placeHolder = placeHolder + ".";
//Returns the string
return placeHolder;
}
//Adds an item to an inventory
static String[] addItem(String inv[], String item){
int i; //Counter for the for loop, and where to add the item at.
boolean stop = false;
for(i=0; stop = true; i++)
{
if(inv[i].equals("0"))
{
stop = true;
}
}
inv[i] = item;
return inv;
}
static void print(String entry){
System.out.print(entry);
}
}
And when I try and run it through the Command Prompt, I get this error:
Exception in thread "main" java.lang.NullPointerExcpetion
at mainGame.addItem(mainGame.java:113)
at mainGame.main(mainGame.java:38)
When I paste this in to a text editor, line 113 is simply a closing brace }.
However, one line before that is a logic flaw which I presume is really line 113 for you.
for(i=0; stop = true; i++)
{
if(inv[i].equals("0"))
{
stop = true;
}
}
Each iteration of the loop assigns true to stop and then tests if true equals true, which it does. Your condition to exit the loop is when true equals false, which is never the case, therefore your loop goes forever until an error occurs. Also, don't you want to iterate while stop is false? I think you have it backwards.
The next problem is your if statement, which is probably where your NullPointerException is coming from:
if(inv[i].equals("0"))
{
stop = true;
}
You assume that inv[i] refers to an object. You need a null check.
Three recommendations:
Never use = for a comparison. Use ==. Since this is a boolean, you can even simplify this to stop.
Check the length in your for loop.
Compare "0" to inv[i] instead of the other way around to avoid null pointer dereferencing.
Try this:
boolean stop = false;
for (int i = 0; i < inv.length && !stop; i++)
{
if("0".equals(inv[i])
{
stop = true;
}
}
Another option, and this is a matter of form, is to remove the looping variable and just break out of the loop explicitly.
for (int i = 0; i < inv.length; i++)
{
if("0".equals(inv[i])
{
break;
}
}
inv = new String[10]; //The player's inventory
inv[0] = "0";
inv = addItem(inv, "Blarg");//GET RID OF THIS LATER
So you only initialize one index of your array but here:
for(i=0; stop = true; i++)
{
if(inv[i].equals("0"))
{
stop = true;
}
}
.. you loop through all of them. just kidding, didn't read the full problem. You should still read the rest of my answer, but the reason why you get the NPE is because your loop condition is broken. (by the way your for loop condition is broken, it should test for equivalence using the == operator, not the assignment = operator.)
So what you actually are doing with that code is this :
inv = new String[10];
At this point you have a new String array of capacity 10, with no values inside, something like this:
[null][null][null][null][null][null][null][null][null][null]
inv[0] = "0";
Now you set [0] to "0", so:
["0"][null][null][null][null][null][null][null][null][null]
Then your loop attempts to access all of those null references, that'll probably be why you have a null reference exception.
To fix it, simply every index position in your array to anything that is not-null:
Arrays.fill(inv, "");
I use Arrays.fill() because it's shorter.
In your for loop condition, which one do you prefer?
stop = true or stop == true
for(i=0; i<inv.length && !stop; i++)
{
if(inv[i]!=null && inv[i].equals("0"))
{
stop = true;
}
}

Most optimal way to combine unique groups of size k from n that meet specific requirements

I have been working on something the past few days that seems to be working as intended, however I am looking for ways to improve it. I have a set of n items, and I need to put together groups of these items that MUST meet ALL of the following requirements:
2 items from Category A
2 items from Category B
2 items from Category C
2 Items from Category D
1 item from Category E
I am currently using the following recursive method to put my groups together and the isValid() method is being used to determine if the group meets the criteria.
void getGroups(String[] arr, int len, int startPosition, String[] result) {
if(len == 0) {
Group group = new Group(Arrays.asList(result));
if(group.isValid()) {
validGroups.add(group);
group.printGroup();
}
return;
}
for(int i = startPosition; i <= arr.length - len; i++) {
result[result.length - len] = arr[i];
getGroups(arr, len - 1, i + 1, result);
}
}
I am able to see valid results get printed as the program runs, however the original size of items that I am working with can be well over 100 items. This means there is a very large number of total possible groups that will be iterated through and a lot of times the program never actually completes.
I know that there are currently a bunch of wasted iterations, for example if at some point I detect a group is invalid because it has 3 items from Category A, I should be able to move on. I am not sure if my current method with a few tweaks is the best way to go about this, or if I should separate the items into their respective groups first, and then from their put only valid combinations together. Any help would be appreciated. Thanks.
EDIT: I tried to make the method a bit more simpler than my actual method. My actual method takes in an array of Objects that I've created that contain their value along with their category. I guess for the example we can assume that each category is represented by a list of Strings that it contains. The method can be called like:
String[] items = {"test1", "test2", "test3", "test4", "test5", "test6", "test7",
"test8", "test9", "test10", "test11", "test12", "test13",
"test14", "test15", "test16", "test17", "test18"};
getGroups(items, 9, 0, new String[9]);
EDIT2:
List<String> catA = new ArrayList<String>();
catA.add("test1");
catA.add("test2");
catA.add("test3");
catA.add("test4");
List<String> catB = new ArrayList<String>();
catB.add("test5");
catB.add("test6");
catB.add("test7");
catB.add("test8");
List<String> catC = new ArrayList<String>();
catC.add("test9");
catC.add("test10");
catC.add("test11");
catC.add("test12");
List<String> catS = new ArrayList<String>();
catD.add("test13");
catD.add("test14");
catD.add("test15");
catD.add("test16");
List<String> catE = new ArrayList<String>();
catE.add("test17");
catE.add("test18");
Output:
{"test1", "test2", "test5", "test6", "test9", "test10", "test13", "test14", "test17"}
{"test1", "test2", "test5", "test6", "test9", "test10", "test13", "test14", "test18"}
{"test1", "test2", "test5", "test6", "test9", "test10", "test13", "test16", "test17"}
{"test1", "test2", "test5", "test6", "test9", "test10", "test13", "test15", "test17"}
{"test1", "test2", "test5", "test6", "test9", "test10", "test14", "test15", "test17"}
etc...
This seems to work.
I use a BitPattern iterator I wrote a while ago that walks all n-bit numbers containing just k set bits and uses that to select from your categories.
Note that much of this code is building the test data to reflect your requirements.
I hold a List of Iterables which are the BitPatterns. A list of Iterators which are the currently in-use Iterators from the BitPatterns (they must be renewed every time they complete) and a List of BigIntgers that are the current values to explode into selections from the data.
public class Test {
enum Category {
A(2), B(2), C(2), D(2), E(1);
public final int required;
Category(int required) {
this.required = required;
}
}
private static final Category[] categories = Category.values();
static class Categorised {
final String name;
final Category category;
Categorised(String name, Category category) {
this.name = name;
this.category = category;
}
#Override
public String toString() {
return category.name() + ":" + name;
}
}
static final List<Categorised> data = new ArrayList<>();
static {
data.add(new Categorised("A-1", Category.A));
data.add(new Categorised("A-2", Category.A));
data.add(new Categorised("A-3", Category.A));
data.add(new Categorised("B-1", Category.B));
data.add(new Categorised("B-2", Category.B));
data.add(new Categorised("B-3", Category.B));
data.add(new Categorised("C-1", Category.C));
data.add(new Categorised("C-2", Category.C));
data.add(new Categorised("C-3", Category.C));
data.add(new Categorised("D-1", Category.D));
data.add(new Categorised("D-2", Category.D));
data.add(new Categorised("D-3", Category.D));
data.add(new Categorised("E-1", Category.E));
data.add(new Categorised("E-2", Category.E));
data.add(new Categorised("E-3", Category.E));
}
// Categorise the data.
private Map<Category, List<Categorised>> categorise(List<Categorised> data) {
Map<Category, List<Categorised>> categorised = new EnumMap<>(Category.class);
for (Categorised d : data) {
List<Categorised> existing = categorised.get(d.category);
if (existing == null) {
existing = new ArrayList<>();
categorised.put(d.category, existing);
}
existing.add(d);
}
return categorised;
}
public void test() {
// Categorise the data.
Map<Category, List<Categorised>> categorised = categorise(data);
// Build my lists.
// A source of Iteratprs.
List<BitPattern> is = new ArrayList<>(categories.length);
// The Iterators.
List<Iterator<BigInteger>> its = new ArrayList<>(categories.length);
// The current it patterns to use to select.
List<BigInteger> next = new ArrayList<>(categories.length);
for (Category c : categories) {
int k = c.required;
List<Categorised> from = categorised.get(c);
// ToDo - Make sure there are enough.
int n = from.size();
// Make my iterable.
BitPattern p = new BitPattern(k, n);
is.add(p);
// Gather an Iterator.
Iterator<BigInteger> it = p.iterator();
// Store it.
its.add(it);
// Prime it.
next.add(it.next());
}
// Walk the lists.
boolean stepped;
do {
// Interpret the current numbers.
List<Categorised> candidates = new ArrayList<>();
for ( int c = 0; c < categories.length; c++ ) {
BigInteger b = next.get(c);
List<Categorised> category = categorised.get(categories[c]);
// Step through the bits in the number.
BitSet bs = BitSet.valueOf(b.toByteArray());
for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) {
// Pull those entries from the categorised list.
candidates.add(category.get(i));
}
}
// Print it for now.
System.out.println(candidates);
// Step again.
stepped = step(is, its, next);
} while (stepped);
}
// Take one step.
private boolean step(List<BitPattern> is, List<Iterator<BigInteger>> its, List<BigInteger> next) {
boolean stepped = false;
// Step each one until we make one successful step.
for (int i = 0; i < is.size() && !stepped; i++) {
Iterator<BigInteger> it = its.get(i);
if (it.hasNext()) {
// Done here!
stepped = true;
} else {
// Exhausted - Reset it.
its.set(i, it = is.get(i).iterator());
}
// Pull that one.
next.set(i, it.next());
}
return stepped;
}
public static void main(String args[]) {
new Test().test();
}
}
This is the BitPattern iterator.
/**
* Iterates all bit patterns containing the specified number of bits.
*
* See "Compute the lexicographically next bit permutation"
* http://graphics.stanford.edu/~seander/bithacks.html#NextBitPermutation
*
* #author OldCurmudgeon
*/
public class BitPattern implements Iterable<BigInteger> {
// Useful stuff.
private static final BigInteger ONE = BigInteger.ONE;
private static final BigInteger TWO = ONE.add(ONE);
// How many bits to work with.
private final int bits;
// Value to stop at. 2^max_bits.
private final BigInteger stop;
// Should we invert the output.
private final boolean not;
// All patterns of that many bits up to the specified number of bits - invberting if required.
public BitPattern(int bits, int max, boolean not) {
this.bits = bits;
this.stop = TWO.pow(max);
this.not = not;
}
// All patterns of that many bits up to the specified number of bits.
public BitPattern(int bits, int max) {
this(bits, max, false);
}
#Override
public Iterator<BigInteger> iterator() {
return new BitPatternIterator();
}
/*
* From the link:
*
* Suppose we have a pattern of N bits set to 1 in an integer and
* we want the next permutation of N 1 bits in a lexicographical sense.
*
* For example, if N is 3 and the bit pattern is 00010011, the next patterns would be
* 00010101, 00010110, 00011001,
* 00011010, 00011100, 00100011,
* and so forth.
*
* The following is a fast way to compute the next permutation.
*/
private class BitPatternIterator implements Iterator<BigInteger> {
// Next to deliver - initially 2^n - 1
BigInteger next = TWO.pow(bits).subtract(ONE);
// The last one we delivered.
BigInteger last;
#Override
public boolean hasNext() {
if (next == null) {
// Next one!
// t gets v's least significant 0 bits set to 1
// unsigned int t = v | (v - 1);
BigInteger t = last.or(last.subtract(BigInteger.ONE));
// Silly optimisation.
BigInteger notT = t.not();
// Next set to 1 the most significant bit to change,
// set to 0 the least significant ones, and add the necessary 1 bits.
// w = (t + 1) | (((~t & -~t) - 1) >> (__builtin_ctz(v) + 1));
// The __builtin_ctz(v) GNU C compiler intrinsic for x86 CPUs returns the number of trailing zeros.
next = t.add(ONE).or(notT.and(notT.negate()).subtract(ONE).shiftRight(last.getLowestSetBit() + 1));
if (next.compareTo(stop) >= 0) {
// Dont go there.
next = null;
}
}
return next != null;
}
#Override
public BigInteger next() {
last = hasNext() ? next : null;
next = null;
return not ? last.not(): last;
}
#Override
public void remove() {
throw new UnsupportedOperationException("Not supported.");
}
#Override
public String toString () {
return next != null ? next.toString(2) : last != null ? last.toString(2): "";
}
}
}
I will not write code but will list a possible approach. I say possible because it will be running and storing all data in memory and is not the best in regard to algorithms. yet, it is an approach where you don't need to eliminate invalid options. I will use an example in order to make things more clear.
suppose you have categories A,B,C. Where K=2 for A,B and K=1 for C.
you also have the input items A1,B1,B2,A2,C1,A3
1- go over the items and divide them according to their category. so you prepare an array/list for each category that has all the items that belong to it.
so now you have arrays:
Category A = [A1,A2,A3] , Category B = [B1,B2] and Category C=[C1]
2- now after preparing the lists, prepare the various legal groups that you can have for picking K items from N items found in that list . here is a link that might help in doing that efficiently: How to iteratively generate k elements subsets from a set of size n in java?
now you have:
first group belonging to category A:
[A1,A2] , [A1,A3], [A2,A3] (3 elements)
second group belonging to category B:
[B1,B2] (1 element)
third group belonging to category C:
[C1] (1 element)
3- now, if you treat each such group as an item, the question transforms to how many different ways are there for picking exactly one element from each group. and that is supposed to be easier to program recursively and will not require eliminating options. and if the number of categories is constant, it will be nested loops over the sets of groups in second point above.
EDIT
the approach works well in eliminating the need to validate bad combinations.
yet, there will still be a problem in regard of time. Here is the code that I made to demonstrate. it makes a list of 100 items. then it does the steps mentioned.
Note that I commented out the code that prints the groups.
The calculation is very fast up to that point. I have added code that prints how many legal choices can be made from each group.
package tester;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
/**
*
* #author
*/
public class Tester {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
//generate 100 random items belonging to categories
Random rand=new Random();
List<Item> items=new ArrayList<>();
int a=1,b=1,c=1,d=1,e=1;
for (int i=0;i<100;i++){
int randomNumber=rand.nextInt(5)+1;
CATEGORY_TYPE categoryType=null;
int num=0;
switch (randomNumber) {
case 1:
categoryType=CATEGORY_TYPE.A;
num=a++;
break;
case 2:
categoryType=CATEGORY_TYPE.B;
num=b++;
break;
case 3:
categoryType=CATEGORY_TYPE.C;
num=c++;
break;
case 4:
categoryType=CATEGORY_TYPE.D;
num=d++;
break;
case 5:
categoryType=CATEGORY_TYPE.E;
num=e++;
break;
}
String dummyData="Item "+categoryType.toString()+num;
Item item=new Item(dummyData,categoryType);
items.add(item);
}
//arrange the items in lists by category
List<Item> categoryAItemsList=new ArrayList<>();
List<Item> categoryBItemsList=new ArrayList<>();
List<Item> categoryCItemsList=new ArrayList<>();
List<Item> categoryDItemsList=new ArrayList<>();
List<Item> categoryEItemsList=new ArrayList<>();
for (Item item:items){
if (item.getCategoryType()==CATEGORY_TYPE.A)
categoryAItemsList.add(item);
else if (item.getCategoryType()==CATEGORY_TYPE.B)
categoryBItemsList.add(item);
else if (item.getCategoryType()==CATEGORY_TYPE.C)
categoryCItemsList.add(item);
else if (item.getCategoryType()==CATEGORY_TYPE.D)
categoryDItemsList.add(item);
else if (item.getCategoryType()==CATEGORY_TYPE.E)
categoryEItemsList.add(item);
}
//now we want to construct lists of possible groups of choosing from each category
List<Item[]> subsetStoringListA=new ArrayList<>();
List<Item[]> subsetStoringListB=new ArrayList<>();
List<Item[]> subsetStoringListC=new ArrayList<>();
List<Item[]> subsetStoringListD=new ArrayList<>();
List<Item[]> subsetStoringListE=new ArrayList<>();
processSubsets(categoryAItemsList.toArray(new Item[0]),2,subsetStoringListA);
processSubsets(categoryBItemsList.toArray(new Item[0]),2,subsetStoringListB);
processSubsets(categoryCItemsList.toArray(new Item[0]),2,subsetStoringListC);
processSubsets(categoryDItemsList.toArray(new Item[0]),2,subsetStoringListD);
processSubsets(categoryEItemsList.toArray(new Item[0]),1,subsetStoringListE);
System.out.println(" A groups number: "+subsetStoringListA.size());
System.out.println(" B groups number: "+subsetStoringListB.size());
System.out.println(" C groups number: "+subsetStoringListC.size());
System.out.println(" D groups number: "+subsetStoringListD.size());
System.out.println(" E groups number: "+subsetStoringListE.size());
//now we just print all possible combinations of picking a single group from each list.
//the group is an array with valid choices
// for (Item[] subsetA:subsetStoringListA){
// for (Item[] subsetB:subsetStoringListB){
// for (Item[] subsetC:subsetStoringListC){
// for (Item[] subsetD:subsetStoringListD){
// for (Item[] subsetE:subsetStoringListE){
// print(subsetA);
// print(subsetB);
// print(subsetC);
// print(subsetD);
// print(subsetE);
// System.out.println("\n");
// }
//
// }
// }
// }
// }
}
static void print(Item[] arr){
for (Item item:arr)
System.out.print(item.getDumyData()+" ");
}
static void processSubsets(Item[] set, int k,List<Item[]> subsetStoringList) {
Item[] subset = new Item[k];
processLargerSubsets(set, subset, 0, 0,subsetStoringList);
}
static void processLargerSubsets(Item[] set, Item[] subset, int subsetSize, int nextIndex,List<Item[]> subsetStoringList) {
if (subsetSize == subset.length) { //here we have a subset we need to store a copy from it
subsetStoringList.add(Arrays.copyOf(subset, subset.length));
} else {
for (int j = nextIndex; j < set.length; j++) {
subset[subsetSize] = set[j];
processLargerSubsets(set, subset, subsetSize + 1, j + 1,subsetStoringList);
}
}
}
public static enum CATEGORY_TYPE {A,B,C,D,E}
private static class Item{
private CATEGORY_TYPE categoryType;
private String dumyData;
public Item(String dumyData,CATEGORY_TYPE categoryType) {
this.dumyData = dumyData; //maybe bad name but i mean the object can have many other fields etc
this.categoryType = categoryType;
}
/**
* #return the categoryType
*/
public CATEGORY_TYPE getCategoryType() {
return categoryType;
}
/**
* #return the dumyData
*/
public String getDumyData() {
return dumyData;
}
}
}
in a specific run, it gave the following:
A groups number: 210
B groups number: 153
C groups number: 210
D groups number: 210
E groups number: 19
that means , if we had to print all possible choices of a single element (and here an elemnt is an array containing k choices from a category) from each of these, you will have : 210*153*210*210*19 = 26,921,727,000
now listing/printing over 26 billion variations will take time no matter what and I don't see how it will be minimized.
try setting the total items to 20 and uncomment the printing code to see that everything is working correctly. And see if you really need to list the possible combinations. please remember that every combination here is legal and there are no wasted iterations in all the parts of the code.
one final note: I did not treat edge cases like when there are no items in a category to complete K. that you can easily put in the code according to the desired behaviour in that case.
So this seems to be a constraint satisfaction problem. So maybe try backtracking?
I believe the following works, but plug in your own data to guaranteee.
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Launch {
public static void main(String[] args) {
// Formulate the constraints.
int[] constraints = { 2, 1, 0, 1 };
// Create all the items.
List<boolean[]> items = new ArrayList<boolean[]>();
boolean[] i1 = { true, false, true, false };
boolean[] i2 = { true, false, false, false };
boolean[] i3 = { false, true, false, true };
boolean[] i4 = { false, false, false, true };
items.add(i1);
items.add(i2);
items.add(i3);
items.add(i4);
// Solve!
backtrack(constraints, items);
}
/**
* Recursively generate possible solutions but backtrack as soon as the constraints fail.
*/
private static void backtrack(int[] constraints, List<boolean[]> items) {
// We start with no items belonging to any categories.
List<List<boolean[]>> memberships = new ArrayList<List<boolean[]>>();
for (int i = 0; i < constraints.length; i++) {
memberships.add(new ArrayList<boolean[]>());
}
backtrack(constraints, items, memberships);
}
/**
* Recursively generate possible solutions but backtrack as soon as the constraints fail.
*/
private static void backtrack(int[] constraints, List<boolean[]> items,
List<List<boolean[]>> memberships) {
if (items.isEmpty() && !acceptable(constraints, memberships)) {
return;
} else if (acceptable(constraints, memberships)) {
display(memberships);
} else {
for (boolean[] item : items) {
int catIdx = 0;
for (boolean belongs : item) {
if (belongs) {
// The item and category were chosen so let's update
// memberships.
List<List<boolean[]>> newMemberships = new ArrayList<List<boolean[]>>();
for (List<boolean[]> old : memberships) {
newMemberships.add(new ArrayList<boolean[]>(old));
}
newMemberships.get(catIdx).add(item);
// We've placed the item so let's remove it from the
// possibilities.
List<boolean[]> newItems = new ArrayList<boolean[]>(
items);
newItems.remove(item);
// Now solve the sub-problem.
backtrack(constraints, newItems, newMemberships);
}
catIdx++;
}
}
}
}
/**
* A nice way to display the membership tables.
*/
private static void display(List<List<boolean[]>> memberships) {
System.out.println("---");
for (List<boolean[]> category : memberships) {
for (boolean[] item : category) {
System.out.print(Arrays.toString(item) + " ");
}
System.out.println();
}
}
/**
* Returns whether or not a list of memberships are accepted by the
* constraints.
*
* #param constraints
* - The number of items required per category.
* #param memberships
* - The current items per category.
*/
private static boolean acceptable(int[] constraints,
List<List<boolean[]>> memberships) {
boolean acceptable = memberships.size() == constraints.length;
for (int i = 0; i < memberships.size(); i++) {
acceptable = acceptable
&& constraints[i] == memberships.get(i).size();
}
return acceptable;
}
}

removeAll seems to affect its argument

I have written a generic Partition class (a partition is a division of a set into disjoint subsets, called parts). Internally this is a Map<T,Integer> and a Map<Integer,Set<T>>, where the Integers are the labels of the parts. For example partition.getLabel(T t) gives the label of the part that t is in, and partition.move(T t, Integer label) moves t to the partition labelled by label (internally, it updates both the Maps).
But my method for moving a Collection of objects to a new part does not work. It seems that Set.removeAll() is affecting its argument. I think the problem is something like a ConcurrentModificationException, but I can't work it out. Sorry the code is rather long, but I have marked where the problem is (about half-way down), and the output at the bottom should make it clear what the problem is - at the end the partition is in an illegal state.
import java.util.*;
public class Partition<T> {
private Map<T,Integer> objToLabel = new HashMap<T,Integer>();
private Map<Integer,Set<T>> labelToObjs =
new HashMap<Integer,Set<T>>();
private List<Integer> unusedLabels;
private int size; // = number of elements
public Partition(Collection<T> objects) {
size = objects.size();
unusedLabels = new ArrayList<Integer>();
for (int i = 1; i < size; i++)
unusedLabels.add(i);
// Put all the objects in part 0.
Set<T> part = new HashSet<T>(objects);
for (T t : objects)
objToLabel.put(t,0);
labelToObjs.put(0,part);
}
public Integer getLabel(T t) {
return objToLabel.get(t);
}
public Set<T> getPart(Integer label) {
return labelToObjs.get(label);
}
public Set<T> getPart(T t) {
return getPart(getLabel(t));
}
public Integer newPart(T t) {
// Move t to a new part.
Integer newLabel = unusedLabels.remove(0);
labelToObjs.put(newLabel,new HashSet<T>());
move(t, newLabel);
return newLabel;
}
public Integer newPart(Collection<T> things) {
// Move things to a new part. (This assumes that
// they are all in the same part to start with.)
Integer newLabel = unusedLabels.remove(0);
labelToObjs.put(newLabel,new HashSet<T>());
moveAll(things, newLabel);
return newLabel;
}
public void move(T t, Integer label) {
// Move t to the part "label".
Integer oldLabel = getLabel(t);
getPart(oldLabel).remove(t);
if (getPart(oldLabel).isEmpty()) // if the old part is
labelToObjs.remove(oldLabel); // empty, remove it
getPart(label).add(t);
objToLabel.put(t,label);
}
public void moveAll(Collection<T> things, Integer label) {
// Move all the things from their current part to label.
// (This assumes all the things are in the same part.)
if (things.size()==0) return;
T arbitraryThing = new ArrayList<T>(things).get(0);
Set<T> oldPart = getPart(arbitraryThing);
// THIS IS WHERE IT SEEMS TO GO WRONG //////////////////////////
System.out.println(" oldPart = " + oldPart);
System.out.println(" things = " + things);
System.out.println("Now doing oldPart.removeAll(things) ...");
oldPart.removeAll(things);
System.out.println(" oldPart = " + oldPart);
System.out.println(" things = " + things);
if (oldPart.isEmpty())
labelToObjs.remove(objToLabel.get(arbitraryThing));
for (T t : things)
objToLabel.put(t,label);
getPart(label).addAll(things);
}
public String toString() {
StringBuilder result = new StringBuilder();
result.append("\nPARTITION OF " + size + " ELEMENTS INTO " +
labelToObjs.size() + " PART");
result.append((labelToObjs.size()==1 ? "" : "S") + "\n");
for (Map.Entry<Integer,Set<T>> mapEntry :
labelToObjs.entrySet()) {
result.append("PART " + mapEntry.getKey() + ": ");
result.append(mapEntry.getValue() + "\n");
}
return result.toString();
}
public static void main(String[] args) {
List<String> strings =
Arrays.asList("zero one two three".split(" "));
Partition<String> p = new Partition<String>(strings);
p.newPart(strings.get(3)); // move "three" to a new part
System.out.println(p);
System.out.println("Now moving all of three's part to the " +
"same part as zero.\n");
Collection<String> oldPart = p.getPart(strings.get(3));
//oldPart = Arrays.asList(new String[]{"three"}); // works fine!
p.moveAll(oldPart, p.getLabel(strings.get(0)));
System.out.println(p);
}
}
/* OUTPUT
PARTITION OF 4 ELEMENTS INTO 2 PARTS
PART 0: [two, one, zero]
PART 1: [three]
Now moving all of three's part to the same part as zero.
oldPart = [three]
things = [three]
Now doing oldPart.removeAll(things) ...
oldPart = []
things = []
PARTITION OF 4 ELEMENTS INTO 1 PART
PART 0: [two, one, zero]
*/
Using my debugger I place a breakpoint before the removeAll and I can see (as I suspected) that oldPart and things as the same collection so removing all elements clears that collection.
Your code is extremely confusing but as far as I can work out, oldPart and things are actually the same object. Set.removeAll() certainly doesn't affect its argument unless it is the same object as it's invoked on:
public boolean removeAll(Collection<?> c) {
boolean modified = false;
if (size() > c.size()) {
for (Iterator<?> i = c.iterator(); i.hasNext(); )
modified |= remove(i.next());
} else {
for (Iterator<?> i = iterator(); i.hasNext(); ) {
if (c.contains(i.next())) {
i.remove();
modified = true;
}
}
}
return modified;
}
Update:
The easy way to eliminate this is to use a copy of things in the moveAll() method. Indeed such a copy already exists.
T arbitraryThing = new ArrayList<T>(things).get(0);
This line creates but then instantly discards a copy of things. So I'd suggest replacing it with:
ArrayList<T> thingsToRemove = new ArrayList<T>(things)
T arbitraryThing = thingsToRemove.get(0);
And in the rest of the method, replace all references to things to thingsToRemove.

Categories

Resources