parent
a11c05759d
commit
d1e05629a4
1 changed files with 107 additions and 0 deletions
@ -0,0 +1,107 @@ |
||||
#+title: Solution of 2024-p9 |
||||
|
||||
Now, this was a bit more challenging than the others |
||||
|
||||
This binds ~data~ to the test input |
||||
#+begin_src emacs-lisp |
||||
(setq data "2333133121414131402") |
||||
#+end_src |
||||
|
||||
This instead binds ~data~ to the actual input |
||||
#+begin_src emacs-lisp |
||||
(with-temp-buffer |
||||
(insert-file-contents "input") |
||||
(goto-char (point-min)) |
||||
(replace-regexp "^" "\"") |
||||
(goto-char (point-min)) |
||||
(replace-regexp "$" "\"") |
||||
(goto-char (point-min)) |
||||
(insert "(setq data ") |
||||
(goto-char (point-max)) |
||||
(insert ")") |
||||
(eval-buffer)) |
||||
#+end_src |
||||
|
||||
#+RESULTS: |
||||
|
||||
This creates a index of the disk: each element is ~(file index |
||||
. length)~, where ~file index~ is ~nil~ if the node is empty |
||||
#+begin_src emacs-lisp |
||||
(setq disk-index (-map-indexed (lambda (index el) |
||||
(if (eq 0 (% index 2)) |
||||
(cons (/ index 2) el) |
||||
(cons nil el))) |
||||
(-map #'string-to-number (split-string data "\\|.+" t))) ) |
||||
#+end_src |
||||
|
||||
This creates the disk image from the index |
||||
#+begin_src emacs-lisp |
||||
(defun disk-from-index (d-index) |
||||
(-mapcat (lambda (x) (-repeat (cdr x) (car x))) d-index)) |
||||
|
||||
(setq disk (disk-from-image disk-index)) |
||||
#+end_src |
||||
* Part 1: |
||||
** The recursive approach |
||||
This works for the test input but stack overflows for the actual input |
||||
since elisp does not have tail call optimization |
||||
#+begin_src emacs-lisp |
||||
(defun defrag (disk) |
||||
(let ((i (-find-index #'not disk)) |
||||
(j (-find-last-index #'identity disk)) ) |
||||
(if (> i j) disk |
||||
(defrag (-replace-at j nil (-replace-at i (nth j disk) disk)))))) |
||||
#+end_src |
||||
** The while-loop approach |
||||
This works, but is quite horrible |
||||
#+begin_src emacs-lisp |
||||
(defun defrag-loop (disk) |
||||
(let ((i (-find-index #'not disk)) |
||||
(j (-find-last-index #'identity disk)) ) |
||||
(while (<= i j) |
||||
(setq disk (-replace-at j nil (-replace-at i (nth j disk) disk))) |
||||
(setq i (-find-index #'not disk) |
||||
j (-find-last-index #'identity disk)))) |
||||
disk ) |
||||
#+end_src |
||||
** The reduce-with-a-copy approach |
||||
#+begin_src emacs-lisp |
||||
(defun defrag-reduce (disk) |
||||
(-reduce-r-from (lambda (el acc) |
||||
(if (not el) acc |
||||
(let ((i (-find-index #'not acc)) |
||||
(j (-find-last-index #'identity acc))) |
||||
(if (> i j) acc |
||||
(-replace-last el nil (-replace-at i el acc)))))) |
||||
disk disk)) |
||||
#+end_src |
||||
And, compute! |
||||
#+begin_src emacs-lisp |
||||
(-reduce #'+ (-map-indexed (lambda (i file) (* i file)) (-non-nil (defrag-reduce disk)))) |
||||
|
||||
6241633730082 |
||||
|
||||
#+end_src |
||||
|
||||
* Part 2: defragging whole files |
||||
This is fun, now we start from the index |
||||
#+begin_src emacs-lisp |
||||
(defun defrag-index (d-index) |
||||
(-reduce-r-from (lambda (el acc) |
||||
(if (not (car el)) acc |
||||
(let ((i (-find-index (lambda (ell) |
||||
(and (not (car ell)) (>= (cdr ell) (cdr el)))) acc)) |
||||
(j (-find-last-index (lambda (e) (equal e el)) acc))) |
||||
(if (or (not i) (> i j)) acc |
||||
(-insert-at i el (-replace-at i (cons nil (- (cdr (nth i acc)) (cdr el))) (-replace-last el (cons nil (cdr el)) acc))))))) |
||||
d-index d-index)) |
||||
#+end_src |
||||
And compute |
||||
#+begin_src emacs-lisp |
||||
(-reduce #'+ (-map-indexed (lambda (i file) (if file (* i file) 0)) (disk-from-index (defrag-index disk-index)))) |
||||
#+end_src |
||||
|
||||
#+RESULTS: |
||||
: 6265268809555 |
||||
|
||||
** |
||||
Loading…
Reference in new issue