|
Alan Kaminsky
|
|
•
|
|
Department of Computer Science
|
|
•
|
|
Rochester Institute of Technology
|
|
•
|
|
4486 +
2220 =
6706
|
|
Home Page
|
Simulation Simplified
7. Random Subsets and Permutations
Class edu.rit.util.RandomSubset
//******************************************************************************
//
// File: RandomSubset.java
// Package: edu.rit.util
// Unit: Class edu.rit.util.RandomSubset
//
// This Java source file is copyright (C) 2011 by Alan Kaminsky. All rights
// reserved. For further information, contact the author, Alan Kaminsky, at
// ark@cs.rit.edu.
//
// This Java source file is part of the Parallel Java Library ("PJ"). PJ is free
// software; you can redistribute it and/or modify it under the terms of the GNU
// General Public License as published by the Free Software Foundation; either
// version 3 of the License, or (at your option) any later version.
//
// PJ is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU General Public License for more details.
//
// A copy of the GNU General Public License is provided in the file gpl.txt. You
// may also obtain a copy of the GNU General Public License on the World Wide
// Web at http://www.gnu.org/licenses/gpl.html.
//
//******************************************************************************
package edu.rit.util;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
/**
* Class RandomSubset provides an object that generates a random subset of a set
* of integers. The original set consists of the integers from 0 to
* <I>N</I>−1 inclusive. The subset consists of integers chosen at random
* without replacement from the original set; each member of the original set is
* equally likely to be chosen. Class RandomSubset is an Iterator that visits
* the elements of the original set in a random order; each <TT>next()</TT>
* method call returns another element of the subset.
* <P>
* Calling the <TT>remove(i)</TT> method one or more times removes the given
* integers from the original set. Those integers will not be chosen for the
* random subset.
* <P>
* Class RandomSubset is layered on top of a pseudorandom number generator
* (PRNG), an instance of class {@linkplain edu.rit.util.Random Random}. Each
* time the <TT>next()</TT> method is called, one random number is consumed from
* the underlying PRNG.
* <P>
* Class RandomSubset has two different implementations with different storage
* requirements. The implementation is specified with a constructor argument.
* <UL>
* <P><LI>
* The <I>sparse</I> implementation requires less storage when the size of the
* random subset (the number of <TT>next()</TT> method calls) is a small
* fraction of the size of the original set (<I>N</I>).
* <P><LI>
* The <I>dense</I> implementation requires less storage when the size of the
* random subset is a large fraction of the size of the original set.
* </UL>
*
* @author Alan Kaminsky
* @version 22-Nov-2011
*/
public class RandomSubset
implements Iterator<Integer>
{
// Hidden data members.
// The underlying PRNG.
private Random prng;
// The size of the original set.
private int N;
// The number of random subset elements returned so far.
private int M;
// Helper class.
private Helper helper;
// Hidden helper classes.
// Helper abstract base class.
private abstract class Helper
{
// Returns the element in the permutation array at index i.
public abstract int getElement
(int i);
// Sets the element in the permutation array at index i to the given
// value.
public abstract void setElement
(int i,
int value);
// Swaps the elements in the permutation array at indexes i and j.
public abstract void swapElements
(int i,
int j);
// Returns the index in the permutation array at which the given value
// resides.
public abstract int indexOf
(int value);
// Restarts the iteration.
public abstract void restart();
}
// Sparse implementation helper class.
private class SparseHelper
extends Helper
{
// A sparse array containing a permutation of the integers from 0 to
// N-1. Implemented as a mapping from array index to array element. If
// an array index is not in the map, the corresponding array element is
// the same as the array index.
private HashMap<Integer,Integer> permutation =
new HashMap<Integer,Integer>();
// Returns the element in the permutation array at index i.
public int getElement
(int i)
{
Integer element = permutation.get (i);
return element == null ? i : element;
}
// Sets the element in the permutation array at index i to the given
// value.
public void setElement
(int i,
int value)
{
if (value == i)
{
permutation.remove (i);
}
else
{
permutation.put (i, value);
}
}
// Swaps the elements in the permutation array at indexes i and j.
public void swapElements
(int i,
int j)
{
int tmp = getElement (i);
setElement (i, getElement (j));
setElement (j, tmp);
}
// Returns the index in the permutation array at which the given value
// resides.
public int indexOf
(int value)
{
for (Map.Entry<Integer,Integer> entry : permutation.entrySet())
{
if (entry.getValue() == value) return entry.getKey();
}
return value;
}
// Restarts the iteration.
public void restart()
{
permutation.clear();
}
}
// Dense implementation helper class.
private class DenseHelper
extends Helper
{
// A dense array containing a permutation of the integers from 0 to
// N-1.
private int[] permutation = new int [N];
// Construct a new dense helper.
public DenseHelper()
{
restart();
}
// Returns the element in the permutation array at index i.
public int getElement
(int i)
{
return permutation[i];
}
// Sets the element in the permutation array at index i to the given
// value.
public void setElement
(int i,
int value)
{
permutation[i] = value;
}
// Swaps the elements in the permutation array at indexes i and j.
public void swapElements
(int i,
int j)
{
int tmp = permutation[i];
permutation[i] = permutation[j];
permutation[j] = tmp;
}
// Returns the index in the permutation array at which the given value
// resides.
public int indexOf
(int value)
{
int i = 0;
while (permutation[i] != value) ++ i;
return i;
}
// Restarts the iteration.
public void restart()
{
for (int i = 0; i < N; ++ i) permutation[i] = i;
}
}
// Exported constructors.
/**
* Construct a new random subset object for the original set consisting of
* the integers from 0 through <I>N</I>−1 inclusive. The sparse
* implementation is used.
*
* @param prng Underlying PRNG.
* @param N Size of original set.
*
* @exception NullPointerException
* (unchecked exception) Thrown if <TT>prng</TT> is null.
* @exception IllegalArgumentException
* (unchecked exception) Thrown if <I>N</I> < 0.
*/
public RandomSubset
(Random prng,
int N)
{
this (prng, N, false);
}
/**
* Construct a new random subset object for the original set consisting of
* the integers from 0 through <I>N</I>−1 inclusive. If <TT>dense</TT>
* is false, the sparse implementation is used. If <TT>dense</TT> is true,
* the dense implementation is used.
*
* @param prng Underlying PRNG.
* @param N Size of original set.
* @param dense False to use the sparse implementation, true to use the
* dense implementation.
*
* @exception NullPointerException
* (unchecked exception) Thrown if <TT>prng</TT> is null.
* @exception IllegalArgumentException
* (unchecked exception) Thrown if <I>N</I> < 0.
*/
public RandomSubset
(Random prng,
int N,
boolean dense)
{
if (prng == null)
{
throw new NullPointerException
("RandomSubset(): prng is null");
}
if (N < 0)
{
throw new IllegalArgumentException
("RandomSubset(): N = "+N+" illegal");
}
this.prng = prng;
this.N = N;
this.helper = dense ? new DenseHelper() : new SparseHelper();
}
// Exported operations.
/**
* Determine whether there are more integers in the random subset.
*
* @return True if there are more integers in the random subset, false if
* all the integers in the original set have been used up.
*/
public boolean hasNext()
{
return M < N;
}
/**
* Returns the next integer in the random subset.
*
* @return Next integer in the random subset.
*
* @exception NoSuchElementException
* (unchecked exception) Thrown if all the integers in the original set
* have been used up.
*/
public Integer next()
{
if (M >= N)
{
throw new NoSuchElementException
("RandomSubset.next(): No further elements");
}
helper.swapElements (M, M + prng.nextInt (N - M));
++ M;
return helper.getElement (M - 1);
}
/**
* Unsupported operation.
*
* @exception UnsupportedOperationException
* (unchecked exception) Thrown always.
*/
public void remove()
{
throw new UnsupportedOperationException();
}
/**
* Remove the given integer from the original set.
* <P>
* If <TT>i</TT> has already been removed from the original set, either by a
* <TT>remove(i)</TT> method call, or by a <TT>next()</TT> method call that
* returned <TT>i</TT>, then this method does nothing.
*
* @param i Integer to remove.
*
* @return This random subset object.
*
* @exception IllegalArgumentException
* (unchecked exception) Thrown if <TT>i</TT> is not in the range 0
* through <I>N</I>−1 inclusive.
*/
public RandomSubset remove
(int i)
{
if (0 > i || i >= N)
{
throw new IllegalArgumentException
("RandomSubset.remove(): i = "+i+" illegal");
}
int j = helper.indexOf (i);
if (j >= M)
{
helper.swapElements (M, j);
++ M;
}
return this;
}
/**
* Restart this random subset's iteration. The original set is reset to
* contain all the integers from 0 through <I>N</I>−1 inclusive. If
* integers had been removed from the original set by calling the
* <TT>remove(i)</TT> method, those integers must be removed again by
* calling the <TT>remove(i)</TT> method again. The iteration is restarted,
* and calling the <TT>next()</TT> method will yield a different random
* subset.
* <P>
* The <TT>restart()</TT> method lets you generate multiple random subsets
* from the same original set using the same RandomSubset object. This
* avoids the multiple storage allocations that would take place when
* creating multiple RandomSubset objects. This in turn can reduce the
* running time, especially when <I>N</I> is large.
*/
public void restart()
{
M = 0;
helper.restart();
}
// Unit test main program.
// /**
// * Unit test main program.
// * <P>
// * Usage: java edu.rit.util.RandomSubset <I>impl</I> <I>seed</I> <I>N</I>
// * <I>M</I> [ <I>i</I> ... ]
// * <BR><I>impl</I> = "sparse" or "dense"
// * <BR><I>seed</I> = Random seed
// * <BR><I>N</I> = Size of original set
// * <BR><I>M</I> = Size of random subset
// * <BR><I>i</I> = Integer(s) to remove from original set
// */
// public static void main
// (String[] args)
// {
// if (args.length < 3) usage();
// boolean dense = args[0].equals ("dense");
// long seed = Long.parseLong (args[1]);
// int N = Integer.parseInt (args[2]);
// int M = Integer.parseInt (args[3]);
// RandomSubset rs =
// new RandomSubset (Random.getInstance (seed), N, dense);
// for (int r = 0; r < 4; ++ r)
// {
// rs.restart();
// for (int j = 3; j < args.length; ++ j)
// {
// rs.remove (Integer.parseInt (args[j]));
// }
// for (int j = 0; j < M; ++ j)
// {
// System.out.printf ("%d ", rs.next());
// }
// System.out.println();
// }
// }
//
// private static void usage()
// {
// System.err.println ("Usage: java edu.rit.util.RandomSubset <impl> <seed> <N> <M> [<i> ...]");
// System.err.println ("<impl> = \"sparse\" or \"dense\"");
// System.err.println ("<seed> = Random seed");
// System.err.println ("<N> = Size of original set");
// System.err.println ("<M> = Size of random subset");
// System.err.println ("<i> = Integer(s) to remove from original set");
// System.exit (1);
// }
}
|
Alan Kaminsky
|
|
•
|
|
Department of Computer Science
|
|
•
|
|
Rochester Institute of Technology
|
|
•
|
|
4486 +
2220 =
6706
|
|
Home Page
|
Copyright © 2011 Alan Kaminsky.
All rights reserved.
Last updated 31-Aug-2011.
Please send comments to ark@cs.rit.edu.