/*
 * Redberry: symbolic tensor computations.
 *
 * Copyright (c) 2010-2015:
 *   Stanislav Poslavsky   
 *   Bolotin Dmitriy       
 *
 * This file is part of Redberry.
 *
 * Redberry 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.
 *
 * Redberry 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.
 *
 * You should have received a copy of the GNU General Public License
 * along with Redberry. If not, see .
 */
package cc.redberry.core.solver.frobenius;

import java.math.BigInteger;
import java.util.Arrays;
import java.util.HashMap;

import static java.math.BigInteger.ONE;
import static java.math.BigInteger.ZERO;

/**
 * @author Dmitry Bolotin
 * @author Stanislav Poslavsky
 */
public class FrobeniusNumber {

    private static final BigInteger MINUS_ONE = BigInteger.valueOf(-1);

    /**
     * Returns Frobenius number
     * 

*
The implementation is taken from *
* http://dansesacrale.wordpress.com/2010/09/20/frobenius-numbers-round-robin-algorithm/ *
*
* * @param array "coins denominations" * @return Frobenius number */ public static BigInteger frobeniusNumber(int[] array) { BigInteger[] _array = new BigInteger[array.length]; for (int i = array.length - 1; i >= 0; --i) _array[i] = BigInteger.valueOf(array[i]); return frobeniusNumber(_array); } /** * Returns Frobenius number *

*
The implementation is taken from *
* http://dansesacrale.wordpress.com/2010/09/20/frobenius-numbers-round-robin-algorithm/ *
*
* * @param array "coins denominations" * @return Frobenius number */ public static BigInteger frobeniusNumber(long[] array) { BigInteger[] _array = new BigInteger[array.length]; for (int i = array.length - 1; i >= 0; --i) _array[i] = BigInteger.valueOf(array[i]); return frobeniusNumber(_array); } private static int ARRAY_SIZE_THRESHOLD_INT = 10000; private static BigInteger ARRAY_SIZE_THRESHOLD = BigInteger.valueOf(ARRAY_SIZE_THRESHOLD_INT); /** * Returns Frobenius number *

*
The implementation is taken from *
* http://dansesacrale.wordpress.com/2010/09/20/frobenius-numbers-round-robin-algorithm/ *
*
* * @param array "coins denominations" * @return Frobenius number */ public static BigInteger frobeniusNumber(BigInteger[] array) { if (array[0].compareTo(ARRAY_SIZE_THRESHOLD) > 0) return frobeniusNumberIntegerArray(array); else return frobeniusNumberIntegerArray(array); } public static BigInteger frobeniusNumberBigIntArray(BigInteger[] array) { HashMap ns = new HashMap<>(ARRAY_SIZE_THRESHOLD_INT); ns.put(ZERO, ZERO); for (int i = 1; i < array.length; i++) { BigInteger d = gcd(array[0], array[i]); for (BigInteger r = ZERO; r.compareTo(d) < 0; r = r.add(ONE)) { BigInteger n = MINUS_ONE; if (r.compareTo(ZERO) == 0) n = ZERO; else { BigInteger q = r; while (q.compareTo(array[0]) < 0) { if (ns.containsKey(q) && (ns.get(q).compareTo(n) < 0 || n.equals(MINUS_ONE))) n = ns.get(q); q = q.add(d); } } if (!n.equals(MINUS_ONE)) for (int j = 0, size = intValue(array[0].divide(d)); j < size; j++) { n = n.add(array[i]); BigInteger p = n.remainder(array[0]); if (ns.containsKey(p) && (ns.get(p).compareTo(n) < 0 || n.equals(MINUS_ONE))) n = ns.get(p); ns.put(p, n); } } } BigInteger max = MINUS_ONE; for (BigInteger c : ns.values()) if (c.compareTo(max) > 0) max = c; return max.subtract(array[0]); } private static BigInteger frobeniusNumberIntegerArray(BigInteger[] array) { int array0 = intValue(array[0]); BigInteger[] ns = new BigInteger[array0]; Arrays.fill(ns, MINUS_ONE); ns[0] = ZERO; for (int i = 1; i < array.length; i++) { int d = intValue(gcd(array[0], array[i])); for (int r = 0; r < d; r++) { BigInteger n = MINUS_ONE; if (r == 0) n = ZERO; else { int q = r; while (q < array0) { if (!ns[q].equals(MINUS_ONE) && (ns[q].compareTo(n) < 0 || n.equals(MINUS_ONE))) n = ns[q]; q += d; } } if (!n.equals(MINUS_ONE)) for (int j = 0, size = array0 / d; j < size; j++) { n = n.add(array[i]); int p = intValue(n.remainder(array[0])); if (!ns[p].equals(MINUS_ONE) && (ns[p].compareTo(n) < 0 || n.equals(MINUS_ONE))) n = ns[p]; ns[p] = n; } } } BigInteger max = ZERO; for (int i = 0; i < array0; i++) if (ns[i].equals(MINUS_ONE) || ns[i].compareTo(max) > 0) max = ns[i]; if (max.equals(MINUS_ONE)) return MINUS_ONE; return max.subtract(array[0]); } private static BigInteger gcd(BigInteger a, BigInteger b) { return a.gcd(b); } private static final BigInteger MAX_VALUE = BigInteger.valueOf(Integer.MAX_VALUE); private static int intValue(BigInteger integer) { if (integer.compareTo(MAX_VALUE) > 0) throw new UnsupportedOperationException("Integer overflow."); return integer.intValue(); } }

Ads help maintain this website.