Skip to content

Commit 6533a9f

Browse files
committed
15/22/23: Pure bash. Minor speedup in slowest days
14: Use printf -v instead of eval 15B: Use function name reference instead of eval 22B: Remove subshells for score Simplify hash and recursion check. Script return code is not depending on the winner of part 2. Speeds up by 15% for some reason. 23B: Add optional pure bash version instead of awk only. Runs in about 20 minutes. Had a 15 minute version but I didn't commit it. Cleanup: Use globs in game of life.
1 parent 7d0c52c commit 6533a9f

File tree

9 files changed

+85
-53
lines changed

9 files changed

+85
-53
lines changed

11.sh

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ while [ ${#I} != 0 ]; do
77
for i in $I; do
88
for j in $J; do
99
x=${B[i]:j:1}
10-
if [[ "$x" == "L" || "$x" == "X" ]]; then
10+
if [[ "$x" == [LX] ]]; then
1111
s="${B[i-1]:j-1:3}${B[i]:j-1:3}${B[i+1]:j-1:3}"
1212
s=${s//[L.]}; s=${s/$x}
13-
if [[ ${#s} == 0 ]]; then x=X; elif [[ ${#s} -ge 4 ]]; then x=L; fi
13+
if [[ -z $s ]]; then x=X; elif (( ${#s} >= 4 )); then x=L; fi
1414
fi
1515
l+=$x
1616
done
@@ -33,7 +33,7 @@ while [ ${#change} != 0 ]; do
3333
for i in $I; do
3434
for j in $J; do
3535
x=${B[i]:$j:1}
36-
[[ "$x" != "L" && "$x" != "X" ]] && l+=$x && continue
36+
[[ "$x" != [LX] ]] && l+=$x && continue
3737
r=${B[i]:j+1}; r=${r//.}; R=${B[i]:0:j}; R=${R//.}
3838
s=${R: -1}${r:0:1}
3939
k=1; d=${B[i-k]:j-k:1}; while [ "$d" = '.' ]; do ((++k)); d=${B[i-k]:j-k:1}; done; s+=$d
@@ -44,7 +44,7 @@ while [ ${#change} != 0 ]; do
4444
k=1; d=${B[i+k]:j+k:1}; while [ "$d" = '.' ]; do ((++k)); d=${B[i+k]:j+k:1}; done; s+=$d
4545
#[[ ${#s} == 8 ]] || echo "ERROR: ($s) $k = $i, $j"
4646
s=${s//L}
47-
if [[ ${#s} == 0 ]]; then l+=X; elif [[ ${#s} -ge 5 ]]; then l+=L; else l+=$x; fi
47+
if [[ -z $s ]]; then l+=X; elif (( ${#s} >= 5 )); then l+=L; else l+=$x; fi
4848
done
4949
[ "${B[i]}" != "L${l}L" ] && change+=1
5050
C+=(L${l}L); l=

14.sh

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
#! /usr/bin/env bash
22
IFS=$'\n'
33
A=($(< "${1:-14.txt}"))
4-
declare -a mem
4+
declare -a mem mem2
55
for i in "${A[@]}"; do
66
case $i in
7-
mask*) y=${i//*= }; o=$((2#${y//X/0})); z=$((2#${y//X/1}));;
8-
mem*) y=${i//*= }; eval "${i// =*}=$(((y&z)|o))";;
7+
mask*) y=${i/*= }; o=$((2#${y//X/0})); z=$((2#${y//X/1}));;
8+
mem*) y=${i/*= }; printf -v "${i/ =*}" "%s" $(((y&z)|o));;
99
esac
1010
done
1111
printf -v sum "+%s" "${mem[@]}"

15.sh

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
#! /usr/bin/env bash
22
A=($(<"${1:-15.txt}"))
3-
PUREBASH=${2:-$PUREBASH}
43
declare -a B=()
54
i=0; l=""
65
for a in "${A[@]}"; do B[a]=$((++i)); done
@@ -9,28 +8,27 @@ while [ $i -lt 2020 ]; do
98
done
109
echo "15A: $n"
1110

12-
# eval is slow, but large arrays are slower
1311
# Split the higher numbers into smaller arrays
12+
# large arrays are incredibly slow
1413
sharded_swap() { # syntax: l=B[$1]; B[$1]=$2
15-
local x="B$(($1>>8))[$(($1&255))]"
16-
#local x="B$(($1>>6))[$1&63]"
17-
eval "l=\${$x}; $x=$2"
14+
local -n x=$1
15+
l=$x; x=$2
1816
}
1917

20-
if [ -n "$PUREBASH" ]; then
18+
if [[ -n ${2:-$PUREBASH} ]]; then
2119
trap 'echo "Giving up (i=$i, $SECONDS seconds)"; exit 1' TERM INT
22-
echo "This could take 15-20 minutes. Ctrl-C or 'kill $$' to stop."
20+
echo "Part 2 could take 15-20 minutes. Ctrl-C or 'kill $$' to stop."
2321
declare -a B{1..1000}
24-
for N in $(seq 1000000 1000000 30000000 ); do
22+
for N in {1000000..30000000..1000000}; do
2523
while ((i < N)); do
2624
n=$((i-${l:-$i}));
2725
if (( n < 2048 )); then
2826
l=${B[n]}; B[n]=$((++i))
2927
else
30-
sharded_swap $n $((++i))
28+
sharded_swap "B$((n>>8))[$((n&255))]" $((++i))
3129
fi
3230
done
33-
echo "$i took $SECONDS seconds" ;
31+
echo "$i took $SECONDS seconds"
3432
done
3533
echo "15B: $n"
3634
else

17.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ solve17() {
3030
done
3131
for i in "${!C[@]}"; do
3232
if [[ -n "${B[$i]}" ]]; then
33-
[[ ${#C[$i]} == 2 || ${#C[$i]} == 3 ]] || unset "B[$i]"
33+
[[ ${#C[$i]} == [23] ]] || unset "B[$i]"
3434
else
3535
[[ ${#C[$i]} == 3 ]] && B[$i]=1
3636
fi

22.sh

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,28 +17,27 @@ while [[ -n "$A" && -n "$B" ]]; do
1717
B=(${B[@]:1})
1818
#((++round%500==0)) && echo "$round: ${#A[@]}/${#B[@]}"
1919
done
20-
echo "22A: $(score "${A[@]}" "${B[@]}")"
20+
printf "22A: "; score "${A[@]}" "${B[@]}"
2121

2222
r() {
2323
declare -A H
24-
local a=($1) b=($2) hash
24+
local a=($1) b=($2) depth=$3
2525
# shellcheck disable=SC2128,SC2181
2626
while [[ -n "$a" && -n "$b" ]]; do
27-
hash=${a[*]}X${b[*]}
28-
if (( H[${hash// /_}]++ > 0)); then
27+
if (( ++H[${a[*]}X${b[*]}] > 1)); then
2928
return 0
3029
elif [[ $a -lt ${#a[@]} && $b -lt ${#b[@]} ]]; then
31-
r "${a[*]:1:a}" "${b[*]:1:b}"
30+
r "${a[*]:1:a}" "${b[*]:1:b}" $((depth+1))
3231
else
3332
[[ $a -gt $b ]]
3433
fi
3534
if [[ $? == 0 ]]; then a+=($a $b); else b+=($b $a); fi
3635
a=(${a[@]:1})
3736
b=(${b[@]:1})
38-
#((${#H[@]}%1000==0)) && echo "$round:${#H[@]}: ${#a[@]}/${#b[@]}"
37+
#((${#H[@]}%1000==0)) && echo "$depth:${#H[@]}: ${#a[@]}/${#b[@]}"
3938
done
40-
((${#a[*]}+${#b[*]} == N)) && echo "22B: $(score "${a[@]}" "${b[@]}")"
41-
return ${#b[@]}
39+
[[ $depth != 0 ]] && return ${#b[@]}
40+
printf "22B: "; score "${a[@]}" "${b[@]}"
4241
}
4342

44-
r "${C[*]:0:N/2}" "${C[*]:N/2}"
43+
r "${C[*]:0:N/2}" "${C[*]:N/2}" 0

23.sh

Lines changed: 55 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,24 +13,60 @@ while [ "${A:i:1}" != "$c" ]; do ((++i==9)) && i=0; done
1313
((++i==9)) && i=0
1414
done
1515
echo "23A: ${A/*1}${A/1*}"
16-
A=$(<"${1:-23.txt}")
16+
A=$(<"${1:-23.txt}") a=0 b=0 c=0
1717
#A=389125467
18-
grep -o "[1-9]" <<< "$A" | awk '
19-
{ if(last){ C[last]=$0 }else{ cup=$0 }; last=$0; ++i; }
20-
END{
21-
N=10000000;
22-
max=1000000;
23-
C[last]=++i;
24-
while (i<max) {C[i-1]=++i}
25-
C[max]=cup;
26-
for (i=0; i<N; i++) {
27-
a=C[cup]; b=C[a]; c=C[b];
28-
d=cup; if (--d<=0) { d=max }
29-
while (d == a || d == b || d == c) {
30-
if (--d<=0) { d=max }
31-
}
32-
C[cup]=C[c];
33-
tmp=C[d]; C[d]=a; C[c]=tmp; cup=C[cup];
18+
if [[ -n ${2:-$PUREBASH} ]]; then
19+
getC() {
20+
local -n x_=$1 Cy=$2
21+
# shellcheck disable=SC2034
22+
x_=$Cy
23+
}
24+
swap() {
25+
declare -n Cc=$1 Cd=$2 Ccup=$3
26+
Ccup=$Cc; tmp=$Cd; Cd=$a; Cc=$tmp; cup=$Ccup
3427
}
35-
printf "23B: %d*%d = %d\n", C[1] , C[C[1]], C[1]*C[C[1]]
36-
}'
28+
trap 'echo "Giving up (i=$i, $SECONDS seconds)"; exit 1' TERM INT
29+
echo "Part 2 could take 15-20 minutes. Ctrl-C or 'kill $$' to stop."
30+
N=10000000 max=1000000
31+
B=($(grep -o "[1-9]" <<< "$A"))
32+
cup=${B[0]}
33+
j=0; for i in {1..1000000}; do printf -v "C$((j>>8))[j&255]" "%s" "$i"; j=$i; done
34+
last=$cup; for i in "${B[@]:1}"; do C0[last]=$i; last=$i; done
35+
printf -v "C$((max>>8))[max&255]" "%s" "$cup"
36+
echo "Ready $SECONDS second $cup"
37+
i=0
38+
for N in {500000..10000000..500000}; do
39+
for (( ; i < N; ++i)); do
40+
getC a "C$((cup>>8))[cup&255]"; getC b "C$((a>>8))[a&255]"; getC c "C$((b>>8))[b&255]"
41+
((d=cup-1)) || d=$max
42+
while ((d == a || d == b || d == c)); do
43+
((--d)) || d=$max
44+
done
45+
swap "C$((c>>8))[c&255]" "C$((d>>8))[d&255]" "C$((cup>>8))[cup&255]"
46+
done
47+
echo "$i took $SECONDS seconds: cup=$cup"
48+
done
49+
a=${C0[1]}; getC b "C$((a>>8))[a&255]"
50+
echo "23B: $a * $b = $((a*b))"
51+
else
52+
grep -o "[1-9]" <<< "$A" | awk '
53+
{ if(last){ C[last]=$0 }else{ cup=$0 }; last=$0; ++i; }
54+
END {
55+
N=10000000;
56+
max=1000000;
57+
C[last]=++i;
58+
while (i<max) {C[i-1]=++i}
59+
C[max]=cup;
60+
for (i=0; i<N; i++) {
61+
#if (i%500000 ==0) {print i, "cup=", cup }
62+
a=C[cup]; b=C[a]; c=C[b];
63+
d=cup; if (--d<=0) { d=max }
64+
while (d == a || d == b || d == c) {
65+
if (--d<=0) { d=max }
66+
}
67+
C[cup]=C[c]; tmp=C[d]; C[d]=a; C[c]=tmp;
68+
cup=C[cup];
69+
}
70+
printf "23B(awk): %d*%d = %d\n", C[1] , C[C[1]], C[1]*C[C[1]]
71+
}'
72+
fi

24.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ solve24() {
3030
done
3131
for i in "${!C[@]}"; do
3232
if [[ -n "${B[$i]}" ]]; then
33-
[[ ${#C[$i]} == 1 || ${#C[$i]} == 2 ]] || unset "B[$i]"
33+
[[ ${#C[$i]} == [12] ]] || unset "B[$i]"
3434
else
3535
[[ ${#C[$i]} == 2 ]] && B[$i]=1
3636
fi

25.sh

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
#! /usr/bin/env bash
22
A=($(< "${1:-25.txt}"))
3-
PUREBASH=${2:-$PUREBASH}
4-
if [ -n "$PUREBASH" ]; then
3+
if [[ -n ${2:-$PUREBASH} ]]; then
54
trap 'echo "Giving up after $SECONDS seconds"; exit 1' INT TERM
65
echo "This could take a minute. Ctrl-C or 'kill $$' to exit"
76
declare -i k=1 e=1 key1=${A[0]} key2=${A[1]}

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ https://adventofcode.com/2020/
44

55
Input not included, but can be given on the command line.
66
Defaults to *number*.txt in the same folder (no leading 0).
7-
Setting env variable PUREBASH to any value (or giving a second argument) will use slow bash instead of using faster awk in days 15 and 25.
7+
Setting env variable PUREBASH to any value (or giving a second argument) will use slow bash instead of using faster awk in days 15, 23 and 25.
88

99
Description of what I'm doing. Contains spoilers....
1010

@@ -66,7 +66,7 @@ Description of what I'm doing. Contains spoilers....
6666
2. Simple (possibly not correct) implementation of Chinese Remainder Theorem.
6767

6868
### 14.sh
69-
1. Switch case similar to day 4 and 8. Use eval to avoid parsing the number in mem\[x\].
69+
1. Switch case similar to day 4 and 8. Use printf -v to avoid parsing the number in mem\[x\].
7070
2. Use IFS and read to simplify handling.
7171
Recursive function that converts the first X to 1 and 0, and calls itself twice until no X remains. Then do the thing.
7272

@@ -113,13 +113,13 @@ Description of what I'm doing. Contains spoilers....
113113

114114
### 23.sh
115115
1. String manipulation. Lots of trial and error to get it right.
116-
2. Not suitable for bash. Done in awk, but still takes about half a minute
116+
2. Just over 20 minutes in bash. Awk is more than 60 times faster.
117117

118118
### 24.sh
119119
1. Use an X-Y grid with only diagonals. One step can be (±2,0) or (±1,±1). Modify input so that standalone e/w are doubled, and count the total movement.
120120
2. Hex game of life. Use the same index trick as day 17. Access the grid manually, since a loop would be a PITA.
121121

122122
### 25.sh
123-
1. A couple of while loops. Yay.
123+
1. While loop for 15M rounds. Yay. Awk finishes in seconds.
124124
2. I need to complete 20-2 to get this one. Nope.
125125

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy