//////////////////////////////////////// // Programming project code // UniBw M, 2022, 2023, 2024 // www.unibw.de/inf2 // (c) Mark Minas (mark.minas@unibw.de) //////////////////////////////////////// package pp.util; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.NoSuchElementException; import java.util.Random; /** * An iterator that creates a random permutation of positions (x, y) where x and y are integer values * in the range {0, ..., width-1} x {0, ..., height-1}. All permutations are uniformly distributed * using the Fisher–Yates shuffle (also known as Knuth shuffle). * * @param the type of elements returned by this iterator */ public class RandomPositionIterator implements Iterator { private final Random random = new Random(); private final int height; private final Map movedMap = new HashMap<>(); private int remaining; private final Creator creator; /** * Functional interface to create instances of type T. * * @param the type of elements created by this creator */ public interface Creator { T create(int x, int y); } /** * Creates a RandomPositionIterator for Position instances with float coordinates. * * @param width the width of the rectangle * @param height the height of the rectangle * @return a RandomPositionIterator for Position instances */ public static RandomPositionIterator floatPoints(int width, int height) { return new RandomPositionIterator<>(FloatPoint::new, width, height); } /** * Creates a new permutation iterator generating a random permutation of positions (x, y) * where x and y are integer values in the range {0, ..., width-1} x {0, ..., height-1}. * * @param creator the creator to create instances of type T * @param width the width of the rectangle * @param height the height of the rectangle */ public RandomPositionIterator(Creator creator, int width, int height) { this.height = height; this.remaining = width * height; this.creator = creator; } @Override public boolean hasNext() { return remaining > 0; } @Override public T next() { if (hasNext()) { final int idx = random.nextInt(remaining--); // note that remaining is decremented final T result = getWhere(idx); if (idx < remaining) movedMap.put(idx, getWhere(remaining)); movedMap.remove(remaining); return result; } throw new NoSuchElementException(); } private T getWhere(int idx) { final T movedWhere = movedMap.get(idx); if (movedWhere != null) return movedWhere; final int x = idx / height; final int y = idx % height; return creator.create(x, y); } }