Efficient implementation of multi-dimensional arrays in Java? - java

As far as I understand (from answers such as this), java has no native multi-dimensional continuous memory arrays (unlike C#, for example).
While the jagged array syntax (arrays of arrays) might be good for most applications, I would still like to know what's the best practice if you do want the raw efficiency of a continuous-memory array (avoiding unneeded memory reads)
I could of course use a single-dimensional array that maps to a 2D one, but I prefer something more structured.

it's not difficult to do it manually:
int[] matrix = new int[ROWS * COLS];
int x_i_j = matrix[ i*COLS + j ];
now, is it really faster than java's multi dimension array?
int x_i_j = matrix[i][j];
for random access, maybe. for continuous access, probably not - matrix[i] is almost certainly in L1 cache, if not in register cache. in best scenario, matrix[i][j] requires one addition and one memory read; while matrix[i*COLS + j] may cost 2 additions, one multiply, one memory read. but who's counting?

It depends on your access pattern. Using this simple program, comparing an int[][] with a 2D mapped over a 1D int[] array treated as a matrix, a native Java 2D matrix is:
25% faster when the row is on the cache, ie: accessing by rows:
100% slower when the row is not in the cache, ie: accessing by colums:
ie:
// Case #1
for (y = 0; y < h; y++)
for (x = 0; x < w; x++)
// Access item[y][x]
// Case #2
for (x = 0; x < w; x++)
for (y = 0; y < h; y++)
// Access item[y][x]
The 1D matrix is calculated as:
public int get(int x, int y) {
return this.m[y * width + x];
}

Let's say you have a 2D array int[][] a = new int[height][width], so by convention you have the indices a[y][x]. Depending on how you represent the data and how you access them, the performance varies in a factor of 20 :
The code:
public class ObjectArrayPerformance {
public int width;
public int height;
public int m[];
public ObjectArrayPerformance(int w, int h) {
this.width = w;
this.height = h;
this.m = new int[w * h];
}
public int get(int x, int y) {
return this.m[y * width + x];
}
public void set(int x, int y, int value) {
this.m[y * width + x] = value;
}
public static void main (String[] args) {
int w = 1000, h = 2000, passes = 400;
int matrix[][] = new int[h][];
for (int i = 0; i < h; ++i) {
matrix[i] = new int[w];
}
long start;
long duration;
System.out.println("duration[ms]\tmethod");
start = System.currentTimeMillis();
for (int z = 0; z < passes; z++) {
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
matrix[y][x] = matrix[y][x] + 1;
}
}
}
duration = System.currentTimeMillis() - start;
System.out.println(duration+"\t2D array, loop on x then y");
start = System.currentTimeMillis();
for (int z = 0; z < passes; z++) {
for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) {
matrix[y][x] = matrix[y][x] + 1;
}
}
}
duration = System.currentTimeMillis() - start;
System.out.println(duration+"\t2D array, loop on y then x");
//
ObjectArrayPerformance mt = new ObjectArrayPerformance(w, h);
start = System.currentTimeMillis();
for (int z = 0; z < passes; z++) {
for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) {
mt.set(x, y, mt.get(x, y) + 1);
}
}
}
duration = System.currentTimeMillis() - start;
System.out.println(duration+"\tmapped 1D array, access trough getter/setter");
//
ObjectArrayPerformance mt2 = new ObjectArrayPerformance(w, h);
start = System.currentTimeMillis();
for (int z = 0; z < passes; z++) {
for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) {
mt2.m[y * w + x] = mt2.m[y * w + x] + 1;
}
}
}
duration = System.currentTimeMillis() - start;
System.out.println(duration+"\tmapped 1D array, access through computed indexes, loop y then x");
ObjectArrayPerformance mt3 = new ObjectArrayPerformance(w, h);
start = System.currentTimeMillis();
for (int z = 0; z < passes; z++) {
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
mt3.m[y * w + x] = mt3.m[y * w + x] + 1;
}
}
}
duration = System.currentTimeMillis() - start;
System.out.println(duration+"\tmapped 1D array, access through computed indexes, loop x then y");
ObjectArrayPerformance mt4 = new ObjectArrayPerformance(w, h);
start = System.currentTimeMillis();
for (int z = 0; z < passes; z++) {
for (int y = 0; y < h; y++) {
int yIndex = y * w;
for (int x = 0; x < w; x++) {
mt4.m[yIndex + x] = mt4.m[yIndex + x] + 1;
}
}
}
duration = System.currentTimeMillis() - start;
System.out.println(duration+"\tmapped 1D array, access through computed indexes, loop x then y, yIndex optimized");
}
}
We can conclude that linear access performance depends more on the way you process the array (lines then columns or the reverse?: performance gain = x10, much due to CPU caches) than the structure of the array itself (1D vs 2D : performance gain = x2).
If random access, the performance differences should be much lower, because the CPU caches have less effect.

If you really want more structure with a continuous-memory array, wrap it in an object.
public class My2dArray<T> {
int sizeX;
private T[] items;
public My2dArray(int x, int y) {
sizeX = x;
items = new T[x*y];
}
public T elementAt(int x, int y) {
return items[x+y*sizeX];
}
}
Not a perfect solution, and you probably already know it. So consider this confirmation of what you suspected to be true.
Java only provides certain constructs for organizing code, so eventually you'll have to reach for a class or interface. Since this also requires specific operations, you need a class.
The performance impacts include creating a JVM stack frame for each array access, and it would be ideal to avoid such a thing; however, a JVM stack frame is how the JVM implements it's scoping. Code organization requires appropriate scoping, so there's not really a way around that performance hit that I can imagine (without violating the spirit of "everything is an object").

Sample implementation, without a compiler. This is basically what C/C++ do behind the scenes when you access multidimensional arrays. You'll have to further define accessor behaviour when less than the actual dimensions are specified & so on. Overhead will be minimal and could be optimized further, but thats microoptimizing imho. Also, you never actually know what goes on under the hood after JIT kicks in.
class MultiDimentionalArray<T> {
//disclaimer: written within SO editor, might contain errors
private T[] data;
private int[] dimensions; //holds each dimensions' size
public MultiDimensionalArray(int... dims) {
dimensions = Arrays.copyOf(dims, dims.length);
int size = 1;
for(int dim : dims)
size *= dim;
data = new T[size];
}
public T access(int... dims) {
int idx = 1;
for(int i = 0; i < dims.length)
idx += dims[i] * dimensions[i]; //size * offset
return data[idx];
}
}

The most efficient method of implementing multi-dimensional arrays is by utilizing one-dimensional arrays as multi-dimensional arrays. See this answer about mapping a 2D array into a 1D array.
// 2D data structure as 1D array
int[] array = new int[width * height];
// access the array
array[x + y * width] = /*value*/;
I could of course use a single-dimensional array that maps to a 2D one, but I prefer something more structured.
If you want to access array in a more structured manner, create a class for it:
public class ArrayInt {
private final int[] array;
private final int width, height;
public ArrayInt(int width, int height) {
array = new int[width * height];
this.width = width;
this.height = height;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public int get(int x, int y) {
return array[x + y * width];
}
public void set(int x, int y, int value) {
array[x + y * width] = value;
}
}
If you wanted arrays of objects, you could use generics and define class Array<T>, where T is the object stored in the array.
Performance-wise, this will, in most cases, be faster than a multi-dimensional array in Java. The reasons can be found in the answers to this question.

If you cannot live without C constructs, there's always JNI.
Or you could develop your own Java-derived language (and VM and optimizing JIT compiler) that has a syntax for multidimensional continuous-memory arrays.

Related

Paint Fill multidimensional array

My goal is a "paint fill" function that one might see on many image editing programs. That is, given a screen (represented by a two-dimensional array of colors), a point, and a new color, fill in the surrounding area until the color changes from the original color.
I've implemented it for a 2D array, and here is the code :
public static void paint (int [][] screen,int OldColor,int NewColor,int y,int x)
{
if(y>screen.length-1||y<0||x>screen[0].length||x<0||screen[y][x]!=OldColor)
return;
screen[y][x]=NewColor;
paint(screen,OldColor,NewColor,y-1,x);
paint(screen, OldColor, NewColor, y+1, x);
paint(screen, OldColor, NewColor, y, x-1);
paint(screen, OldColor, NewColor, y, x+1);
}
But I want to implement it for multidimensional arrays like 3D that could be solved by adding:
paint(screen, OldColor, NewColor, y, x,z-1);
paint(screen, OldColor, NewColor, y, x,z+1);
But imagine the array is 100 D... How can I solve this problem?
Thanks to #Spektre's suggestion about the structure of the points, I managed to write a simple N-Dimensional floodfill.
Instead of images, I used char matrix to simplify the coding. Changing it to int as color value and some changes in other matrix's data type, will do the 100D for you :)
In this simple program, I try to fill all "A"'s with "B" and it fill all of connected char values similar to ants nest. You can trace the connections between A's using other layers to see the fill path.
In second image (Im1, add intentionally added a B and then added an A above it which is not accessible from fill point) and it worked fine as well.
package test;
import java.awt.Point;
import java.util.LinkedList;
import java.util.Queue;
/**
*
* #author Pasban
*/
public class NDFloodFill {
public int N1 = 8; // width
public int N2 = 6; // height
public int N = 3; // number of layers
public ImageData[] images = new ImageData[N];
public static void main(String[] args) {
NDFloodFill ndf = new NDFloodFill();
//print original data
//ndf.print();
ndf.fill(0, 0, 0, 'A', 'B');
ndf.print();
}
public NDFloodFill() {
String im0 = ""
+ "AA...A..\n"
+ ".....A..\n"
+ "....AA..\n"
+ "........\n"
+ "........\n"
+ "...AA.AA";
String im1 = ""
+ ".A..A...\n"
+ "....B...\n"
+ "..AAA...\n"
+ "........\n"
+ "...AA.A.\n"
+ "..AA..A.";
String im2 = ""
+ ".A......\n"
+ ".AA.....\n"
+ "..A.....\n"
+ "..A.....\n"
+ "..A.AAA.\n"
+ "..A.....";
images[0] = new ImageData(im0, 0);
images[1] = new ImageData(im1, 1);
images[2] = new ImageData(im2, 2);
}
private void print() {
for (int i = 0; i < N; i++) {
System.out.println(images[i].getImage());
}
}
private void fill(int x, int y, int index, char original, char fill) {
Queue<PixFill> broadCast = new LinkedList<>();
broadCast.add(new PixFill(new Point(x, y), index));
for (int i = 0; i < N; i++) {
images[i].reset();
}
while (!broadCast.isEmpty()) {
PixFill pf = broadCast.remove();
Queue<PixFill> newPoints = images[pf.index].fillArea(pf.xy, original, fill);
if (newPoints != null) {
broadCast.addAll(newPoints);
}
}
}
public class PixFill {
Point xy;
int index;
public PixFill(Point xy, int index) {
this.xy = xy;
this.index = index;
}
#Override
public String toString() {
return this.xy.x + " : " + this.xy.y + " / " + this.index;
}
}
public class ImageData {
char[][] pix = new char[N1][N2];
boolean[][] done = new boolean[N1][N2];
int index;
public ImageData(String image, int index) {
int k = 0;
this.index = index;
for (int y = 0; y < N2; y++) { // row
for (int x = 0; x < N1; x++) { // column
pix[x][y] = image.charAt(k++);
}
k++; // ignoring the \n char
}
}
public void reset() {
for (int y = 0; y < N2; y++) {
for (int x = 0; x < N1; x++) {
done[x][y] = false;
}
}
}
public String getImage() {
String ret = "";
for (int y = 0; y < N2; y++) { // row
String line = "";
for (int x = 0; x < N1; x++) { // column
line += pix[x][y];
}
ret += line + "\n";
}
return ret;
}
public Queue<PixFill> fillArea(Point p, char original, char fill) {
if (!(p.x >= 0 && p.y >= 0 && p.x < N1 && p.y < N2) || !(pix[p.x][p.y] == original)) {
return null;
}
// create queue for efficiency
Queue<Point> list = new LinkedList<>();
list.add(p);
// create broadcasting to spread filled points to othwer layers
Queue<PixFill> broadCast = new LinkedList<>();
while (!list.isEmpty()) {
p = list.remove();
if ((p.x >= 0 && p.y >= 0 && p.x < N1 && p.y < N2) && (pix[p.x][p.y] == original) && (!done[p.x][p.y])) {
//fill
pix[p.x][p.y] = fill;
done[p.x][p.y] = true;
//look for neighbors
list.add(new Point(p.x - 1, p.y));
list.add(new Point(p.x + 1, p.y));
list.add(new Point(p.x, p.y - 1));
list.add(new Point(p.x, p.y + 1));
// there will not be a duplicate pixFill as we always add the filled points that are not filled yet,
// so duplicate fill will never happen, so do pixFill :)
// add one for upper layer
if (index < N - 1) {
broadCast.add(new PixFill(p, index + 1));
}
// add one for lower layer
if (index > 0) {
broadCast.add(new PixFill(p, index - 1));
}
//layers out of range <0, N> can be filtered
}
}
return broadCast;
}
}
}
Avoid recursive functions! Use a queue instead to flood fill the image ith.
On which image you want to start filling?
check the image color on ith image and add that point to your list.
Later on check if you can go up or down from the stored point to (i+1)th or (i-1)th image and repeat this process from there.
This is a raw idea, but all you may need is this.
Plus, you need to have an array for each level to check if you have filled that pixel for that image or not. So you will escape from infinite loop :)
Check this for flood fill using queue:
Flood Fill Optimization: Attempting to Using a Queue
Salivan is right with his suggestions but he did not grasp the real problem you are asking about. For arbitrary dimensionality you need to change the point structure from notation like pnt.x,pnt.y,pnt.z to pnt[0],pnt[1],pnt[2] then there are few approaches how to handle this:
fixed limit size padded with zeros
so handle all like 10D (if 10D is maximal dimensionality used) and fill unused axises with zeros. This is slow ugly,painfully demanding on memory and limiting max dimensionality.
use nested for loop (for initializations and more)
look here: rasterize and fill a hypersphere
many multidimensional operations require nested loops this one has arbitrary depth. You can look at it as an increment function of multi digit number where each digit represents axis in your space.
use normal for loop for neighbors generation in N-D
// point variables
int p[N],q[N];
// here you have actual point p and want to find its neighbors
for (int i=0;i<N;i++)
{
for (int j=0;i<N;i++) q[j]=p[j]; // copy point
q[i]--;
// add q to flood fill
q[i]+=2;
// add q to flood fill
}

Is there a data structure to grab values by x or y?

I have an ArrayList with a lot of points (so x,y).
I sorted the points on XY.
Is there a datatype/algorithm to get all y points a certain x fast? (or the other way around, all x on a certain y).
At the moment I have something like this, which works ok but I have the feeling it's to complex for what I need.
int lastXPos;
int lastIndex;
int[] workArray = new int[4096];
int workArrayIndex;
// returns all the y values where x matches
public int[] grab(int xPos) {
workArrayIndex = 0;
int startIndex = 0;
// this can increase speed a lot
if (lastXPos+1 == xPos) {
startIndex = lastIndex+1;
}
PVector v;
for (int i = startIndex; i < vecs.size(); i++) {
v = vecs.get(i);
if (v.x > xPos) {
lastIndex = i-1;
break;
}
if (v.x == xPos) {
workArray[workArrayIndex++] = (int) v.y;
}
}
lastXPos = xPos;
int[] result = new int[workArrayIndex];
for (int i = 0; i < workArrayIndex; i++) {
result[i] = workArray[i];
}
return result;
}
Edit:
One more thing, it has to process a new list 60 times a second, so creating the data object also has to be fast.
Then as a bonus question, we have rows and columns, is there a way to describe both of those? (direction for example).

Sudoku generator algorithm optimization welcome

I created a recursive DFS algorithm to generate/solve sudoku boards in Java, but it's taking forever to terminate, and an explanation/optimization would be welcome. I can't imagine that generating a sudoku board would be so time-consuming, especially with all the apps around (although they might have a database.)
Basically, I traverse all cells, seeing whether any of [1-9] would satisfy the sudoku constraints, and backtrack on dead-end branches. To conserve memory and avoid copying the 2D array that serves as the board with each invocation of the recursive method (and there are potentially 81*9! leaves in that tree, if I'm not mistaken...), I created a 2D matrix of integer stacks, in which an element is pushed every time a branch is explored, and popped if it's a dead-end.
Below is the implementation. Any advice on speedup would be welcome. I'm doing this as a personal excercise, and I'm wondering if something asymptotically better exists.
Hope it's not a terrible read below.. Thank you!
1) The algorithm implementation: (note that values are in a "jumbled" array of [1-9] to create unique boards.)
/**
* Provides one solution to a board with an initial configuration, or <code>null</code> if there is none.
* The search is randomized, s.t. the algorithm can serve to populate an empty board.
*
* #param initial The initial board given to solve.
* #return The fully solved board, or null if no solution found.
*/
public static int[][] solveBoard (int[][] initial){
return solveBoard(new StackedBoard(initial), 0, 0);
}
private static int[][] solveBoard (StackedBoard board, int xPos, int yPos){
// base case - success
int remaining = 81;
for (int x = 0; x < 9; x++){
for (int y = 0; y < 9; y++){
if (board.peekAt(x, y) != Board.EMPTY){
remaining--;
}
}
}
if (remaining == 0){
return board.flatView();// the only creation of an array.
}
// recursive case
for (int x = 0; x < 9; x++){
for (int y = 0; y < 9; y++){
if (board.peekAt(x, y) == Board.EMPTY){
for (int val : getJumbledRandomVals()){
if (isMoveLegal(board, x, y, val)){
board.pushAt(x, y, val);
int[][] leafBoard = solveBoard(board, x, y);
if (leafBoard != null) {
return leafBoard;
}
}
}
}
}
}
// base case - dead branch
board.popAt(xPos, yPos);
return null;
}
2) The StackedBoard implementation:
/**
* Represents square boards with stacked int elements.
*/
class StackedBoard {
ArrayList<ArrayList<Stack<Integer>>> xaxis = new ArrayList<ArrayList<Stack<Integer>>>();
/**
*
* #param init A square array - both dimensions of equal length, or <code>null</code> if no initialization.
*/
public StackedBoard (int[][] init) {
for (int i = 0; i < 9; i++){
ArrayList<Stack<Integer>> yaxis = new ArrayList<Stack<Integer>>();
xaxis.add(yaxis);
for (int j = 0; j < 9; j++){
Stack<Integer> stack = new Stack<Integer>();
yaxis.add(stack);
}
}
if (init != null){
for (int x = 0; x < init.length; x++){
for (int y = 0; y < init.length; y++){
getStackAt(x, y).push(init[x][y]);
}
}
}
}
public Stack<Integer> getStackAt (int x, int y){
return xaxis.get(x).get(y);
}
public int peekAt (int x, int y){
return getStackAt(x, y).peek();
}
public void pushAt (int x, int y, int value){
getStackAt(x, y).push(value);
}
public Integer popAt (int x, int y){
try {
return getStackAt(x, y).pop();
} catch (EmptyStackException e){
// shhhhh!
return Board.EMPTY;
}
}
/**
* Flat view of the stacked-board; peek of the top elements.
*/
public int[][] flatView (){
int[][] view = new int[xaxis.size()][xaxis.size()];
for (int x = 0; x < xaxis.size(); x++){
for (int y = 0; y < xaxis.size(); y++){
view[x][y] = getStackAt(x, y).peek();
}
}
return view;
}
}
3) The constraints function implementation:
/**
* Is the move legal on the suggested board?
*
* #param board The board.
* #param x The X coordinate, starts with 0.
* #param y The Y coordinate, starts with 0.
* #param value The value.
* #return <code>true</code> iff the move is legal.
*/
private static boolean isMoveLegal (StackedBoard board, int x, int y, int value){
// by value
if (1 > value || value > 9){
return false;
}
// by column
for (int i = 0; i < 9; i++){
if (board.peekAt(i, y) == value){
return false;
}
}
// by row
for (int i = 0; i < 9; i++){
if (board.peekAt(x, i) == value){
return false;
}
}
// by lil square
int lowerX = x < 3 ? 0 : (x < 6 ? 3 : 6);
int upperX = lowerX + 2;
int lowerY = y < 3 ? 0 : (y < 6 ? 3 : 6);
int upperY = lowerY + 2;
for (int i = lowerX; i <= upperX; i++){
for (int j = lowerY; j <= upperY; j++){
if (board.peekAt(i, j) == value){
return false;
}
}
}
return true;
}
If you are willing to make a complete left turn, there are much better algorithms for generating / solving Sudokus. Don Knuth's dancing links algorithm is known to be extremely good at rapidly enumerating all Sudoku solutions (once they're phrased as instances of the exact cover problem) and is commonly used as the main algorithm in Sudoku solvers, and it's worth looking into. It requires a lot of pointer/reference gymnastics, but it relatively short to code up.
If you want to stick with your existing approach, one useful optimization would be to always choose the most constrained cell as the next value to fill in. This will likely cause a cascade of "forced moves" that will help you reduce the size of your search space, though it's only a heuristic.
Hope this helps!

Change order of for loops?

I have a situation where i need to loop though xyz coordinates in different orders depending on a users input. So i an area in 3D space then a set of for loops like so.
for(int x = 0; x < build.getWidth(); x++){
for(int y = 0; y < build.getHeight(); y++){
for(int z = 0; z < build.getLength(); z++){
//do stuff
}
}
}
but depending on the users input, the order may be like this.
for(int z = 0; z < build.getLenght(); z++){
for(int y = 0; y < build.getHeight(); y++){
for(int x = 0; x < build.getWidth(); x++){
//do stuff
}
}
}
or even negative.
for(int x = build.getWidth(); x > 0; x--){
for(int y = 0; y < build.getHeight(); y++){
for(int z = 0; z < build.getLength(); z++){
//do stuff
}
}
}
Is there any way to do this without hard coding every case?
Here's an n-dimensional stepper that can step in any number of dimensions in any order from any start locations to any limits. See the test code for an example.
public class Test {
public void test() {
int[] limits = {3, -5, 7};
int[] order = {0, 2, 1};
int[] starts = {0, 0, 0};
int[] steps = {1, -1, 2};
NDimensionalStepper nds = new NDimensionalStepper(limits, order, starts, steps);
do {
System.out.println(nds);
} while (nds.step());
}
public static void main(String args[]) {
new Test().test();
}
public static class NDimensionalStepper {
// The current positions in each dimension.
// Note that i[order[0]] is the fastest mover.
final int[] i;
// Starts.
final int[] starts;
// Steps.
final int[] steps;
// Limits.
final int[] limits;
// Order.
final int[] order;
// The (unordered) dimension we last stepped.
int d = 0;
// Full constructor.
public NDimensionalStepper(int[] limits, int[] order, int[] starts, int[] steps) {
// Should parameter check to ensure all are the same length.
// Should also check that each dimension will terminate.
this.i = Arrays.copyOf(starts, starts.length);
this.starts = Arrays.copyOf(starts, starts.length);
this.steps = Arrays.copyOf(steps, steps.length);
this.limits = Arrays.copyOf(limits, limits.length);
this.order = Arrays.copyOf(order, order.length);
}
// Default steps to 1.
public NDimensionalStepper(int[] limits, int[] order, int[] starts) {
this(limits, order, starts, defaultSteps(limits, starts));
}
// Default steps - 1 Towards limits.
private static int[] defaultSteps(int[] limits, int[] starts) {
int[] steps = new int[limits.length];
for (int i = 0; i < limits.length; i++) {
// Step towrds limits.
steps[i] = (int) Math.signum(limits[i] - starts[i]);
}
return steps;
}
// Default starts to 0.
public NDimensionalStepper(int[] limits, int[] order) {
this(limits, order, defaultStarts(limits.length));
}
// Default starts - 0, 0, ...
private static int[] defaultStarts(int d) {
int[] starts = new int[d];
Arrays.fill(starts, 0);
return starts;
}
// Default order to normal.
public NDimensionalStepper(int[] limits) {
this(limits, defaultOrder(limits.length));
}
// Default order - ..., 1, 0
private static int[] defaultOrder(int d) {
int[] order = new int[d];
for (int i = 0; i < d; i++) {
order[i] = d - i - 1;
}
return order;
}
// Get the current position in dimension d.
public int get(int d) {
return i[d];
}
// Take just one step. Return false if cant.
public boolean step() {
boolean stepped = false;
boolean finished = false;
while (!stepped && !finished) {
// Which dimension should be stepped (depends on order).
int o = order[d];
// Can we step in the current dimension?
while (finished(o) && d < order.length - 1) {
// Reached a limit! - Move up one dimension.
o = order[++d];
}
if (d < order.length && !finished(o)) {
// Step it.
i[o] += steps[o];
stepped = true;
// Zero all lower dimensions.
while (d > 0) {
d -= 1;
i[order[d]] = starts[order[d]];
}
} else {
// Got to the last without finding one below limit. Finished!
finished = true;
}
}
return !finished;
}
// Equal or passed the limits.
private boolean finished(int o) {
int sign = (int) Math.signum(steps[o]);
return sign * (i[o] + steps[o]) >= sign * limits[o];
}
#Override
public String toString() {
StringBuilder s = new StringBuilder();
s.append("{");
for (int d = 0; d < order.length; d++) {
s.append(get(d));
if (d < order.length - 1) {
s.append(",");
}
}
s.append("}");
return s.toString();
}
}
}
My tests of the equivalents of your three scenarios look like:
private void testBuild1(Build build) {
System.out.println("Build: x,y,z");
for (int x = 0; x < build.getWidth(); x++) {
for (int y = 0; y < build.getHeight(); y++) {
for (int z = 0; z < build.getLength(); z++) {
System.out.println("{" + x + "," + y + "," + z + "}");
}
}
}
int[] limits = {build.getWidth(), build.getHeight(), build.getLength()};
testNDS(new NDimensionalStepper(limits));
}
private void testBuild2(Build build) {
System.out.println("Build: z,y,x");
for (int z = 0; z < build.getLength(); z++) {
for (int y = 0; y < build.getHeight(); y++) {
for (int x = 0; x < build.getWidth(); x++) {
System.out.println("{" + x + "," + y + "," + z + "}");
}
}
}
int[] limits = {build.getWidth(), build.getHeight(), build.getLength()};
int[] order = {0,1,2};
testNDS(new NDimensionalStepper(limits, order));
}
private void testBuild3(Build build) {
System.out.println("Build: x--,y,z");
for (int x = build.getWidth(); x > 0; x--) {
for (int y = 0; y < build.getHeight(); y++) {
for (int z = 0; z < build.getLength(); z++) {
System.out.println("{" + x + "," + y + "," + z + "}");
}
}
}
int[] limits = {0, build.getHeight(), build.getLength()};
int[] order = {2,1,0};
int[] starts = {build.getWidth(), 0, 0};
int[] steps = {-1, 1, 1};
testNDS(new NDimensionalStepper(limits, order, starts, steps));
}
private void testNDS(NDimensionalStepper nds) {
System.out.println("--nds--");
do {
System.out.println(nds);
} while (nds.step());
}
You said depending on user input the order of loop changes. The logic for handling user input will have to be written.
You can code like this:
//Code to populate XInit, XEnd, YInit, YEnd, ZInit, ZEnd based on user input
for(int x = XInit; x < XEnd; x=XInit<XEnd?x+1:x-1){
for(int y = YInit; y < YEnd; y=YInit<YEnd?y+1:y-1){
for(int z = ZInit; z < ZEnd; z=ZInit<ZEnd?z+1:z-1){
//do stuff
}
}
}
Note: You may even want to abstract the calculation of XInit, XEnd etc. parameters in a separate method.
Your "stuff" is likely accessing the values of x, y, and z, so the way you're hard-coding is probably the easiest to follow. Your method names could clearly indicate the ordering. For the three examples you gave, it would look similar to:
public void somethingXYZ(Build build, Stuff stuff) {...}
public void somethingZYX(Build build, Stuff stuff) {...}
public void somethingXnYZ(Build build, Stuff stuff) {...}
When you're coding and want to select one of those methods, your IDE will even help you by listing the available options for that class. I think the way you're organizing it would already work well.

Faster way to extract histogram from an image

I'm looking for a faster way to extract histogram data from an image.
I'm currently using this piece of code that needs about 1200ms for a 6mpx JPEG image:
ImageReader imageReader = (ImageReader) iter.next();
imageReader.setInput(is);
BufferedImage image = imageReader.read(0);
int height = image.getHeight();
int width = image.getWidth();
Raster raster = image.getRaster();
int[][] bins = new int[3][256];
for (int i = 0; i < width; i++)
for (int j = 0; j < height; j++) {
bins[0][raster.getSample(i, j, 0)]++;
bins[1][raster.getSample(i, j, 1)]++;
bins[2][raster.getSample(i, j, 2)]++;
}
Do you have any suggestions?
You're doing a lot of getSamples method calls and they're in turn doing calls and calls etc.
I work often with pictures and the typical trick to gain speed is to manipulate directly the underlying int[] (in this case your BufferedImage must be backed by an int[]).
The difference between accessing the int[] and doing, say, a getRGB can be gigantic. When I write gigantic, I mean by as much as two orders of magnitude (try doing a getRGB on OS X 10.4 vs int[x] and you'll see the perf gain).
Also, there's no call three times getSamples. I'd simply retrieve one int corresponding to your ARGB pixel and then bitshift to get the RGB bands (you're doing one histogram per R, G and B component right?).
You can gain access to the pixels array by doing something like this:
final int[] a = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
Also you can do what you want to do with a single loop, looping over all the pixels.
Instead of:
for ( int x = 0; x < width; x++ ) {
for ( int y = 0; y < height; y++ ) {
....
You can do:
for ( int p = 0; p < width*height; p++ ) {
Now if you want to get into weirder optimizations, not as likely to prove effective you could:
use loop unrolling (iterating over 6 million pixels is one of the rare case where it may help)
invert the loop: for ( p = width*height - 1; p >= 0; p--)
You can use getSamples(int x, int y, int w, int h, int b, double[] dArray) method
It's possible that this method have internal optimisations.
Also, you can try to swap width and height.
for (int i = 0; i < width; i++)
for (int j = 0; j < height; j++) {
}
}
And
for (int i = 0; i < height; i++)
for (int j = 0; j < width; j++) {
}
}
Between this two variants performance difference will be huge.
This is influence of the cpu cache

Categories