package de.invesdwin.util.math.decimal.internal.randomize; import java.util.Iterator; import java.util.List; import javax.annotation.concurrent.ThreadSafe; import org.apache.commons.math3.random.RandomGenerator; import de.invesdwin.util.assertions.Assertions; import de.invesdwin.util.math.decimal.ADecimal; import de.invesdwin.util.math.decimal.IDecimalAggregate; import de.invesdwin.util.math.decimal.internal.randomize.blocklength.CircularOptimalBlockLength; /** * http://www.math.ucsd.edu/~politis/SOFT/PPW/ppw.R * * http://www.numericalmethod.com/javadoc/suanshu/com/numericalmethod/suanshu/stats/random/sampler/resampler/bootstrap/block/PattonPolitisWhite2009.html * * https://github.com/colintbowers/DependentBootstrap.jl */ @ThreadSafe public class CircularBootstrapRandomizer<E extends ADecimal<E>> implements IDecimalRandomizer<E> { private final int blockLength; private final List<E> sample; private final IDecimalRandomizer<E> delegate; public CircularBootstrapRandomizer(final IDecimalAggregate<E> parent) { this.sample = parent.values(); this.blockLength = newOptimalBlockLength(parent); Assertions.assertThat(blockLength).isGreaterThanOrEqualTo(1); if (blockLength == 1) { //blockwise resample makes no sense with block maxResampleIdx 1 delegate = new BootstrapRandomizer<E>(parent); } else { delegate = new IDecimalRandomizer<E>() { @Override public Iterator<E> randomize(final RandomGenerator random) { return internalResample(random); } }; } } protected int newOptimalBlockLength(final IDecimalAggregate<E> parent) { return new CircularOptimalBlockLength<E>(parent).getBlockLength(); } @Override public final Iterator<E> randomize(final RandomGenerator random) { return delegate.randomize(random); } protected int nextBlockLength(final RandomGenerator random) { return blockLength; } private Iterator<E> internalResample(final RandomGenerator random) { return new Iterator<E>() { private final int maxResampleIdx = sample.size(); private int curResampleIdx = 0; private int curBlockIdx = 0; private int maxBlockIdx = 0; private int curStartIdx = 0; @Override public boolean hasNext() { return curResampleIdx < maxResampleIdx; } @Override public E next() { if (curBlockIdx == maxBlockIdx) { initNextBlock(random); } final int valuesIndex = (curStartIdx + curBlockIdx) % maxResampleIdx; final E value = sample.get(valuesIndex); curBlockIdx++; curResampleIdx++; return value; } private void initNextBlock(final RandomGenerator random) { curStartIdx = random.nextInt(maxResampleIdx); final int curBlockLength = nextBlockLength(random); if (curResampleIdx + curBlockLength < maxResampleIdx) { maxBlockIdx = curBlockLength; } else { maxBlockIdx = maxResampleIdx - curResampleIdx; } curBlockIdx = 0; } }; } }