Rectangle packing in smallest possible area












1














I have written a Java Program to place a bunch of rectangles
into the smallest possible rectangluar area(no overlapping and rotating).



Here is a short summary:



I calculate minArea and maxArea:



minArea: minArea is sum up all given rectangle areas.



maxArea: put all given rectangles side by side, maxArea is rectangle area in which all given rectangles fit in.



Between minArea and maxArea must exist the smallest rectangular area, in which all given rectangles fit in.



For each rectangular area between minArea and maxArea I try recursiv, if all given rectangles fit in the rectangular area.



Example input/output:



(height,width of rectangles)

2,3
4,4
5,5
7,2
1,1
9,3

1 1 1 2 2 2 2 6 6 6
1 1 1 2 2 2 2 6 6 6
4 4 5 2 2 2 2 6 6 6
4 4 0 2 2 2 2 6 6 6
4 4 3 3 3 3 3 6 6 6
4 4 3 3 3 3 3 6 6 6
4 4 3 3 3 3 3 6 6 6
4 4 3 3 3 3 3 6 6 6
4 4 3 3 3 3 3 6 6 6


0 represents free space, all other numbers are for differentiating rectangles.



RectanglePacking.java:



import java.util.ArrayList;
import java.util.Scanner;
import java.awt.Point;
import java.io.IOException;


class Rectangle {

public int id;
public int width;
public int height;

public Rectangle(int height, int width, int id) {
this.width = width;
this.height = height;
this.id = id;
}
}

public class RectanglePacking {

private ArrayList<Rectangle> rectangleList = new ArrayList<Rectangle>();
private int minArea;
private int maxArea;


private int getMinArea() {

for (Rectangle r : rectangleList) {
minArea = minArea + r.height * r.width;
}
return minArea;
}


private int getMaxArea() {

int totalWidth = 0;
int height = 0;
int maxHeight = 0;

for (Rectangle r : rectangleList) {
totalWidth = totalWidth + r.width;
height = r.height;

if (height >= maxHeight) {
maxHeight = height;
}
}

maxArea = totalWidth * maxHeight;
return maxArea;
}


private ArrayList<Point> possiblePlaces(int field, int id) {

ArrayList<Point> places = new ArrayList<Point>();

for (int i = 0; i < field.length; i++) {

for (int j = 0; j < field[0].length; j++) {

if (isEmpty(field, i, j, rectangleList.get(id).height, rectangleList.get(id).width)) {
places.add(new Point(i, j));
}

}
}
return places;
}


public boolean isEmpty(int field, int x, int y, int height, int width) {

for (int i = x; i < x + height; i++) {
for (int j = y; j < y + width; j++) {

if (i > field.length - 1 || j > field[0].length - 1 || field[i][j] != 0) {
return false;
}
}
}
return true;
}


private int markRectangle(int field, int x, int y, int height, int width, int id) {

id = id + 1;

for (int i = x; i < x + height; i++) {
for (int j = y; j < y + width; j++) {

field[i][j] = id;
}
}
return field;
}


private void removeRectangle(int field, int x, int y, int height, int width) {
for (int i = x; i < x + height; i++) {
for (int j = y; j < y + width; j++) {

field[i][j] = 0;
}
}
}


private void placeRectangles(int field, int id) {

if (id == rectangleList.size()) {
printSolution(field);
System.exit(0);
}

ArrayList<Point> possiblePlaces = possiblePlaces(field, id);

for (int i = 0; i < possiblePlaces.size(); i++) {

Point p = possiblePlaces.get(i);
field = markRectangle(field, p.x, p.y, rectangleList.get(id).height, rectangleList.get(id).width, id);

placeRectangles(field, id + 1);
removeRectangle(field, p.x, p.y, rectangleList.get(id).height, rectangleList.get(id).width);

}
}


private ArrayList<Point> calcPossibleAreas() {

ArrayList<Point> possibleAreas= new ArrayList<Point>();
ArrayList<Point> factors = new ArrayList<Point>();

for (int i = getMinArea(); i <= getMaxArea(); i++) {

factors = calcFactors(i);

for (int j = 0; j < factors.size(); j++) {
possibleAreas.add(new Point(factors.get(j).x, factors.get(j).y));
}

}
return possibleAreas;
}


private ArrayList<Point> calcFactors(int number) {

ArrayList<Point> factors = new ArrayList<Point>();

for (int i = 1; i <= number; i++) {

if (number % i == 0) {
factors.add(new Point(i, number/i));
factors.add(new Point(number/i, i));
}

}
return factors;
}


private void calcMinArea() {

ArrayList<Point> possibleAreas = calcPossibleAreas();
int field;

for (int i = 0; i < possibleAreas.size(); i++) {

int x = possibleAreas.get(i).x;
int y = possibleAreas.get(i).y;

field = new int[x][y];
placeRectangles(field, 0);
}
}


private void printSolution(int field) {
System.out.println("Solution:");
for (int i = 0; i < field.length; i++) {
for (int j = 0; j < field[0].length; j++) {

System.out.print(" "+field[i][j] + " ");

}
System.out.println();
}
}

public static void main(String args) throws IOException {

RectanglePacking r = new RectanglePacking();
Scanner input = new Scanner(System.in);

int id = 1;

try {

while (true) {

String line = input.nextLine();

if (line.equals("")) {
break;
}

String split = line.split(",");

int height = Integer.parseInt(split[0]);
int width = Integer.parseInt(split[1]);

r.rectangleList.add(new Rectangle(height, width, id));
id++;
}

input.close();
} catch (Exception ex) {
ex.printStackTrace();
}

r.calcMinArea();
}
}


How can I improve my code?



Any help is really appreciated.










share|improve this question






















  • Change your algortihm to ijcai.org/Proceedings/09/Papers/092.pdf
    – RobAu
    yesterday


















1














I have written a Java Program to place a bunch of rectangles
into the smallest possible rectangluar area(no overlapping and rotating).



Here is a short summary:



I calculate minArea and maxArea:



minArea: minArea is sum up all given rectangle areas.



maxArea: put all given rectangles side by side, maxArea is rectangle area in which all given rectangles fit in.



Between minArea and maxArea must exist the smallest rectangular area, in which all given rectangles fit in.



For each rectangular area between minArea and maxArea I try recursiv, if all given rectangles fit in the rectangular area.



Example input/output:



(height,width of rectangles)

2,3
4,4
5,5
7,2
1,1
9,3

1 1 1 2 2 2 2 6 6 6
1 1 1 2 2 2 2 6 6 6
4 4 5 2 2 2 2 6 6 6
4 4 0 2 2 2 2 6 6 6
4 4 3 3 3 3 3 6 6 6
4 4 3 3 3 3 3 6 6 6
4 4 3 3 3 3 3 6 6 6
4 4 3 3 3 3 3 6 6 6
4 4 3 3 3 3 3 6 6 6


0 represents free space, all other numbers are for differentiating rectangles.



RectanglePacking.java:



import java.util.ArrayList;
import java.util.Scanner;
import java.awt.Point;
import java.io.IOException;


class Rectangle {

public int id;
public int width;
public int height;

public Rectangle(int height, int width, int id) {
this.width = width;
this.height = height;
this.id = id;
}
}

public class RectanglePacking {

private ArrayList<Rectangle> rectangleList = new ArrayList<Rectangle>();
private int minArea;
private int maxArea;


private int getMinArea() {

for (Rectangle r : rectangleList) {
minArea = minArea + r.height * r.width;
}
return minArea;
}


private int getMaxArea() {

int totalWidth = 0;
int height = 0;
int maxHeight = 0;

for (Rectangle r : rectangleList) {
totalWidth = totalWidth + r.width;
height = r.height;

if (height >= maxHeight) {
maxHeight = height;
}
}

maxArea = totalWidth * maxHeight;
return maxArea;
}


private ArrayList<Point> possiblePlaces(int field, int id) {

ArrayList<Point> places = new ArrayList<Point>();

for (int i = 0; i < field.length; i++) {

for (int j = 0; j < field[0].length; j++) {

if (isEmpty(field, i, j, rectangleList.get(id).height, rectangleList.get(id).width)) {
places.add(new Point(i, j));
}

}
}
return places;
}


public boolean isEmpty(int field, int x, int y, int height, int width) {

for (int i = x; i < x + height; i++) {
for (int j = y; j < y + width; j++) {

if (i > field.length - 1 || j > field[0].length - 1 || field[i][j] != 0) {
return false;
}
}
}
return true;
}


private int markRectangle(int field, int x, int y, int height, int width, int id) {

id = id + 1;

for (int i = x; i < x + height; i++) {
for (int j = y; j < y + width; j++) {

field[i][j] = id;
}
}
return field;
}


private void removeRectangle(int field, int x, int y, int height, int width) {
for (int i = x; i < x + height; i++) {
for (int j = y; j < y + width; j++) {

field[i][j] = 0;
}
}
}


private void placeRectangles(int field, int id) {

if (id == rectangleList.size()) {
printSolution(field);
System.exit(0);
}

ArrayList<Point> possiblePlaces = possiblePlaces(field, id);

for (int i = 0; i < possiblePlaces.size(); i++) {

Point p = possiblePlaces.get(i);
field = markRectangle(field, p.x, p.y, rectangleList.get(id).height, rectangleList.get(id).width, id);

placeRectangles(field, id + 1);
removeRectangle(field, p.x, p.y, rectangleList.get(id).height, rectangleList.get(id).width);

}
}


private ArrayList<Point> calcPossibleAreas() {

ArrayList<Point> possibleAreas= new ArrayList<Point>();
ArrayList<Point> factors = new ArrayList<Point>();

for (int i = getMinArea(); i <= getMaxArea(); i++) {

factors = calcFactors(i);

for (int j = 0; j < factors.size(); j++) {
possibleAreas.add(new Point(factors.get(j).x, factors.get(j).y));
}

}
return possibleAreas;
}


private ArrayList<Point> calcFactors(int number) {

ArrayList<Point> factors = new ArrayList<Point>();

for (int i = 1; i <= number; i++) {

if (number % i == 0) {
factors.add(new Point(i, number/i));
factors.add(new Point(number/i, i));
}

}
return factors;
}


private void calcMinArea() {

ArrayList<Point> possibleAreas = calcPossibleAreas();
int field;

for (int i = 0; i < possibleAreas.size(); i++) {

int x = possibleAreas.get(i).x;
int y = possibleAreas.get(i).y;

field = new int[x][y];
placeRectangles(field, 0);
}
}


private void printSolution(int field) {
System.out.println("Solution:");
for (int i = 0; i < field.length; i++) {
for (int j = 0; j < field[0].length; j++) {

System.out.print(" "+field[i][j] + " ");

}
System.out.println();
}
}

public static void main(String args) throws IOException {

RectanglePacking r = new RectanglePacking();
Scanner input = new Scanner(System.in);

int id = 1;

try {

while (true) {

String line = input.nextLine();

if (line.equals("")) {
break;
}

String split = line.split(",");

int height = Integer.parseInt(split[0]);
int width = Integer.parseInt(split[1]);

r.rectangleList.add(new Rectangle(height, width, id));
id++;
}

input.close();
} catch (Exception ex) {
ex.printStackTrace();
}

r.calcMinArea();
}
}


How can I improve my code?



Any help is really appreciated.










share|improve this question






















  • Change your algortihm to ijcai.org/Proceedings/09/Papers/092.pdf
    – RobAu
    yesterday
















1












1








1







I have written a Java Program to place a bunch of rectangles
into the smallest possible rectangluar area(no overlapping and rotating).



Here is a short summary:



I calculate minArea and maxArea:



minArea: minArea is sum up all given rectangle areas.



maxArea: put all given rectangles side by side, maxArea is rectangle area in which all given rectangles fit in.



Between minArea and maxArea must exist the smallest rectangular area, in which all given rectangles fit in.



For each rectangular area between minArea and maxArea I try recursiv, if all given rectangles fit in the rectangular area.



Example input/output:



(height,width of rectangles)

2,3
4,4
5,5
7,2
1,1
9,3

1 1 1 2 2 2 2 6 6 6
1 1 1 2 2 2 2 6 6 6
4 4 5 2 2 2 2 6 6 6
4 4 0 2 2 2 2 6 6 6
4 4 3 3 3 3 3 6 6 6
4 4 3 3 3 3 3 6 6 6
4 4 3 3 3 3 3 6 6 6
4 4 3 3 3 3 3 6 6 6
4 4 3 3 3 3 3 6 6 6


0 represents free space, all other numbers are for differentiating rectangles.



RectanglePacking.java:



import java.util.ArrayList;
import java.util.Scanner;
import java.awt.Point;
import java.io.IOException;


class Rectangle {

public int id;
public int width;
public int height;

public Rectangle(int height, int width, int id) {
this.width = width;
this.height = height;
this.id = id;
}
}

public class RectanglePacking {

private ArrayList<Rectangle> rectangleList = new ArrayList<Rectangle>();
private int minArea;
private int maxArea;


private int getMinArea() {

for (Rectangle r : rectangleList) {
minArea = minArea + r.height * r.width;
}
return minArea;
}


private int getMaxArea() {

int totalWidth = 0;
int height = 0;
int maxHeight = 0;

for (Rectangle r : rectangleList) {
totalWidth = totalWidth + r.width;
height = r.height;

if (height >= maxHeight) {
maxHeight = height;
}
}

maxArea = totalWidth * maxHeight;
return maxArea;
}


private ArrayList<Point> possiblePlaces(int field, int id) {

ArrayList<Point> places = new ArrayList<Point>();

for (int i = 0; i < field.length; i++) {

for (int j = 0; j < field[0].length; j++) {

if (isEmpty(field, i, j, rectangleList.get(id).height, rectangleList.get(id).width)) {
places.add(new Point(i, j));
}

}
}
return places;
}


public boolean isEmpty(int field, int x, int y, int height, int width) {

for (int i = x; i < x + height; i++) {
for (int j = y; j < y + width; j++) {

if (i > field.length - 1 || j > field[0].length - 1 || field[i][j] != 0) {
return false;
}
}
}
return true;
}


private int markRectangle(int field, int x, int y, int height, int width, int id) {

id = id + 1;

for (int i = x; i < x + height; i++) {
for (int j = y; j < y + width; j++) {

field[i][j] = id;
}
}
return field;
}


private void removeRectangle(int field, int x, int y, int height, int width) {
for (int i = x; i < x + height; i++) {
for (int j = y; j < y + width; j++) {

field[i][j] = 0;
}
}
}


private void placeRectangles(int field, int id) {

if (id == rectangleList.size()) {
printSolution(field);
System.exit(0);
}

ArrayList<Point> possiblePlaces = possiblePlaces(field, id);

for (int i = 0; i < possiblePlaces.size(); i++) {

Point p = possiblePlaces.get(i);
field = markRectangle(field, p.x, p.y, rectangleList.get(id).height, rectangleList.get(id).width, id);

placeRectangles(field, id + 1);
removeRectangle(field, p.x, p.y, rectangleList.get(id).height, rectangleList.get(id).width);

}
}


private ArrayList<Point> calcPossibleAreas() {

ArrayList<Point> possibleAreas= new ArrayList<Point>();
ArrayList<Point> factors = new ArrayList<Point>();

for (int i = getMinArea(); i <= getMaxArea(); i++) {

factors = calcFactors(i);

for (int j = 0; j < factors.size(); j++) {
possibleAreas.add(new Point(factors.get(j).x, factors.get(j).y));
}

}
return possibleAreas;
}


private ArrayList<Point> calcFactors(int number) {

ArrayList<Point> factors = new ArrayList<Point>();

for (int i = 1; i <= number; i++) {

if (number % i == 0) {
factors.add(new Point(i, number/i));
factors.add(new Point(number/i, i));
}

}
return factors;
}


private void calcMinArea() {

ArrayList<Point> possibleAreas = calcPossibleAreas();
int field;

for (int i = 0; i < possibleAreas.size(); i++) {

int x = possibleAreas.get(i).x;
int y = possibleAreas.get(i).y;

field = new int[x][y];
placeRectangles(field, 0);
}
}


private void printSolution(int field) {
System.out.println("Solution:");
for (int i = 0; i < field.length; i++) {
for (int j = 0; j < field[0].length; j++) {

System.out.print(" "+field[i][j] + " ");

}
System.out.println();
}
}

public static void main(String args) throws IOException {

RectanglePacking r = new RectanglePacking();
Scanner input = new Scanner(System.in);

int id = 1;

try {

while (true) {

String line = input.nextLine();

if (line.equals("")) {
break;
}

String split = line.split(",");

int height = Integer.parseInt(split[0]);
int width = Integer.parseInt(split[1]);

r.rectangleList.add(new Rectangle(height, width, id));
id++;
}

input.close();
} catch (Exception ex) {
ex.printStackTrace();
}

r.calcMinArea();
}
}


How can I improve my code?



Any help is really appreciated.










share|improve this question













I have written a Java Program to place a bunch of rectangles
into the smallest possible rectangluar area(no overlapping and rotating).



Here is a short summary:



I calculate minArea and maxArea:



minArea: minArea is sum up all given rectangle areas.



maxArea: put all given rectangles side by side, maxArea is rectangle area in which all given rectangles fit in.



Between minArea and maxArea must exist the smallest rectangular area, in which all given rectangles fit in.



For each rectangular area between minArea and maxArea I try recursiv, if all given rectangles fit in the rectangular area.



Example input/output:



(height,width of rectangles)

2,3
4,4
5,5
7,2
1,1
9,3

1 1 1 2 2 2 2 6 6 6
1 1 1 2 2 2 2 6 6 6
4 4 5 2 2 2 2 6 6 6
4 4 0 2 2 2 2 6 6 6
4 4 3 3 3 3 3 6 6 6
4 4 3 3 3 3 3 6 6 6
4 4 3 3 3 3 3 6 6 6
4 4 3 3 3 3 3 6 6 6
4 4 3 3 3 3 3 6 6 6


0 represents free space, all other numbers are for differentiating rectangles.



RectanglePacking.java:



import java.util.ArrayList;
import java.util.Scanner;
import java.awt.Point;
import java.io.IOException;


class Rectangle {

public int id;
public int width;
public int height;

public Rectangle(int height, int width, int id) {
this.width = width;
this.height = height;
this.id = id;
}
}

public class RectanglePacking {

private ArrayList<Rectangle> rectangleList = new ArrayList<Rectangle>();
private int minArea;
private int maxArea;


private int getMinArea() {

for (Rectangle r : rectangleList) {
minArea = minArea + r.height * r.width;
}
return minArea;
}


private int getMaxArea() {

int totalWidth = 0;
int height = 0;
int maxHeight = 0;

for (Rectangle r : rectangleList) {
totalWidth = totalWidth + r.width;
height = r.height;

if (height >= maxHeight) {
maxHeight = height;
}
}

maxArea = totalWidth * maxHeight;
return maxArea;
}


private ArrayList<Point> possiblePlaces(int field, int id) {

ArrayList<Point> places = new ArrayList<Point>();

for (int i = 0; i < field.length; i++) {

for (int j = 0; j < field[0].length; j++) {

if (isEmpty(field, i, j, rectangleList.get(id).height, rectangleList.get(id).width)) {
places.add(new Point(i, j));
}

}
}
return places;
}


public boolean isEmpty(int field, int x, int y, int height, int width) {

for (int i = x; i < x + height; i++) {
for (int j = y; j < y + width; j++) {

if (i > field.length - 1 || j > field[0].length - 1 || field[i][j] != 0) {
return false;
}
}
}
return true;
}


private int markRectangle(int field, int x, int y, int height, int width, int id) {

id = id + 1;

for (int i = x; i < x + height; i++) {
for (int j = y; j < y + width; j++) {

field[i][j] = id;
}
}
return field;
}


private void removeRectangle(int field, int x, int y, int height, int width) {
for (int i = x; i < x + height; i++) {
for (int j = y; j < y + width; j++) {

field[i][j] = 0;
}
}
}


private void placeRectangles(int field, int id) {

if (id == rectangleList.size()) {
printSolution(field);
System.exit(0);
}

ArrayList<Point> possiblePlaces = possiblePlaces(field, id);

for (int i = 0; i < possiblePlaces.size(); i++) {

Point p = possiblePlaces.get(i);
field = markRectangle(field, p.x, p.y, rectangleList.get(id).height, rectangleList.get(id).width, id);

placeRectangles(field, id + 1);
removeRectangle(field, p.x, p.y, rectangleList.get(id).height, rectangleList.get(id).width);

}
}


private ArrayList<Point> calcPossibleAreas() {

ArrayList<Point> possibleAreas= new ArrayList<Point>();
ArrayList<Point> factors = new ArrayList<Point>();

for (int i = getMinArea(); i <= getMaxArea(); i++) {

factors = calcFactors(i);

for (int j = 0; j < factors.size(); j++) {
possibleAreas.add(new Point(factors.get(j).x, factors.get(j).y));
}

}
return possibleAreas;
}


private ArrayList<Point> calcFactors(int number) {

ArrayList<Point> factors = new ArrayList<Point>();

for (int i = 1; i <= number; i++) {

if (number % i == 0) {
factors.add(new Point(i, number/i));
factors.add(new Point(number/i, i));
}

}
return factors;
}


private void calcMinArea() {

ArrayList<Point> possibleAreas = calcPossibleAreas();
int field;

for (int i = 0; i < possibleAreas.size(); i++) {

int x = possibleAreas.get(i).x;
int y = possibleAreas.get(i).y;

field = new int[x][y];
placeRectangles(field, 0);
}
}


private void printSolution(int field) {
System.out.println("Solution:");
for (int i = 0; i < field.length; i++) {
for (int j = 0; j < field[0].length; j++) {

System.out.print(" "+field[i][j] + " ");

}
System.out.println();
}
}

public static void main(String args) throws IOException {

RectanglePacking r = new RectanglePacking();
Scanner input = new Scanner(System.in);

int id = 1;

try {

while (true) {

String line = input.nextLine();

if (line.equals("")) {
break;
}

String split = line.split(",");

int height = Integer.parseInt(split[0]);
int width = Integer.parseInt(split[1]);

r.rectangleList.add(new Rectangle(height, width, id));
id++;
}

input.close();
} catch (Exception ex) {
ex.printStackTrace();
}

r.calcMinArea();
}
}


How can I improve my code?



Any help is really appreciated.







java performance recursion






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Jan 1 at 20:25









Marten

1607




1607












  • Change your algortihm to ijcai.org/Proceedings/09/Papers/092.pdf
    – RobAu
    yesterday




















  • Change your algortihm to ijcai.org/Proceedings/09/Papers/092.pdf
    – RobAu
    yesterday


















Change your algortihm to ijcai.org/Proceedings/09/Papers/092.pdf
– RobAu
yesterday






Change your algortihm to ijcai.org/Proceedings/09/Papers/092.pdf
– RobAu
yesterday












1 Answer
1






active

oldest

votes


















1















For each rectangular area between minArea and maxArea I [test recursively] if all given rectangles fit in the rectangular area.




My first comment is that this subproblem seems like a perfect application for Dancing Links.
It won't solve your entire problem (the "area minimization" problem), but it will very quickly solve your subproblem (the "can I fit all the rectangles into this other rectangle" problem).





It is weird that your markRectangle function returns a value (the value of field, which is just its first parameter), but your removeRectangle function returns void. I would expect both of them to return void.





Let's see how you compute "each rectangular area between minArea and maxArea". Slightly reformatted for whitespace:



private ArrayList<Point> calcPossibleAreas() {
ArrayList<Point> possibleAreas = new ArrayList<Point>();
ArrayList<Point> factors = new ArrayList<Point>();
for (int i = getMinArea(); i <= getMaxArea(); ++i) {
factors = calcFactors(i);
for (int j = 0; j < factors.size(); ++j) {
possibleAreas.add(new Point(factors.get(j).x, factors.get(j).y));
}
}
return possibleAreas;
}


Calculating getMaxArea() every time through the loop is silly. Also, you set factors to new ArrayList<Point>() at the top of the function, and then immediately throw away that value by assigning it a new value inside the loop. So at the bare minimum, I'd expect



private ArrayList<Point> calcPossibleAreas() {
ArrayList<Point> possibleAreas = new ArrayList<Point>();
int maxArea = getMaxArea();
for (int i = getMinArea(); i <= maxArea; ++i) {
ArrayList<Point> factors = calcFactors(i);
for (int j = 0; j < factors.size(); ++j) {
possibleAreas.add(factors.get(j));
}
}
return possibleAreas;
}


And then (AFAIK) you can use Java 5's for-each loop:



private ArrayList<Point> calcPossibleAreas() {
ArrayList<Point> possibleAreas = new ArrayList<Point>();
int maxArea = getMaxArea();
for (int i = getMinArea(); i <= maxArea; ++i) {
for (Point factor : calcFactors(i)) {
possibleAreas.add(factor);
}
}
return possibleAreas;
}


But all that calcFactors is expensive; and you wind up with a lot of 2xN rectangles that can't possibly be filled. So I would rather write it like this:



private ArrayList<Point> calcPossibleAreas() {
ArrayList<Point> possibleAreas = new ArrayList<Point>();
int minWidth = 0;
int maxWidth = 0;
int minHeight = 0;
int maxHeight = 0;
int minArea = 0;
for (Rectangle r : rectangleList) {
minWidth = Math.max(minWidth, r.width);
minHeight = Math.max(minHeight, r.height);
maxWidth += r.width;
maxHeight += r.height;
minArea += r.width * r.height;
}
int maxArea = Math.min(maxWidth * minHeight, minWidth * maxHeight);
for (int x = minWidth; x <= maxWidth; ++x) {
for (int y = minHeight; y <= maxHeight; ++y) {
if (minArea <= x * y && x * y <= maxArea) {
possibleAreas.add(new Point(x, y));
}
}
}
// Now sort possibleAreas so we'll try the smaller rectangles first.
Collections.sort(possibleAreas, new Comparator<Point>() {
@Override
public int compare(Point lhs, Point rhs) {
int la = (lhs.x * lhs.y), ra = (rhs.x * rhs.y);
return (la < ra) ? -1 : (la > ra);
}
});
}




Of course, calcPossibleAreas() is just a preliminary step that will be very fast compared to placeRectangles(). So Amdahl's Law says that you (and I ;)) should spend time optimizing placeRectangles() rather than calcPossibleAreas(). I would recommend looking at some way to eliminate the O(w*h) writes to field — is there some way you can express "all these positions are filled" without actually doing the O(w*h) writes? Vice versa, is there some way you can test "is this a valid placement for my current rectangle" without doing O(w*h) reads?



Rather than looking for places to put each rectangle, it might be better to look for ways to fill each point in the target. Pretend you have your set of rectangles, plus just enough "spare" 1x1 rectangles to fill up the empty spaces. If you try to fill all the points in the target, working downward from the upper left corner, then you have to try each rectangle in only one position — and you can skip rectangles that are too wide or tall to fit — and if none of your rectangles fit, and you're out of spare 1x1s, then you know it's time to backtrack. (This is essentially how Dancing Links would do it.)






share|improve this answer





















    Your Answer





    StackExchange.ifUsing("editor", function () {
    return StackExchange.using("mathjaxEditing", function () {
    StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
    StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
    });
    });
    }, "mathjax-editing");

    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "196"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: false,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: null,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f210711%2frectangle-packing-in-smallest-possible-area%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    1 Answer
    1






    active

    oldest

    votes








    1 Answer
    1






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    1















    For each rectangular area between minArea and maxArea I [test recursively] if all given rectangles fit in the rectangular area.




    My first comment is that this subproblem seems like a perfect application for Dancing Links.
    It won't solve your entire problem (the "area minimization" problem), but it will very quickly solve your subproblem (the "can I fit all the rectangles into this other rectangle" problem).





    It is weird that your markRectangle function returns a value (the value of field, which is just its first parameter), but your removeRectangle function returns void. I would expect both of them to return void.





    Let's see how you compute "each rectangular area between minArea and maxArea". Slightly reformatted for whitespace:



    private ArrayList<Point> calcPossibleAreas() {
    ArrayList<Point> possibleAreas = new ArrayList<Point>();
    ArrayList<Point> factors = new ArrayList<Point>();
    for (int i = getMinArea(); i <= getMaxArea(); ++i) {
    factors = calcFactors(i);
    for (int j = 0; j < factors.size(); ++j) {
    possibleAreas.add(new Point(factors.get(j).x, factors.get(j).y));
    }
    }
    return possibleAreas;
    }


    Calculating getMaxArea() every time through the loop is silly. Also, you set factors to new ArrayList<Point>() at the top of the function, and then immediately throw away that value by assigning it a new value inside the loop. So at the bare minimum, I'd expect



    private ArrayList<Point> calcPossibleAreas() {
    ArrayList<Point> possibleAreas = new ArrayList<Point>();
    int maxArea = getMaxArea();
    for (int i = getMinArea(); i <= maxArea; ++i) {
    ArrayList<Point> factors = calcFactors(i);
    for (int j = 0; j < factors.size(); ++j) {
    possibleAreas.add(factors.get(j));
    }
    }
    return possibleAreas;
    }


    And then (AFAIK) you can use Java 5's for-each loop:



    private ArrayList<Point> calcPossibleAreas() {
    ArrayList<Point> possibleAreas = new ArrayList<Point>();
    int maxArea = getMaxArea();
    for (int i = getMinArea(); i <= maxArea; ++i) {
    for (Point factor : calcFactors(i)) {
    possibleAreas.add(factor);
    }
    }
    return possibleAreas;
    }


    But all that calcFactors is expensive; and you wind up with a lot of 2xN rectangles that can't possibly be filled. So I would rather write it like this:



    private ArrayList<Point> calcPossibleAreas() {
    ArrayList<Point> possibleAreas = new ArrayList<Point>();
    int minWidth = 0;
    int maxWidth = 0;
    int minHeight = 0;
    int maxHeight = 0;
    int minArea = 0;
    for (Rectangle r : rectangleList) {
    minWidth = Math.max(minWidth, r.width);
    minHeight = Math.max(minHeight, r.height);
    maxWidth += r.width;
    maxHeight += r.height;
    minArea += r.width * r.height;
    }
    int maxArea = Math.min(maxWidth * minHeight, minWidth * maxHeight);
    for (int x = minWidth; x <= maxWidth; ++x) {
    for (int y = minHeight; y <= maxHeight; ++y) {
    if (minArea <= x * y && x * y <= maxArea) {
    possibleAreas.add(new Point(x, y));
    }
    }
    }
    // Now sort possibleAreas so we'll try the smaller rectangles first.
    Collections.sort(possibleAreas, new Comparator<Point>() {
    @Override
    public int compare(Point lhs, Point rhs) {
    int la = (lhs.x * lhs.y), ra = (rhs.x * rhs.y);
    return (la < ra) ? -1 : (la > ra);
    }
    });
    }




    Of course, calcPossibleAreas() is just a preliminary step that will be very fast compared to placeRectangles(). So Amdahl's Law says that you (and I ;)) should spend time optimizing placeRectangles() rather than calcPossibleAreas(). I would recommend looking at some way to eliminate the O(w*h) writes to field — is there some way you can express "all these positions are filled" without actually doing the O(w*h) writes? Vice versa, is there some way you can test "is this a valid placement for my current rectangle" without doing O(w*h) reads?



    Rather than looking for places to put each rectangle, it might be better to look for ways to fill each point in the target. Pretend you have your set of rectangles, plus just enough "spare" 1x1 rectangles to fill up the empty spaces. If you try to fill all the points in the target, working downward from the upper left corner, then you have to try each rectangle in only one position — and you can skip rectangles that are too wide or tall to fit — and if none of your rectangles fit, and you're out of spare 1x1s, then you know it's time to backtrack. (This is essentially how Dancing Links would do it.)






    share|improve this answer


























      1















      For each rectangular area between minArea and maxArea I [test recursively] if all given rectangles fit in the rectangular area.




      My first comment is that this subproblem seems like a perfect application for Dancing Links.
      It won't solve your entire problem (the "area minimization" problem), but it will very quickly solve your subproblem (the "can I fit all the rectangles into this other rectangle" problem).





      It is weird that your markRectangle function returns a value (the value of field, which is just its first parameter), but your removeRectangle function returns void. I would expect both of them to return void.





      Let's see how you compute "each rectangular area between minArea and maxArea". Slightly reformatted for whitespace:



      private ArrayList<Point> calcPossibleAreas() {
      ArrayList<Point> possibleAreas = new ArrayList<Point>();
      ArrayList<Point> factors = new ArrayList<Point>();
      for (int i = getMinArea(); i <= getMaxArea(); ++i) {
      factors = calcFactors(i);
      for (int j = 0; j < factors.size(); ++j) {
      possibleAreas.add(new Point(factors.get(j).x, factors.get(j).y));
      }
      }
      return possibleAreas;
      }


      Calculating getMaxArea() every time through the loop is silly. Also, you set factors to new ArrayList<Point>() at the top of the function, and then immediately throw away that value by assigning it a new value inside the loop. So at the bare minimum, I'd expect



      private ArrayList<Point> calcPossibleAreas() {
      ArrayList<Point> possibleAreas = new ArrayList<Point>();
      int maxArea = getMaxArea();
      for (int i = getMinArea(); i <= maxArea; ++i) {
      ArrayList<Point> factors = calcFactors(i);
      for (int j = 0; j < factors.size(); ++j) {
      possibleAreas.add(factors.get(j));
      }
      }
      return possibleAreas;
      }


      And then (AFAIK) you can use Java 5's for-each loop:



      private ArrayList<Point> calcPossibleAreas() {
      ArrayList<Point> possibleAreas = new ArrayList<Point>();
      int maxArea = getMaxArea();
      for (int i = getMinArea(); i <= maxArea; ++i) {
      for (Point factor : calcFactors(i)) {
      possibleAreas.add(factor);
      }
      }
      return possibleAreas;
      }


      But all that calcFactors is expensive; and you wind up with a lot of 2xN rectangles that can't possibly be filled. So I would rather write it like this:



      private ArrayList<Point> calcPossibleAreas() {
      ArrayList<Point> possibleAreas = new ArrayList<Point>();
      int minWidth = 0;
      int maxWidth = 0;
      int minHeight = 0;
      int maxHeight = 0;
      int minArea = 0;
      for (Rectangle r : rectangleList) {
      minWidth = Math.max(minWidth, r.width);
      minHeight = Math.max(minHeight, r.height);
      maxWidth += r.width;
      maxHeight += r.height;
      minArea += r.width * r.height;
      }
      int maxArea = Math.min(maxWidth * minHeight, minWidth * maxHeight);
      for (int x = minWidth; x <= maxWidth; ++x) {
      for (int y = minHeight; y <= maxHeight; ++y) {
      if (minArea <= x * y && x * y <= maxArea) {
      possibleAreas.add(new Point(x, y));
      }
      }
      }
      // Now sort possibleAreas so we'll try the smaller rectangles first.
      Collections.sort(possibleAreas, new Comparator<Point>() {
      @Override
      public int compare(Point lhs, Point rhs) {
      int la = (lhs.x * lhs.y), ra = (rhs.x * rhs.y);
      return (la < ra) ? -1 : (la > ra);
      }
      });
      }




      Of course, calcPossibleAreas() is just a preliminary step that will be very fast compared to placeRectangles(). So Amdahl's Law says that you (and I ;)) should spend time optimizing placeRectangles() rather than calcPossibleAreas(). I would recommend looking at some way to eliminate the O(w*h) writes to field — is there some way you can express "all these positions are filled" without actually doing the O(w*h) writes? Vice versa, is there some way you can test "is this a valid placement for my current rectangle" without doing O(w*h) reads?



      Rather than looking for places to put each rectangle, it might be better to look for ways to fill each point in the target. Pretend you have your set of rectangles, plus just enough "spare" 1x1 rectangles to fill up the empty spaces. If you try to fill all the points in the target, working downward from the upper left corner, then you have to try each rectangle in only one position — and you can skip rectangles that are too wide or tall to fit — and if none of your rectangles fit, and you're out of spare 1x1s, then you know it's time to backtrack. (This is essentially how Dancing Links would do it.)






      share|improve this answer
























        1












        1








        1







        For each rectangular area between minArea and maxArea I [test recursively] if all given rectangles fit in the rectangular area.




        My first comment is that this subproblem seems like a perfect application for Dancing Links.
        It won't solve your entire problem (the "area minimization" problem), but it will very quickly solve your subproblem (the "can I fit all the rectangles into this other rectangle" problem).





        It is weird that your markRectangle function returns a value (the value of field, which is just its first parameter), but your removeRectangle function returns void. I would expect both of them to return void.





        Let's see how you compute "each rectangular area between minArea and maxArea". Slightly reformatted for whitespace:



        private ArrayList<Point> calcPossibleAreas() {
        ArrayList<Point> possibleAreas = new ArrayList<Point>();
        ArrayList<Point> factors = new ArrayList<Point>();
        for (int i = getMinArea(); i <= getMaxArea(); ++i) {
        factors = calcFactors(i);
        for (int j = 0; j < factors.size(); ++j) {
        possibleAreas.add(new Point(factors.get(j).x, factors.get(j).y));
        }
        }
        return possibleAreas;
        }


        Calculating getMaxArea() every time through the loop is silly. Also, you set factors to new ArrayList<Point>() at the top of the function, and then immediately throw away that value by assigning it a new value inside the loop. So at the bare minimum, I'd expect



        private ArrayList<Point> calcPossibleAreas() {
        ArrayList<Point> possibleAreas = new ArrayList<Point>();
        int maxArea = getMaxArea();
        for (int i = getMinArea(); i <= maxArea; ++i) {
        ArrayList<Point> factors = calcFactors(i);
        for (int j = 0; j < factors.size(); ++j) {
        possibleAreas.add(factors.get(j));
        }
        }
        return possibleAreas;
        }


        And then (AFAIK) you can use Java 5's for-each loop:



        private ArrayList<Point> calcPossibleAreas() {
        ArrayList<Point> possibleAreas = new ArrayList<Point>();
        int maxArea = getMaxArea();
        for (int i = getMinArea(); i <= maxArea; ++i) {
        for (Point factor : calcFactors(i)) {
        possibleAreas.add(factor);
        }
        }
        return possibleAreas;
        }


        But all that calcFactors is expensive; and you wind up with a lot of 2xN rectangles that can't possibly be filled. So I would rather write it like this:



        private ArrayList<Point> calcPossibleAreas() {
        ArrayList<Point> possibleAreas = new ArrayList<Point>();
        int minWidth = 0;
        int maxWidth = 0;
        int minHeight = 0;
        int maxHeight = 0;
        int minArea = 0;
        for (Rectangle r : rectangleList) {
        minWidth = Math.max(minWidth, r.width);
        minHeight = Math.max(minHeight, r.height);
        maxWidth += r.width;
        maxHeight += r.height;
        minArea += r.width * r.height;
        }
        int maxArea = Math.min(maxWidth * minHeight, minWidth * maxHeight);
        for (int x = minWidth; x <= maxWidth; ++x) {
        for (int y = minHeight; y <= maxHeight; ++y) {
        if (minArea <= x * y && x * y <= maxArea) {
        possibleAreas.add(new Point(x, y));
        }
        }
        }
        // Now sort possibleAreas so we'll try the smaller rectangles first.
        Collections.sort(possibleAreas, new Comparator<Point>() {
        @Override
        public int compare(Point lhs, Point rhs) {
        int la = (lhs.x * lhs.y), ra = (rhs.x * rhs.y);
        return (la < ra) ? -1 : (la > ra);
        }
        });
        }




        Of course, calcPossibleAreas() is just a preliminary step that will be very fast compared to placeRectangles(). So Amdahl's Law says that you (and I ;)) should spend time optimizing placeRectangles() rather than calcPossibleAreas(). I would recommend looking at some way to eliminate the O(w*h) writes to field — is there some way you can express "all these positions are filled" without actually doing the O(w*h) writes? Vice versa, is there some way you can test "is this a valid placement for my current rectangle" without doing O(w*h) reads?



        Rather than looking for places to put each rectangle, it might be better to look for ways to fill each point in the target. Pretend you have your set of rectangles, plus just enough "spare" 1x1 rectangles to fill up the empty spaces. If you try to fill all the points in the target, working downward from the upper left corner, then you have to try each rectangle in only one position — and you can skip rectangles that are too wide or tall to fit — and if none of your rectangles fit, and you're out of spare 1x1s, then you know it's time to backtrack. (This is essentially how Dancing Links would do it.)






        share|improve this answer













        For each rectangular area between minArea and maxArea I [test recursively] if all given rectangles fit in the rectangular area.




        My first comment is that this subproblem seems like a perfect application for Dancing Links.
        It won't solve your entire problem (the "area minimization" problem), but it will very quickly solve your subproblem (the "can I fit all the rectangles into this other rectangle" problem).





        It is weird that your markRectangle function returns a value (the value of field, which is just its first parameter), but your removeRectangle function returns void. I would expect both of them to return void.





        Let's see how you compute "each rectangular area between minArea and maxArea". Slightly reformatted for whitespace:



        private ArrayList<Point> calcPossibleAreas() {
        ArrayList<Point> possibleAreas = new ArrayList<Point>();
        ArrayList<Point> factors = new ArrayList<Point>();
        for (int i = getMinArea(); i <= getMaxArea(); ++i) {
        factors = calcFactors(i);
        for (int j = 0; j < factors.size(); ++j) {
        possibleAreas.add(new Point(factors.get(j).x, factors.get(j).y));
        }
        }
        return possibleAreas;
        }


        Calculating getMaxArea() every time through the loop is silly. Also, you set factors to new ArrayList<Point>() at the top of the function, and then immediately throw away that value by assigning it a new value inside the loop. So at the bare minimum, I'd expect



        private ArrayList<Point> calcPossibleAreas() {
        ArrayList<Point> possibleAreas = new ArrayList<Point>();
        int maxArea = getMaxArea();
        for (int i = getMinArea(); i <= maxArea; ++i) {
        ArrayList<Point> factors = calcFactors(i);
        for (int j = 0; j < factors.size(); ++j) {
        possibleAreas.add(factors.get(j));
        }
        }
        return possibleAreas;
        }


        And then (AFAIK) you can use Java 5's for-each loop:



        private ArrayList<Point> calcPossibleAreas() {
        ArrayList<Point> possibleAreas = new ArrayList<Point>();
        int maxArea = getMaxArea();
        for (int i = getMinArea(); i <= maxArea; ++i) {
        for (Point factor : calcFactors(i)) {
        possibleAreas.add(factor);
        }
        }
        return possibleAreas;
        }


        But all that calcFactors is expensive; and you wind up with a lot of 2xN rectangles that can't possibly be filled. So I would rather write it like this:



        private ArrayList<Point> calcPossibleAreas() {
        ArrayList<Point> possibleAreas = new ArrayList<Point>();
        int minWidth = 0;
        int maxWidth = 0;
        int minHeight = 0;
        int maxHeight = 0;
        int minArea = 0;
        for (Rectangle r : rectangleList) {
        minWidth = Math.max(minWidth, r.width);
        minHeight = Math.max(minHeight, r.height);
        maxWidth += r.width;
        maxHeight += r.height;
        minArea += r.width * r.height;
        }
        int maxArea = Math.min(maxWidth * minHeight, minWidth * maxHeight);
        for (int x = minWidth; x <= maxWidth; ++x) {
        for (int y = minHeight; y <= maxHeight; ++y) {
        if (minArea <= x * y && x * y <= maxArea) {
        possibleAreas.add(new Point(x, y));
        }
        }
        }
        // Now sort possibleAreas so we'll try the smaller rectangles first.
        Collections.sort(possibleAreas, new Comparator<Point>() {
        @Override
        public int compare(Point lhs, Point rhs) {
        int la = (lhs.x * lhs.y), ra = (rhs.x * rhs.y);
        return (la < ra) ? -1 : (la > ra);
        }
        });
        }




        Of course, calcPossibleAreas() is just a preliminary step that will be very fast compared to placeRectangles(). So Amdahl's Law says that you (and I ;)) should spend time optimizing placeRectangles() rather than calcPossibleAreas(). I would recommend looking at some way to eliminate the O(w*h) writes to field — is there some way you can express "all these positions are filled" without actually doing the O(w*h) writes? Vice versa, is there some way you can test "is this a valid placement for my current rectangle" without doing O(w*h) reads?



        Rather than looking for places to put each rectangle, it might be better to look for ways to fill each point in the target. Pretend you have your set of rectangles, plus just enough "spare" 1x1 rectangles to fill up the empty spaces. If you try to fill all the points in the target, working downward from the upper left corner, then you have to try each rectangle in only one position — and you can skip rectangles that are too wide or tall to fit — and if none of your rectangles fit, and you're out of spare 1x1s, then you know it's time to backtrack. (This is essentially how Dancing Links would do it.)







        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered 2 days ago









        Quuxplusone

        11.4k11959




        11.4k11959






























            draft saved

            draft discarded




















































            Thanks for contributing an answer to Code Review Stack Exchange!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            Use MathJax to format equations. MathJax reference.


            To learn more, see our tips on writing great answers.





            Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


            Please pay close attention to the following guidance:


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f210711%2frectangle-packing-in-smallest-possible-area%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            Сан-Квентин

            8-я гвардейская общевойсковая армия

            Алькесар