blob: 747e78de0a079a5d9f473690836cf679b288b58a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2016 École Polytechnique de Montréal
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License 2.0 which
* accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package org.eclipse.tracecompass.incubator.internal.xaf.core.statemachine.variable.utils;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Random;
import java.util.Set;
import java.util.TreeSet;
import org.eclipse.tracecompass.incubator.internal.xaf.ui.statemachine.StateMachineReport;
class RandomCombinationsIteratorUnique implements Iterator<int[]> {
private final int n, k;
private Integer count = null;
private int[] origin, solution;
private Random rand = new Random();
private Set<Integer> used = new TreeSet<>();
public RandomCombinationsIteratorUnique(int n, int k) {
this.n = n;
this.k = k;
init();
}
public RandomCombinationsIteratorUnique(int n, int k, int max) {
this.n = n;
this.k = k;
this.count = max;
init();
}
private void init() {
StateMachineReport.debug("Using the random combinations iterator unique!"); //$NON-NLS-1$
origin = new int[n];
for (int i = 0; i < n; i++) {
origin[i] = i;
}
solution = new int[k];
}
@Override
public boolean hasNext() {
return (count == null || count > 0);
}
@Override
public int[] next() {
if (count != null) {
if (count <= 0) {
return null;
}
count--;
}
int test = 0;
do {
if (test > 0) {
StateMachineReport.debug("REMATCH " + test + " with " + Arrays.toString(solution)); //$NON-NLS-1$ //$NON-NLS-2$
}
test++;
int tmp, j;
// Knuth shuffle
/*if (k <= n/2) {
for (int i = 0; i < k; i++) {
j = i + rand.nextInt(n - i);
solution[i] = origin[j];
origin[j] = origin[i];
origin[i] = solution[i];
}
} else {
for (int i = k; i < n; i++) {
j = rand.nextInt(k);
tmp = origin[i];
origin[i] = origin[j];
origin[j] = tmp;
}
System.arraycopy(origin, 0, solution, 0, solution.length);
}*/
// Knuth shuffle: order doesn't matter, we only roll the dices for the elements that are OUTSIDE
// https://en.wikipedia.org/wiki/Reservoir_sampling
/*for (int i = k; i < n; i++) {
j = rand.nextInt(i + 1);
tmp = origin[i];
origin[i] = origin[j];
origin[j] = tmp;
}
System.arraycopy(origin, 0, solution, 0, solution.length);*/
// Basic reservoir
/*for (int i = 0; i < n; i++) {
if (i < k) {
solution[i] = origin[i];
} else {
int randomPos = rand.nextInt(i + 1);
tmp = origin[i];
origin[i] = origin[randomPos];
origin[randomPos] = tmp;
if (randomPos < k) {
solution[randomPos] = origin[randomPos];
}
}
}*/
// Reservoir algorithm R
/*System.arraycopy(origin, 0, solution, 0, solution.length);
for (int i = k; i < n; i++) {
j = rand.nextInt(i + 1);
tmp = origin[i];
origin[i] = origin[j];
origin[j] = tmp;
if (j < k) {
solution[j] = origin[j];
}
}*/
// Fast geometric approximation of reservoir sampling
System.arraycopy(origin, 0, solution, 0, k);
int idx;
int max = Math.min(n, (k * 4) + 1);
for (idx = k; idx < max; idx++) {
j = rand.nextInt(idx + 1);
tmp = origin[idx];
origin[idx] = origin[j];
origin[j] = tmp;
if (j < k) {
solution[j] = origin[j];
}
}
while (idx > 0 && idx < n) {
double p = (double) k / idx;
double u = rand.nextDouble();
int g = (int) Math.floor(Math.log(u) / Math.log(1 - p));
idx += g;
if (idx > 0 && idx < n) {
j = rand.nextInt(k); // integer from 0 inclusive to k exclusive
solution[j] = origin[idx];
origin[idx] = origin[j];
origin[j] = solution[j];
idx++;
}
}
// Reservoir algorithm L
/*System.arraycopy(origin, 0, solution, 0, solution.length);
int R = k;
double W = Math.exp(Math.log(rand.nextDouble()) / k);
while (true) {
int S = (int) Math.floor(Math.log(rand.nextDouble()) / Math.log(1 - W));
R += S + 1;
//System.out.println("W: " + W);
//System.out.println("S: " + S);
//System.out.println("R: " + R);
if (R < n) {
int idx = (int) Math.floor(k * rand.nextDouble());
tmp = origin[idx];
origin[idx] = origin[R];
origin[R] = tmp;
solution[idx] = origin[idx];
W = W * Math.exp(Math.log(rand.nextDouble()) / k);
} else {
break;
}
}*/
Arrays.sort(solution);
//System.out.println("solution: " + Arrays.toString(solution));
//System.exit(0);
} while (!used.add(getArrayHashCode(solution)));
//System.out.println(Arrays.toString(solution));
return solution;
}
public static int getArrayHashCode(int[] array) {
int hash = 7;
for (int i = 0; i < array.length; i++) {
hash = 29 * hash + array[i];
}
return hash;
}
}