• # Enfin...

    Posté par . En réponse au message Advent of Code 2023, jour 20. Évalué à 1.

    J'ai enfin ma solution pour le jour 20.
    1) J'ai exploré plusieurs solutions comme laisser mon PC calculé à l'aveugle pendant 1j de travail. Je chauffe en partie à l'électrique. Donc, cela ne me coutait pas de laisser mon PC cramer des Watt :).
    2) J’ai tenté de simplifier les cycles des flip-flop sans grand succès. Car je pensais que les conjuctions serait beaucoup plus complexe avec leurs multiples false, true qu’elles crachent
    3) Via récursivité de trouver une méthode pour factoriser les circuits. Je n’ai pas vu la solution suivante toute suite, car ma fonction recursive allé jusqu’au flip / flop. Mais au final,c’était trop complexe pour trouver une simplification évidente.
    4) Finalement, j’ai tenté juste de regarder les cycles des trues sur les 4 conjonctions qui sont avant RX { "hz", "pv", "qh", "xm" }
    Et là, c’était évident. J’avais des cycles réguliers pour ces 4 conjonctions. Il ne me reste plus qu’à trouver quand elles sont à true toutes les 4 en même temps. Ce qui revient à trouver le PPCM des 4 cycles.
    J’ai demandé à chatgpt de me fournir le calcul d’un PPCM (çà se reconnait au style).
    En prenant, le problème dans le bon sens, j’aurai pu le résoudre en 1h partie 1 et partie 2.... J’ai mis encore bien 5h cumulé.

    public class Aoc2023s20v3 { 
     public record Message(String sourceName, boolean hight, Component component) { 
     }
     // Hard coded main conjunction
     protected static final String[] CHECK_MAIN_RX_CONJUNCTION = new String[] {
     "hz", "pv", "qh", "xm"
     };
     // Function to find the GCD (Greatest Common Divisor) of two numbers
     private static long findGCD(long a, long b) {
     while (b != 0) {
     long temp = b;
     b = a % b;
     a = temp;
     }
     return a;
     }
     // Function to find the LCM (Lowest Common Multiple) of two numbers
     private static long findLCM(long a, long b) {
     return (a * b) / findGCD(a, b);
     }
     // Function to find the LCM of an array of numbers
     public static long findLCMOfArray(Long[] numbers) {
     if (numbers.length == 0) {
     throw new IllegalArgumentException("Array should not be empty");
     }
     long lcm = numbers[0];
     for (int i = 1; i < numbers.length; i++) {
     lcm = findLCM(lcm, numbers[i]);
     }
     return lcm;
     }
     public static abstract class Component {
     String name;
     String[] targetNames = new String[0] ;
     public Map<String, Message> getMessageHight = new HashMap<>();
     public Map<String, Message> getMessageLow = new HashMap<>();
     private Message[] sendTrue;
     private Message[] sendFalse;
     public HashMap<Long, List<Boolean>> signalHistory = new LinkedHashMap<>();
     public Component(String name) {
     this.name = name;
     }
     public void registerInput(String name) {
     }
     public void clear() {
     }
     public void process(Circuit circuit, Message message) {
     }
     public final void send(Circuit circuit, boolean sendSignalHight) { 
     Message[] toSend;
     if(sendSignalHight) {
     if(sendTrue == null) {
     sendTrue = Arrays.stream(targetNames).map(target -> new Message(this.name, sendSignalHight, circuit.elementsByName.get(target))).toArray(k-> new Message[k]);
     }
     toSend = sendTrue;
     } else {
     if(sendFalse == null) {
     sendFalse = Arrays.stream(targetNames).map(target -> new Message(this.name, sendSignalHight, circuit.elementsByName.get(target))).toArray(k-> new Message[k]);
     }
     toSend = sendFalse;
     }
     if(this instanceof Conjuction && sendSignalHight)
     signalHistory.computeIfAbsent((long)circuit.circuitIndex, (k)->new ArrayList<Boolean>()).add(sendSignalHight);
     ArrayDeque<Message> messages = circuit.messages; 
     for (Message target : toSend) { 
     messages.add(target);
     } 
     }
     public String expr(Circuit circuit) {
     return this.name;
     }
     }
     public static class FlipFlop extends Component {
     boolean state;
     public FlipFlop(String name) {
     super(name);
     }
     @Override
     public void clear() {
     state = false;
     }
     @Override
     public void process(Circuit circuit, Message message) {
     if (message.hight) {
     // do nothing
     return;
     }
     this.state = !state;
     send(circuit, this.state);
     }
     @Override
     public String toString() {
     return "FlipFlop [state=" + state + ", name=" + name + ", targetNames=" + targetNames + "]";
     }
     public String expr(Circuit circuit) {
     return "%" + this.name;
     }
     }
     public static class Conjuction extends Component {
     private long state= 0;
     private long allUp = 0;
     private Map<String, Long> inputMask = new HashMap<>();
     private Map<String, Long> notInputMask = new HashMap<>();
     public Conjuction(String name) {
     super(name);
     }
     @Override
     public void registerInput(String name) {
     int nextId = inputMask.size();
     long mask = 1L << nextId;
     inputMask.put(name, mask);
     notInputMask.put(name, ~mask);
     allUp = (1L << (nextId+1))-1;
     }
     @Override
     public void process(Circuit circuit, Message message) {
     if(message.hight) {
     long mask = inputMask.get(message.sourceName);
     state = state | mask;
     } else {
     long notMask = notInputMask.get(message.sourceName);
     state = state & notMask;
     }
     var notAllTrue = state != allUp;
     send(circuit, notAllTrue); 
     }
     @Override
     public String toString() {
     return "Conjuction [name=" + name + ", targetNames=" + targetNames + "]";
     }
     public String expr(Circuit circuit) {
     return "/*" + this.name + "*/(" + this.inputMask.keySet().stream().map(n->circuit.elementsByName.get(n).expr(circuit)).collect(Collectors.joining(" & ")) + ") \n";
     }
     }
     public static class Output extends Component {
     private long countHight;
     private long countSmall;
     public Output(String name) {
     super(name);
     }
     @Override
     public void clear() {
     this.countHight = 0;
     this.countSmall = 0;
     }
     @Override
     public void process(Circuit circuit, Message message) {
     if (message.hight)
     countHight++;
     else
     countSmall++;
     }
     @Override
     public String toString() {
     return "Output [countHight=" + countHight + ", countSmall=" + countSmall + ", name=" + name
     + ", targetNames=" + targetNames + "]";
     }
     }
     public static class Circuit {
     Map<String, Component> elementsByName = new HashMap<>();
     String[] broadcastList = new String[0]; 
     ArrayDeque<Message> messages = new ArrayDeque<>();
     long countHight;
     long countLow;
     long circuitIndex = 0;
     void parse(Scanner in) {
     while (in.hasNext()) {
     String row = in.nextLine();
     String[] part = row.split("->");
     String typeName = part[0].trim();
     String[] targetNames = Arrays.stream(part[1].trim().split(",")).map(String::trim).toArray(k ->new String[k]);
     if ("broadcaster".equals(typeName)) {
     broadcastList = targetNames;
     } else if (typeName.startsWith("%")) {
     FlipFlop flipFlop = new FlipFlop(typeName.substring(1));
     flipFlop.targetNames = targetNames;
     elementsByName.put(flipFlop.name, flipFlop);
     } else if (typeName.startsWith("&")) {
     Conjuction c = new Conjuction(typeName.substring(1));
     c.targetNames = targetNames;
     elementsByName.put(c.name, c);
     }
     }
     elementsByName.put("output", new Output("output"));
     elementsByName.put("rx", new Output("rx"));
     for (Component source : elementsByName.values()) {
     for (String target : source.targetNames) {
     Component rx= elementsByName.get(target);
     if(rx != null) {
     rx.registerInput(source.name);
     }
     }
     }
     for (String target : broadcastList) {
     elementsByName.get(target).registerInput("broadcast");
     }
     }
     public long experienceToFindWhereRxIsUp(long count) {
     messages.clear();
     elementsByName.values().forEach(Component::clear);
     countHight = 0;
     countLow = 0;
     long s = System.currentTimeMillis();
     int x=0;
     while(true) {
     circuitIndex = x;
     countLow++;
     for (String initial : broadcastList) {
     messages.add(new Message("broadcast", false, elementsByName.get(initial)));
     }
     processMessages(); 
     if(x > count) {
     System.out.println("seek " + x + " " + (System.currentTimeMillis() -s) + "ms");
     s = System.currentTimeMillis();
     return tryToFindLcmInMainConjuction();
     }
     x++;
     }
     }
     private long tryToFindLcmInMainConjuction() { 
     List<Long> toComputeLcm = new ArrayList<>();
     for(String name: CHECK_MAIN_RX_CONJUNCTION) {
     Component c= elementsByName.get(name);
     System.out.println(c.name + " => " + c.signalHistory.keySet());
     Long previous = null;
     Long previousDiff= null;
     for(Long l: c.signalHistory.keySet()) {
     if(previous !=null) {
     long diff= l-previous;
     System.out.print((l-previous) + ",");
     if(previousDiff != null) {
     if(diff == previousDiff) {
     toComputeLcm.add(diff);
     } else {
     System.out.println("Can't compute LCM");
     }
     }
     previousDiff = diff;
     }
     previous = l;
     }
     System.out.println();
     }
     return findLCMOfArray(toComputeLcm.toArray(new Long[toComputeLcm.size()]));
     }
     private void processMessages() {
     while (!messages.isEmpty()) {
     Message message = messages.pollFirst();
     if(message.hight) {
     countHight++;
     } else {
     countLow++;
     }
     message.component.process(this, message);
     }
     }
     }
     public static void main(String[] args) {
     try (Scanner in = new Scanner(Aoc2023s18v2.class.getResourceAsStream("res/t20.txt"))) {
     Circuit circuit = new Circuit();
     circuit.parse(in);
     long result = circuit.experienceToFindWhereRxIsUp(20000);
     System.out.println(result);
     }
     }
    }