import java.math.BigInteger; public class A372309 { public static final int TERMS = 17; public static final int START_BASE = 2; public static final int LARGE_PRIME_INDEX = 1000000000; public static final boolean VERBOSE = true; public static void main(String[] args) { Factor.setBase(START_BASE); for (int base = START_BASE; base < START_BASE + TERMS; base++) { Factor.update(LARGE_PRIME_INDEX); BigInteger upperBound = GLPKSolver.run().getBigVal(); if (VERBOSE) { System.out.println("Guess: " + upperBound.longValue()); } BigInteger maxVal = upperBound; int concat = base; while (opinionorial(concat).multiply(factorial(base - concat)).compareTo(upperBound) > 0) { if (opinionorial(concat).compareTo(maxVal) < 0) { maxVal = opinionorial(concat); } concat--; } if (VERBOSE) { System.out.println("Upper Bound: " + maxVal); } if (BigInteger.valueOf(maxVal.longValue()).compareTo(maxVal) != 0) { System.err.print("Error! Max val calculation had long wrap-around."); } Factor.update(maxVal.longValue()); if (VERBOSE) { System.out.println("Primes to check: " + Factor.count()); } if (VERBOSE) { System.out.println("Final calculation!"); } Result finalResult = GLPKSolver.run(); if (VERBOSE) { System.out.println(finalResult.toString() + "\n" + "\n"); } else { System.out.print(finalResult.getBigVal() + ", "); } Factor.nextBase(); } } private static BigInteger factorial(int num) { BigInteger prod = BigInteger.ONE; for (int i = 1; i <= num; i++) { prod = prod.multiply(BigInteger.valueOf(i)); } return prod; } private static BigInteger opinionorial(int num) { BigInteger offset = BigInteger.valueOf(Factor.getBase() - num); BigInteger sum = BigInteger.ONE.add(offset); for (int i = 1; i < num; i++) { sum = sum.multiply(BigInteger.valueOf(Factor.getBase())); if (i != 1) { sum = sum.add(BigInteger.valueOf(i)).add(offset); } } return sum; } } import java.util.ArrayList; import java.util.HashSet; public class Factor { private static final ArrayList < Long > primes = new ArrayList < > (); private static long maxVal; public static void update(long uMaxVal) { primes.clear(); maxVal = uMaxVal; long sqrtMax = (long) Math.ceil(Math.sqrt((double) maxVal)); final int MAX_SEGMENT_SIZE = 10000000; int segmentSize = (int) Math.min(sqrtMax, MAX_SEGMENT_SIZE); boolean[] firstIsComposite = new boolean[segmentSize]; firstIsComposite[0] = true; firstIsComposite[1] = true; for (int p = 0; p < segmentSize; p++) { if (!firstIsComposite[p]) { primes.add((long) p); for (int i = p; (long) i * (long) p < segmentSize; i++) { firstIsComposite[i * p] = true; } } } if (!findCovering()) { update(maxVal * 2); return; } long segmentStart = 0 L; long segmentEnd = (long) segmentSize - 1; double progress = ((double) segmentSize) / ((double) maxVal); boolean printProgress = A372309.VERBOSE; primeSearch: while (true) { if (printProgress) { progress += ((double) segmentSize) / ((double) maxVal); while (progress >= 0.05) { System.out.print("*"); progress -= 0.05; } } segmentStart += segmentSize; segmentEnd += segmentSize; boolean[] isComposite = new boolean[segmentSize]; int pIndex = 0; while (pIndex < primes.size() && val(pIndex) * val(pIndex) <= segmentEnd) { long lower = segmentStart / val(pIndex); while (lower * val(pIndex) < segmentStart) { lower++; } while (lower * val(pIndex) <= segmentEnd) { int compositeIndex = (int)((lower * val(pIndex)) - segmentStart); isComposite[compositeIndex] = true; lower++; } pIndex++; } for (int p = 0; p < segmentSize; p++) { long primeValue = segmentStart + (long) p; if (maxVal < primeValue) { break primeSearch; } if (!isComposite[p] && (primeValue <= sqrtMax || quickCover(primeValue) >= primeValue)) { primes.add(primeValue); } } } if (printProgress) { System.out.println(""); } System.gc(); } public static long val(int index) { if (index >= primes.size()) { System.err.println("Attempted to access inaccessible prime!"); } return primes.get(index); } public static double log(int index) { return Math.log((double) val(index)); } public static int count() { return primes.size(); } public static void print() { for (int i = 0; i < primes.size(); i++) { System.out.print(primes.get(i) + ", "); } System.out.println(); } public static void add_TEST(long num) { primes.add(num); } public static void remove_TEST(int index) { primes.remove(index); } private static int base = 2; public static boolean hasDigit(long num, int digit) { while (num > 0) { if (num % base == digit) { return true; } num /= base; } return false; } public static boolean indexHasDigit(int index, int digit) { return hasDigit(val(index), digit); } public static void setBase(int newBase) { base = newBase; } public static void nextBase() { setBase(base + 1); } public static int getBase() { return base; } public static int len(int index) { long p = Factor.val(index); int sum = 0; while (p > 0) { sum++; p /= base; } return sum; } private static final ArrayList < Long > cover1 = new ArrayList < > (); private static final ArrayList < Long > cover2 = new ArrayList < > (); private static final ArrayList < Long > cover3 = new ArrayList < > (); private static HashSet < Long > coverIDarray; private static boolean findCovering() { cover1.clear(); for (int d1 = 0; d1 < base; d1++) { boolean foundCover = false; for (int pIndex = 0; pIndex < primes.size(); pIndex++) { if (indexHasDigit(pIndex, d1)) { cover1.add(val(pIndex)); foundCover = true; break; } } if (!foundCover) { return false; } } cover2.clear(); for (int d1 = 0; d1 < base; d1++) { for (int d2 = 0; d2 < base; d2++) { long smallest = cover1.get(d1) * cover1.get(d2); for (int pIndex = 0; pIndex < primes.size(); pIndex++) { if (val(pIndex) < smallest && indexHasDigit(pIndex, d1) && indexHasDigit(pIndex, d2)) { smallest = val(pIndex); break; } } cover2.add(smallest); } } cover3.clear(); for (int d1 = 0; d1 < base; d1++) { for (int d2 = 0; d2 < base; d2++) { for (int d3 = 0; d3 < base; d3++) { long smallest = cover1.get(d1) * cover1.get(d2) * cover1.get(d3); if (cover1.get(d1) * cover2.get(base * d2 + d3) < smallest) { smallest = cover1.get(d1) * cover2.get(base * d2 + d3); } if (cover2.get(d1 * base + d2) * cover1.get(d3) < smallest) { smallest = cover2.get(d1 * base + d2) * cover1.get(d3); } for (int pIndex = 0; pIndex < primes.size(); pIndex++) { if (val(pIndex) < smallest && indexHasDigit(pIndex, d1) && indexHasDigit(pIndex, d2) && indexHasDigit(pIndex, d3)) { smallest = val(pIndex); break; } } cover3.add(smallest); } } } coverIDarray = new HashSet < > (); return true; } public static long quickCover(long num) { if (num <= base * base * base) { return base * base * base + 1; } boolean[] coverage = new boolean[base]; long coverageID = 0; for (int d = 0; d < base; d++) { coverage[d] = hasDigit(num, d); if (coverage[d]) { coverageID += (long) Math.pow(2, d); } } if (coverIDarray.contains(coverageID)) { return -1; } coverIDarray.add(coverageID); long prod = 1; int d1 = 0; int d2 = 0; int d3 = 0; sumLoop: while (true) { while (!coverage[d1]) { d1++; if (d1 == base) { break sumLoop; } } d2 = d1 + 1; if (d2 == base) { prod *= cover1.get(d1); break sumLoop; } while (!coverage[d2]) { d2++; if (d2 == base) { prod *= cover1.get(d1); break sumLoop; } } d3 = d2 + 1; if (d3 == base) { prod *= cover2.get(d2 * base + d1); break sumLoop; } while (!coverage[d3]) { d3++; if (d3 == base) { prod *= cover2.get(d2 * base + d1); break sumLoop; } } prod *= cover3.get((d3 * base * base) + d2 * base + d1); d1 = d3 + 1; if (d1 == base) { break sumLoop; } } return prod; } } import org.gnu.glpk.*; public class GLPKSolver { public static Result run() { int rows = Factor.getBase(); int cols = Factor.count(); GLPK.glp_term_out(GLPK.GLP_OFF); glp_prob lp = GLPK.glp_create_prob(); GLPK.glp_add_cols(lp, cols); for (int i = 1; i <= cols; i++) { GLPK.glp_set_col_name(lp, i, "p" + (i - 1)); GLPK.glp_set_col_kind(lp, i, GLPKConstants.GLP_BV); // Binary variable GLPK.glp_set_col_bnds(lp, i, GLPKConstants.GLP_DB, 0, 1); } GLPK.glp_add_rows(lp, Factor.getBase()); for (int row = 1; row <= rows; row++) { GLPK.glp_set_row_name(lp, row, "d" + (row - 1)); GLPK.glp_set_row_bnds(lp, row, GLPKConstants.GLP_LO, b(row), 0); SWIGTYPE_p_int ind = GLPK.new_intArray(cols + 1); SWIGTYPE_p_double val = GLPK.new_doubleArray(cols + 1); for (int col = 1; col <= cols; col++) { GLPK.intArray_setitem(ind, col, col); GLPK.doubleArray_setitem(val, col, A(row, col)); //m.A[row-1][col-1] } GLPK.glp_set_mat_row(lp, row, cols, ind, val); GLPK.delete_intArray(ind); GLPK.delete_doubleArray(val); } GLPK.glp_set_obj_name(lp, "obj"); GLPK.glp_set_obj_dir(lp, GLPKConstants.GLP_MIN); for (int col = 1; col <= cols; col++) { GLPK.glp_set_obj_coef(lp, col, c(col)); //m.c[col-1] } glp_iocp iocp = new glp_iocp(); GLPK.glp_init_iocp(iocp); iocp.setMsg_lev(GLPKConstants.GLP_JAVA_MSG_LVL_OFF); iocp.setPresolve(GLPKConstants.GLP_ON); GLPK.glp_intopt(lp, iocp); int status = GLPK.glp_mip_status(lp); boolean[] hasFactors = new boolean[Factor.count() + 1]; if (status == GLPKConstants.GLP_OPT) { for (int col = 1; col <= cols; col++) { double value = GLPK.glp_mip_col_val(lp, col); if (value == 1.0) { hasFactors[col - 1] = true; } } } else { System.out.println("Error"); } GLPK.glp_delete_prob(lp); return new Result(hasFactors); } public static double A(int row, int col) { return Factor.indexHasDigit(col - 1, row - 1) ? 1 : 0; } public static double b(int row) { return 1; } public static double c(int col) { return Factor.log(col - 1); } } public class Result { private BigInteger bigVal; private boolean[] hasFactors; private int digitalLength; private String decFactorString; private String baseFactorString; private int base; public Result(boolean[] cHasFactors) { base = Factor.getBase(); decFactorString = ""; baseFactorString = ""; bigVal = BigInteger.ONE; digitalLength = 0; hasFactors = new boolean[cHasFactors.length]; boolean firstFactor = true; for (int i = 0; i < cHasFactors.length; i++) { hasFactors[i] = cHasFactors[i]; if (cHasFactors[i]) { bigVal = bigVal.multiply(BigInteger.valueOf(Factor.val(i))); digitalLength += Factor.len(i); if (firstFactor) { firstFactor = false; } else { decFactorString += ", "; baseFactorString += ", "; } decFactorString += Factor.val(i); String num = ""; long p = Factor.val(i); while (p > 0) { num = (p % base) + "|" + num; p /= base; } baseFactorString += "|" + num; } } } public BigInteger getBigVal() { return bigVal; } public boolean hasFactor(int index) { if (index >= hasFactors.length) { return false; } return hasFactors[index]; } public int getDigitalLength() { return digitalLength; } public String getDecFactorString() { return decFactorString; } public String getBaseFactorString() { return baseFactorString; } public int getBase() { return base; } @Override public String toString() { String str = ""; str += "Base: " + getBase() + "\n"; str += "Value: " + getBigVal().toString() + "\n"; str += "Factors (decimal): " + getDecFactorString() + "\n"; str += "Factors (base " + getBase() + "): " + getBaseFactorString(); return str; } }