//----------------------------------------------------------------------------// // // // I n j e c t i o n S o l v e r // // // //----------------------------------------------------------------------------// // <editor-fold defaultstate="collapsed" desc="hdr"> // // Copyright © Hervé Bitteur and others 2000-2013. All rights reserved. // // This software is released under the GNU General Public License. // // Goto http://kenai.com/projects/audiveris to report bugs or suggestions. // //----------------------------------------------------------------------------// // </editor-fold> package omr.math; import java.util.Arrays; /** * Class {@code InjectionSolver} handles the injection of a collection * of elements (called domain) into another collection of elements * (called range, or codomain). * * <p>It finds a mapping that minimizes the global mapping distance, given * the individual distance for each domain/range elements pair. This * implementation uses brute force, and thus should be used with small * sizes only. * * @author Hervé Bitteur */ public class InjectionSolver { //~ Instance fields -------------------------------------------------------- private final int domainSize; private final int rangeSize; private final Distance distance; private final boolean[] free; private int bestCost = Integer.MAX_VALUE; private final int[] bestConfig; private final int[] config; //~ Constructors ----------------------------------------------------------- /** * Creates a new instance of InjectionSolver * * @param domainSize size of the domain collection * @param rangeSize size of the range collection * @param distance */ public InjectionSolver (int domainSize, int rangeSize, Distance distance) { // Parameters of the solver this.domainSize = domainSize; this.rangeSize = rangeSize; this.distance = distance; free = new boolean[rangeSize]; bestConfig = new int[domainSize]; config = new int[domainSize]; // System.out.println( // "InjectionSolver domainSize=" + domainSize + " rangeSize=" + // rangeSize); } //~ Methods ---------------------------------------------------------------- //-------// // solve // //-------// /** * Report (one of) the mapping(s) for which the global distance is * minimum. * * @return an array parallel to the domain collection, which for each * (domain) element gives the mapped range element */ public int[] solve () { Arrays.fill(free, true); inspect(0, 0); return bestConfig; } //------// // dump // //------// private void dump () { StringBuilder sb = new StringBuilder(); sb.append("bestCost=") .append(bestCost); sb.append(" ["); for (int i = 0; i < bestConfig.length; i++) { sb.append(" ") .append(bestConfig[i]); } sb.append("]"); System.out.println(sb.toString()); } //---------// // inspect // //---------// private void inspect (int id, int cost) { // System.out.println("inspect id=" + id + " cost=" + cost); for (int ir = 0; ir < rangeSize; ir++) { if (free[ir]) { free[ir] = false; config[id] = ir; int newCost = cost + distance.getDistance(id, ir); /// System.out.println("ir=" + ir + " newCost=" + newCost); if (id < (domainSize - 1)) { inspect(id + 1, newCost); } else if (newCost < bestCost) { // Record best config so far System.arraycopy(config, 0, bestConfig, 0, domainSize); bestCost = newCost; // dump(); } free[ir] = true; } } } //~ Inner Interfaces ------------------------------------------------------- /** * Interface {@code Distance} provides the measurement for * individual mapping costs. */ public static interface Distance { //~ Methods ------------------------------------------------------------ /** * Report the distance when mapping element 'id' of domain to * element 'ir' of range * * @param id index of domain element * @param ir index of range element * @return the cost of mapping these two elements */ int getDistance (int id, int ir); } }