views:

58

answers:

3

Hi,

I'm trying to do a convex hull approach and the little problem is that I need to get all sets of three consecutive vertices, like this:

private void isConvexHull(Ponto[] points) {
        Arrays.sort(points);
        for (int i = 0; i <points.length; i++) {
            isClockWise(points[i],points[i+1],points[i+2]);
        }
     //... 
    }

I always do something that I don't consider clean code. Could please help me find one or more ways to this? I want it to be circular, i.e., if my fisrt point of the a set is the last element in the array, the 2nd element will be the 3rd in the list and the 3rd in that set will be the the 2nd element in the list, and so on. They must be consecutive, that's all.

+5  A: 

The remainder "trick"

You can use the % "trick" (% is the remainder operator JLS 15.17.3) for circular indexing. Here I will illustrate the general idea using a String instead.

    String s = "ABCDE";
    final int L = s.length();
    for (int i = 0; i < L; i++) {
        System.out.format("%c%c%c ",
            s.charAt(i),
            s.charAt((i + 1) % L),
            s.charAt((i + 2) % L)
        );
    } // prints "ABC BCD CDE DEA EAB "

An Iterator approach

If you're doing this triplet processing often, though, it will be a better overall design to have an Iterator<PointTriplet> or something similar.

Here's a prototype to illustrate the idea:

import java.util.*;

public class CircSubArray {
    static <T> Iterator<T[]> circularIterator(final T[] arr, final int K) {
        return new Iterator<T[]>() {
            int index = 0;
            final int L = arr.length;
            T[] sub = Arrays.copyOf(arr, K); // let it do the dirty work!

            @Override public boolean hasNext() {
                return index < L;
            }
            @Override public T[] next() {
                for (int i = 0; i < K; i++) {
                    sub[i] = arr[(index + i) % L];
                }
                index++;
                return sub; // we always overwrite; no need to .clone()
            }
            @Override public void remove() {
                throw new UnsupportedOperationException();
            }
        };
    }   
    public static void main(String[] args) {
        String[] arr = { "s1", "s2", "s3", "s4", "s5", "s6" };

        Iterator<String[]> iter = circularIterator(arr, 4);
        while (iter.hasNext()) {
            System.out.println(Arrays.toString(iter.next()));
        }
    }
}

This prints:

[s1, s2, s3, s4]
[s2, s3, s4, s5]
[s3, s4, s5, s6]
[s4, s5, s6, s1]
[s5, s6, s1, s2]
[s6, s1, s2, s3]

A List-based solution

As Effective Java 2nd Edition says, Item 25: Prefer lists to arrays. Here's a solution that redundantly stores the first K-1 elements at the end of the List, and then simply uses subList to get K elements at a time.

    String[] arr = { "s1", "s2", "s3", "s4", "s5", "s6" };

    final int L = arr.length;
    final int K = 3;
    List<String> list = new ArrayList<String>(Arrays.asList(arr));
    list.addAll(list.subList(0, K-1));

    for (int i = 0; i < L; i++) {
        System.out.println(list.subList(i, i + K));
    }

This prints:

[s1, s2, s3]
[s2, s3, s4]
[s3, s4, s5]
[s4, s5, s6]
[s5, s6, s1]
[s6, s1, s2]

Since subList is a view, this does not have to do the O(K) shifting that the array-based solution does. This also shows the expressiveness of List over arrays.

polygenelubricants
An Iterator<PointTriplet>...how is that? Could you please demonstrate?
neverMind
The % operator won't be anywhere near as fast as special-casing the last two elements (the ones where the sequence wraps). If you special-case, though, be sure to handle the case of Length == 1 separately.
Ben Voigt
@newba: `Iterator<PointTriplet>` is much the same as the approach you're talking about, except using `yield return` instead of calling `isClockwise`. This decouples triplet generation from triplet processing. Another way to do that (push vs pull iteration) is to pass in an `Action<Point, Point, Point>` delegate to be called for each triplet.
Ben Voigt
@Ben: yes, the `%` isn't as efficient, but I'd say this qualifies as small inefficiency that can be ignored (premature optimization etc). Using `%` is cleaner (special-cases = added complexity!), and easier to understand if you know the remainder semantics.
polygenelubricants
Interesting solutions, but honestly... isn't that a little over-engineered?
FredOverflow
@FredOverflow: hey, I upvoted your answer! But I still stand by my own. I think the `subList` is the most powerful, most readable, and most efficient, since it's a view that captures the sublist in `O(1)`, where as your lovely(!!) technique is `O(K)` to shift every iteration.
polygenelubricants
@FredOverflow: also, if you combine the `subList` with the `Iterator<List<T>>` solution, and go a step further to make `Iterable` out of that, you can for-each it wherever and whenever and not have to repeat the shifting code from your solution. If you're doing this just one time, and `K` is fixed at 3, your solution is quite lovely(!!!).
polygenelubricants
@poly I like your solution for its generality, but I think the initialization is too expensive. `new ArrayList<String>(Arrays.asList(arr))` is O(n), and because the list is already full, the subsequent `list.addAll(list.subList(0, K-1))` is O(n) again. My initialization of `p1` and `p2`, on the other hand, is constant.And my "O(k) shift" does not really count, because I only do one list access per iteration, which adds up to 3*n operations total, whereas with your solution, the client will do three, which also adds up to 3*n operations total.
FredOverflow
@FredOverflow: and I like your solution for its specificity. Which is why I upvoted your answer. What do you want me to do? Downvote my own answer? Delete it? Like I said, I still stand by my answer, generality being what I was aiming for (which is why I used `K` in the first place).
polygenelubricants
@poly I don't want you to do anything, just thought I'd let you know what I think :) Peace out.
FredOverflow
A: 

Can you elaborate the problem with some sample data ?

"I want it to be circular, i.e., if my fisrt point of the a set is the last element in the array, the 2nd element will be the 3rd in the list and the 3rd in that set will be the the 2nd element in the list, and so on. They must be consecutive, that's all."Can you please state your expectation using sample data ?
+2  A: 

Simply remember the previous two points, starting at the end, and shift them with each iteration.

private static boolean isConvexHull(Ponto[] points)
{
    final int len = points.length;
    if (len < 3) return false;
    Arrays.sort(points);

    Ponto p1 = points[len - 2];
    Ponto p2 = points[len - 1];
    for (Ponto p3 : points)
    {
        if (!isClockWise(p1, p2, p3)) return false;
        p1 = p2;
        p2 = p3;
    }
    return true;
}

By the way, how exactly do you sort the array? What is the sorting criterion?

FredOverflow
+1 that's pretty nice =)
polygenelubricants
@FredOverflow. I actually don't sort it right now, because it wasn't necessary. But I have a classe Ponto that implements Comparable and I make it sort first by x and then by y, "tweaking" the compareTo as they helped me here: http://stackoverflow.com/questions/2741846/how-to-sort-an-array-or-arraylistpoint-asc-first-by-x-and-then-by-y
neverMind