/* * ARX: Powerful Data Anonymization * Copyright 2012 - 2017 Fabian Prasser, Florian Kohlmayer and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.deidentifier.arx.algorithm; import java.util.Comparator; import org.deidentifier.arx.framework.data.GeneralizationHierarchy; import org.deidentifier.arx.framework.lattice.SolutionSpace; import org.deidentifier.arx.framework.lattice.Transformation; /** * This class implements a total order on all transformations in the search space. It is * used by the Flash algorithm to achieve stable execution times. * * @author Fabian Prasser * @author Florian Kohlmayer */ public class FLASHStrategy implements Comparator<Integer> { /** The distinct values. */ private final int[][] distinct; /** The maximal level in the lattice. */ private final int maxlevel; /** The maximal level for each quasi-identifier. */ private final int[] maxLevels; /** The cached values for a node with id 'index'. */ private final double[][] cache; /** The solution space */ private final SolutionSpace solutionSpace; /** * Creates a new instance. * * @param solutionSpace the solution space * @param hierarchies the hierarchies */ public FLASHStrategy(final SolutionSpace solutionSpace, final GeneralizationHierarchy[] hierarchies) { // Check if (solutionSpace.getSize() > Integer.MAX_VALUE) { throw new IllegalArgumentException("Solution space is too large for executing the FLASH algorithm"); } // Store this.solutionSpace = solutionSpace; // Prepare information about levels int level = 0; this.maxLevels = solutionSpace.getTop().getGeneralization().clone(); for (int i=0; i < this.maxLevels.length; i++) { level += this.maxLevels[i]; this.maxLevels[i] ++; } this.maxlevel = level; // Prepare information about distinct values this.distinct = new int[hierarchies.length][]; for (int i = 0; i < hierarchies.length; i++) { this.distinct[i] = hierarchies[i].getDistinctValues(); } // Prepare cache this.cache = new double[(int)solutionSpace.getSize()][]; } /** * Compares transformations. * * @param n1 * the n1 * @param n2 * the n2 * @return the int */ @Override public int compare(final Integer n1, final Integer n2) { // Obtain vals if (cache[n1] == null) { cache[n1] = getCriteria(n1); } if (cache[n2] == null) { cache[n2] = getCriteria(n2); } final double[] m1 = cache[n1]; final double[] m2 = cache[n2]; // Compare vals if (m1[0] < m2[0]) { return -1; } else if (m1[0] > m2[0]) { return +1; } else if (m1[1] < m2[1]) { return -1; } else if (m1[1] > m2[1]) { return +1; } else if (m1[2] < m2[2]) { return -1; } else if (m1[2] > m2[2]) { return +1; } else { return 0; } } /** * Returns the criteria that determines a transformations's position. * * @param id the id * @return the value */ private final double[] getCriteria(final int id) { // Prepare double level = 0; double prec = 0; double ddistinct = 0; Transformation transformation = solutionSpace.getTransformation(id); int[] generalization = transformation.getGeneralization(); // Compute level = ((double) transformation.getLevel() / (double) maxlevel); for (int i = 0; i < generalization.length; i++) { ddistinct += ((double) distinct[i][generalization[i]] / (double) (distinct[i][0])); prec += ((double) generalization[i] / (double) (maxLevels[i] - 1)); } ddistinct /= generalization.length; prec /= generalization.length; ddistinct = 1d - ddistinct; // Return return new double[] { level, prec, ddistinct }; } }