I'm trying to write an algorithm that will let me iterate over all desired points within an n-dimensional space to find the minimum of a function f(x) where x is a vector of size n.
Obviously, searching a 2-d or 3-d space is fairly straightforward, you can simply do:
for(int i = 0; i < x; i++) {
for(int j = 0; j < y; j++) {
//and so on for however many dimensions you want
Unfortunately, for my problem, the dimensionality of the space is not fixed (I'm writing a generalised minimum finder for many functions in a statistical program) and so I'd have to write loops for each value of n I want to use - which might ultimately be rather large.
I've been trying to get my head around how I could do this using recursion but can't quite see the solution - although I'm sure there is one there.
The solution doesn't have to be recursive, but it must be general and efficient (the inner most line in that nested loop is going to get called an awful lot...).
The way I'm representing the volume to search is a 2d array of double:
double[][] space = new double[2][4];
This would represent a 4d space with the minimum and maximum bound in each dimension in position 0 or 1 of the array, respectively. Eg:
dim 0 1 2 3
min(0):-10 5 10 -0.5
max(1): 10 55 99 0.2
Any ideas?
-0.5
to 0.2
? Also, what data will you need in the inner loop to process it? An array of points - mellamokb 2012-04-05 22:32
Here is the general idea:
interface Callback {
void visit(int[] p); // n-dimensional point
}
// bounds[] - each number the limits iteration on i'th axis from 0 to bounds[i]
// current - current dimension
// callback - point
void visit(int[] bounds, int currentDimension, int[] p, Callback c) {
for (int i = 0; i < bounds[currentDimension]; i++) {
p[currentDimension] = i;
if (currentDimension == p.length - 1) c.visit(p);
else visit(bounds, currentDimension + 1, p, c);
}
}
/// now visiting
visit(new int[] {10, 10, 10}, 0, new int[3], new Callback() {
public void visit(int[] p) {
System.out.println(Arrays.toString(p));
}
});
currentDimension + 1
, not currentDimension++
, then it works perfectly. Otherwise + - mellamokb 2012-04-05 22:42
50 ms
average for me to run from {0,0,0,0}
to {50,50,50,50}
: http://ideone.com/oxJDe. I have made it ever-so-slightly faster (about 40 ms
) by getting rid of the recursion here: http://ideone.com/uJ4j - mellamokb 2012-04-05 23:35
I'd stick with reucrsion, and use Object
as a parameter, with an extra parameter of dim
, and cast it when you reach a depth of 1 to the relevant array [in my example, it is an int[]
]
public static int getMin(Object arr, int dim) {
int min = Integer.MAX_VALUE;
//stop clause, it is 1-dimensional array - finding a min is trivial
if (dim == 1) {
for (int x : ((int[])arr)) {
min = Math.min(min,x);
}
//else: find min among all elements in an array of one less dimenstion.
} else {
for (Object o : ((Object[])arr)) {
min = Math.min(min,getMin(o,dim-1));
}
}
return min;
}
example:
public static void main(String[] args) {
int[][][] arr = { { {5,4},{2}, {35} } , { {2, 1} , {0} } , {{1}}};
System.out.println(getMin(arr, 3));
}
will produce:
0
The advantage of this approach is no need for any processing of the array - you just send it as it is, and send the dimension as a parameter.
The downside - is type [un]safety, since we dynamically cast the Object
to an array.
Another option is to iterate from 0 to x*y*z*... like you do when converting a number between binary and decimal representations. This is a non-recursive solution, so you won't run into performance issues.
ndims = n;
spacesize = product(vector_sizes)
int coords[n];
for (i = 0; i < spacesize; i++) {
k = i;
for (j = 0; j < ndims; j++ ) {
coords[j] = k % vector_sizes[j];
k /= vector_sizes[j];
}
// do something with this element / these coords
}
This is a non-recursive solution, so you won't run into performance issues.
That's not really a relevant statement. The recursive solutions are actually probably more efficient because they don't have to keep repopulating the inner array, and the recursion level is never more deep than the number of dimensions. Plus, your solution is going to have trouble with rounding errors since the values are all doubles - mellamokb 2012-04-05 22:46
@Eugene
's solution. Running from {0,0,0,0}
to {50,50,50,50}
, your solution takes about 650 ms
, while @Eugene
's takes around 50 ms
. And in case you don't believe me, take a look at http://ideone.com/oxJDe and http://ideone.com/nUdrl, which are my working implementations of both solutions with outputs and timings - mellamokb 2012-04-05 23:12
n-dimensional arrays can be flattened into one-dimensional arrays. What you need is to do the math for these things:
This is what I'd do:
int[]
. So, the size of a 5x7x13x4 4-dimensional array represented as the 4-element array `{ 5, 7, 13, 4 }'.3 + 2*5 + 6*5*7 + 0*5*7*13 == 223
. To access that 4-dimensional index, access index 223 in the flat array.Isn't the function just:
Function loopDimension(int dimensionNumber)
If there is no more dimension, stop;
for(loop through this dimension){
loopDimension(dimensionNumber + 1);
}
This runs through a List of List of values (Integers) and picks the minimum of each List:
import java.util.*;
/**
MultiDimMin
@author Stefan Wagner
@date Fr 6. Apr 00:37:22 CEST 2012
*/
public class MultiDimMin
{
public static void main (String args[])
{
List <List <Integer>> values = new ArrayList <List <Integer>> ();
Random r = new Random ();
for (int i = 0; i < 5; ++i)
{
List<Integer> vals = new ArrayList <Integer> ();
for (int j = 0; j < 25; ++j)
{
vals.add (100 - r.nextInt (200));
}
values.add (vals);
}
showAll (values);
List<Integer> res = multiDimMin (values);
show (res);
}
public static int minof (List <Integer> in)
{
int res = in.get (0);
for (int v : in)
if (res > v) res = v;
return res;
}
public static List<Integer> multiDimMin (List <List <Integer>> in)
{
List<Integer> mins = new ArrayList <Integer> ();
for (List<Integer> li : in)
mins.add (minof (li));
return mins;
}
public static void showAll (List< List <Integer>> lili)
{
for (List <Integer> li : lili) {
show (li);
System.out.println ();
}
}
public static void show (List <Integer> li)
{
for (Integer i: li) {
System.out.print (" " + i);
}
System.out.println ();
}
}