diff --git a/2021/README.md b/2021/README.md index 300ba54c41b58f686369fa093041f22cf4f57a31..b5d150c07f23b3bcd917fc8f2726e19128d6852b 100644 --- a/2021/README.md +++ b/2021/README.md @@ -193,3 +193,16 @@ Oh, yes. This is *much* faster. Still taking a while to get through part 2, but in a few seconds we got past the point that the StringBuilder approach took a couple of hours while I was in a meeting. But we still ran out of memory when growing the new polymer on iteration 28 of the sample data. + +Perhaps instead of creating strings, we can keep track of the positions of each +element in the polymer. Computationally, this will increase the asymptotic +complexity, probably by two orders of magnitude, but asymptotic complexity isn't +everything: memory management was definitely a HUGE constant factor. Keeping +track of the positions of each element in the polymer will use very, very little +memory. + +Yup, this has definitely slowed down computation. And, now that I think about it, +I have to create as many indices as the original string's length was. This isn't +a savings. At. All. + +What am I missing? diff --git a/2021/src/main/java/edu/unl/cse/bohn/year2021/Day14.java b/2021/src/main/java/edu/unl/cse/bohn/year2021/Day14.java index cabe3f2a9ebaec051987756bcbf1fa3322aa017c..6cc4c916e82ccd8999b62f3f7b2372b89bce52ca 100644 --- a/2021/src/main/java/edu/unl/cse/bohn/year2021/Day14.java +++ b/2021/src/main/java/edu/unl/cse/bohn/year2021/Day14.java @@ -2,10 +2,11 @@ package edu.unl.cse.bohn.year2021; import edu.unl.cse.bohn.Puzzle; -import java.util.Arrays; import java.util.HashMap; +import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.concurrent.atomic.AtomicLong; @SuppressWarnings("unused") public class Day14 extends Puzzle { @@ -46,68 +47,69 @@ public class Day14 extends Puzzle { return initialMolecule; } - @SuppressWarnings("CommentedOutCode") - private String growMolecule(String molecule) { -// StringBuilder growingMolecule = new StringBuilder(); -// String pattern = ""; -// for (int i = 0; i < molecule.length() - 1; i++) { -// pattern = molecule.substring(i, i + 2); -// growingMolecule.append(pattern.charAt(0)).append(rules.get(pattern)); -// } -// growingMolecule.append(pattern.charAt(1)); -// return growingMolecule.toString(); - char[] oldElements = molecule.toCharArray(); - char[] newElements = new char[2 * molecule.length() - 1]; - for (int i = 0; i < oldElements.length - 1; i++) { - newElements[2 * i] = oldElements[i]; - newElements[2 * i + 1] = rules.get(molecule.substring(i, i + 2)); + private long producePolymer(List<String> data, int numberOfSteps) { + Map<Character, List<AtomicLong>> molecule = new HashMap<>(); + long index = 0; + for (char element : parseData(data).toCharArray()) { + if (!molecule.containsKey(element)) { + molecule.put(element, new LinkedList<>()); + } + molecule.get(element).add(new AtomicLong(index++)); } - newElements[newElements.length - 1] = oldElements[oldElements.length - 1]; - return new String(newElements); - } - - private Map<Character, Long> countElements(String molecule) { - Map<Character, Long> counts = new HashMap<>(); - for (char element : molecule.toCharArray()) { - if (!counts.containsKey(element)) { - counts.put(element, 0L); + for (char element : molecule.keySet()) { + System.out.println("\t\t" + element + ": " + molecule.get(element)); + } + for (int i = 0; i < numberOfSteps; i++) { + System.out.println("\tGrowing...\t" + i); + growMolecule(molecule); + if (i < 4) { + for (char element : molecule.keySet()) { + System.out.println("\t\t" + element + ": " + molecule.get(element)); + } } - counts.put(element, counts.get(element) + 1); } - return counts; + long leastFrequentOccurrence = molecule.keySet().stream() + .mapToLong(element -> molecule.get(element).size()).min().orElseThrow(); + long mostFrequentOccurrence = molecule.keySet().stream() + .mapToLong(element -> molecule.get(element).size()).max().orElseThrow(); + return mostFrequentOccurrence - leastFrequentOccurrence; } - private long producePolymer(List<String> data, int numberOfSteps) { - String molecule = parseData(data); - for (int i = 0; i < numberOfSteps; i++) { - System.out.print("Molecular growth (" + i + ")--\toriginal size: " + molecule.length()); - molecule = growMolecule(molecule); - System.out.println("\tnew size: " + molecule.length()); -// if (molecule.length() < 60) { -// String expectedString = switch (i) { -// case 0 -> "NCNBCHB"; -// case 1 -> "NBCCNBBBCBHCB"; -// case 2 -> "NBBBCNCCNBBNBNBBCHBHHBCHB"; -// case 3 -> "NBBNBNBBCCNBCNCCNBBNBBNBBBNBBNBBCBHCBHHNHCBBCBHCB"; -// default -> "??"; -// }; -// System.out.println("\texpected: " + expectedString); -// System.out.println("\t actual: " + molecule); -// assert (molecule.equals(expectedString)); -// } + private Character getCharAt(Map<Character, List<AtomicLong>> molecule, long index) { + return molecule.keySet().stream() + .filter(e -> molecule.get(e).stream().anyMatch(position -> position.get() == index)) + .findFirst().orElse(null); + } + + private void incrementIndicesAfter(Map<Character, List<AtomicLong>> molecule, long start) { + for (char element : molecule.keySet()) { + molecule.get(element).stream() + .filter(position -> start <= position.get()) + .forEach(AtomicLong::getAndIncrement); } - Map<Character, Long> elementCounts = countElements(molecule); - char leastFrequentElement = molecule.charAt(0); - char mostFrequentElement = molecule.charAt(0); - for (char element : elementCounts.keySet()) { - if (elementCounts.get(element) < elementCounts.get(leastFrequentElement)) { - leastFrequentElement = element; + } + + @SuppressWarnings("UnusedReturnValue") + private Map<Character, List<AtomicLong>> growMolecule(Map<Character, List<AtomicLong>> molecule) { + boolean stillGrowing = true; + long index = 0; + char[] pattern = {getCharAt(molecule, index), getCharAt(molecule, ++index)}; + do { + incrementIndicesAfter(molecule, index); + char newElement = rules.get(new String(pattern)); + if (!molecule.containsKey(newElement)) { + molecule.put(newElement, new LinkedList<>()); } - if (elementCounts.get(element) > elementCounts.get(mostFrequentElement)) { - mostFrequentElement = element; + molecule.get(newElement).add(new AtomicLong(index++)); + Character nextElement = getCharAt(molecule, ++index); + if (nextElement == null) { + stillGrowing = false; + } else { + pattern[0] = pattern[1]; + pattern[1] = nextElement; } - } - return elementCounts.get(mostFrequentElement) - elementCounts.get(leastFrequentElement); + } while (stillGrowing); + return molecule; } @Override