Created
September 26, 2012 21:49
-
-
Save hsanchez/3790836 to your computer and use it in GitHub Desktop.
It allows us to get an optimal placement of "clusters of parts" on a grid using backtracking and incremental improvement ideas.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.util.ArrayList; | |
import java.util.Arrays; | |
import java.util.List; | |
import java.util.Random; | |
public class ItemsPlacement { | |
// Area where we will placing parts | |
static class Area { | |
int x = 0; | |
int y = 0; | |
int w = 0; | |
int h = 0; | |
Cluster cluster = new Cluster(); | |
@Override public String toString() { | |
return String.format("Area(x=%d, y=%d, w=%d, h=%d)", x, y, w, h); | |
} | |
} | |
static class Solution { | |
Area[] current = new Area[0]; | |
} | |
// Part of a Robot | |
static class Part { | |
String name = null; | |
@Override public String toString() { | |
return String.format("Part(name=%s)", name == null ? "." : name); | |
} | |
} | |
// Clusters of parts | |
static class Cluster { | |
List<Part> parts = new ArrayList<Part>(); | |
} | |
// Helper object that does not rely on Collections.shuffle functionality | |
static class Shuffler { | |
static Random random; | |
static long seed; | |
static { | |
seed = System.currentTimeMillis(); | |
random = new Random(seed); | |
} | |
static int uniform(int N) { | |
return random.nextInt(N); | |
} | |
static void shuffle(Object[] a) { | |
final int N = a.length; | |
for(int i = 0; i < N; i++){ | |
int r = i + uniform(N - i); // btw i and N - 1 | |
Object temp = a[i]; | |
a[i] = a[r]; | |
a[r] = temp; | |
} | |
} | |
} | |
private static Cluster[] CLUSTERS = new Cluster[5]; | |
static { | |
final Cluster ONE = new Cluster(); | |
ONE.parts.add(new Part(){{name = "MICRO"; }}); // microcontroller | |
ONE.parts.add(new Part(){{name = "BPACK"; }}); // battery pack | |
final Cluster TWO = new Cluster(); | |
TWO.parts.add(new Part(){{name = "SERVO"; }}); // servo | |
TWO.parts.add(new Part(){{name = "WHEEL"; }}); // wheel controlled by servo | |
final Cluster THREE = new Cluster(); | |
// this cluster represent the two holes on the chassis to put wheels | |
THREE.parts.add(new Part(){{name = "WHEEL"; }}); | |
THREE.parts.add(new Part(){{name = "WHEEL"; }}); | |
final Cluster FOUR = new Cluster(); | |
// cluster comprised of two sensors (either light or motion) | |
FOUR.parts.add(new Part(){{name = "SENSOR"; }}); | |
FOUR.parts.add(new Part(){{name = "SENSOR"; }}); | |
final Cluster FIVE = new Cluster(); | |
// cluster comprised of one sensor (either light or motion) | |
FIVE.parts.add(new Part(){{name = "SENSOR"; }}); | |
// set up available clusters. | |
CLUSTERS[0] = ONE; | |
CLUSTERS[1] = TWO; | |
CLUSTERS[2] = THREE; | |
CLUSTERS[3] = FOUR; | |
CLUSTERS[4] = FIVE; | |
} | |
private final int n; | |
private final int d; | |
private final List<Solution> solutions = new ArrayList<Solution>(); | |
public ItemsPlacement(int n, int d) { | |
this.n = n; | |
this.d = d; | |
} | |
void admit(int[] q) { | |
final List<Area> area = evictSolution(q); | |
final Area[] src = area.toArray(new Area[area.size()]); | |
final Area[] dst = new Area[area.size()]; | |
System.arraycopy(src, 0, dst, 0, src.length); | |
final Solution sol = new Solution(); | |
sol.current = dst; | |
solutions.add(sol); | |
} | |
void enumerate() { | |
int[] grid = new int[n]; | |
enumerate(grid, 0); | |
prepClusters(); | |
} | |
void enumerate(int[] q, int r) { | |
int N = q.length; | |
if (r == N) { admit(q); } else { | |
for (int j = 0; j < N; j++) { | |
q[r] = j; | |
if (isConsistent(q, j, r)) { enumerate(q, r + 1); } | |
} | |
} | |
} | |
static int distance(Solution sol){ | |
// this is a point to point distance | |
// if this does not work, then maybe we can calculate this dist wrt the center of the grid | |
int dist = 0; | |
Area pivot = sol.current[0]; | |
for(int i = 1; i < sol.current.length; i++){ | |
final Area each = sol.current[i]; | |
final double pow = Math.pow(pivot.x - each.x, 2) + Math.pow(pivot.y - each.y, 2); | |
dist += Math.floor(Math.sqrt(pow)); | |
pivot = each; | |
} | |
return dist; | |
} | |
static boolean less(Solution a, Solution b){ | |
return distance(a) < distance(b); | |
} | |
static Solution findMin(List<Solution> solutions){ | |
Solution min = null; | |
// Get an optimal one | |
for(Solution eachSolution : solutions){ | |
if(min == null) { min = eachSolution; } else { | |
if(less(eachSolution, min)){ | |
min = eachSolution; | |
} | |
} | |
} | |
return min; | |
} | |
void prepClusters(){ | |
for(Solution each : solutions){ | |
// feed the areas with randomly selected cluster | |
Shuffler.shuffle(CLUSTERS); | |
for(int i = 0; i < each.current.length; i++){ | |
each.current[i].cluster = CLUSTERS[i]; | |
} | |
} | |
} | |
Solution solve(){ | |
// Enumerate feasible placements | |
enumerate(); | |
// Get an optimal placement | |
return findMin(solutions); | |
} | |
/** | |
* @param q current cluster placement | |
* @param j column index | |
* @param r row index | |
* @return true if cluster placement q[n] does not conflict with other cluster placement q[0] through q[n-1] | |
*/ | |
private static boolean isConsistent(int[] q, int j, int r) { | |
for (int i = 0; i < r; i++) { | |
if (q[i] == j) { | |
return false; // same column in the grid | |
} | |
if (q[i] == j + r - i) { | |
return false; // same major diagonal in the grid | |
} | |
if (q[i] == j - r + i) { | |
return false; // same minor diagonal in the grid | |
} | |
} | |
return true; | |
} | |
/** | |
* Print out N-by-N placement of areas from permutation q in ASCII. | |
* | |
* @param q cluster placement | |
*/ | |
private List<Area> evictSolution(int[] q) { | |
final List<Area> areas = new ArrayList<Area>(); | |
int N = q.length; | |
int X = 0; | |
int Y = 0; | |
for (int i = 0; i < N; i++) { | |
for (int j = 0; j < N; j++) { | |
if (q[i] == j) { | |
final Area area = new Area(); | |
area.x = X; | |
area.y = Y; | |
area.w = d; | |
area.h = d; | |
areas.add(area); | |
String val = String.format("[%d,%d] ", area.x, area.y); | |
System.out.print(val); | |
} else { | |
System.out.print("[X] "); | |
} | |
X += d; | |
if (j + 1 == N) { | |
X = 0; | |
Y += d; | |
} | |
} | |
if (i + 1 == N) { | |
Y = 0; | |
} | |
System.out.println(); | |
} | |
System.out.println(); | |
return areas; | |
} | |
public static void main(String[] args) { | |
/** | |
* possible clusters are | |
* A: batterypack, microprocessor | |
* B: servo, batterpack, sensors | |
* C: etc. | |
* | |
* Clusters are formed based on user input. Users know---based on their experience assembling | |
* these things---what to include in these clusters of parts. | |
*/ | |
int N = 5; // N-by-N grid | |
int D = 2; // Distance from point to point. Since this is a grid, we assume that width == height | |
final ItemsPlacement solver = new ItemsPlacement(N, D); | |
final Solution optimal = solver.solve(); | |
System.out.println("Solution=>" + Arrays.toString(optimal.current)); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment