1
1
use std:: { fs, iter:: zip, num:: ParseIntError , path:: PathBuf , str:: FromStr } ;
2
2
3
3
use itertools:: Itertools ;
4
+ use rayon:: prelude:: * ;
4
5
5
6
pub fn day07 ( mut input_path : PathBuf ) {
6
7
input_path. push ( "07.txt" ) ;
@@ -35,8 +36,8 @@ fn part1(equations: &[Equation]) -> u64 {
35
36
36
37
fn part2 ( equations : & [ Equation ] ) -> u64 {
37
38
equations
38
- . iter ( )
39
- . filter ( |e| can_be_true ( e, vec ! [ Operator :: Add , Operator :: Multiply , Operator :: Concat ] ) )
39
+ . into_par_iter ( ) // this gives speedup of x1.2
40
+ . filter ( |e| can_be_true_par ( e, vec ! [ Operator :: Add , Operator :: Multiply , Operator :: Concat ] ) )
40
41
. map ( |e| e. result )
41
42
. sum ( )
42
43
}
@@ -60,27 +61,35 @@ fn is_true(equation: &Equation, operators: &[Operator]) -> bool {
60
61
// we checked during parsing
61
62
panic ! ( "RHS needs at least one term" ) ;
62
63
} ;
63
-
64
- equation. result == zip ( operators, tail) . fold ( * init, |acc, ( & op, & t) | eval ( acc, op, t) )
65
-
66
64
// Early stopping the fold when we exceed result is surprisingly not appreciably faster.
67
65
// this try_fold is similar to (sum . filter ((>) result) . (scan foldop))
68
66
69
- // zip(operators, tail)
70
- // .try_fold(*init, |acc, (&op, &t)| {
71
- // let s = eval(acc, op, t);
72
- // if s > equation.result {
73
- // return Err(s);
74
- // }
75
- // Ok(s)
76
- // })
77
- // .is_ok_and(|lhs| lhs == equation.result)
67
+ zip ( operators, tail)
68
+ . try_fold ( * init, |acc, ( & op, & t) | {
69
+ let s = eval ( acc, op, t) ;
70
+ if s > equation. result {
71
+ return Err ( s) ;
72
+ }
73
+ Ok ( s)
74
+ } )
75
+ . is_ok_and ( |lhs| lhs == equation. result )
78
76
}
79
77
80
78
fn can_be_true ( equation : & Equation , options : Vec < Operator > ) -> bool {
81
79
operator_combinations_for ( & equation, options) . any ( |ops| is_true ( & equation, & ops) )
82
80
}
83
81
82
+ fn can_be_true_par ( equation : & Equation , options : Vec < Operator > ) -> bool {
83
+ // because Combinations has mutable state, we cannot straightforwardly parallelize,
84
+ // this is not as well encapsulated by has a speedup of x2.5 (0.8 vs 0.3s).
85
+ // still, we only parallize per equation, could parallelize all
86
+ let n_ops = ( equation. terms . len ( ) - 1 ) as u32 ;
87
+ ( 0 ..options. len ( ) . pow ( n_ops) )
88
+ . into_par_iter ( )
89
+ . filter_map ( |n| combinations_for ( n, n_ops, & options) )
90
+ . any ( |ops| is_true ( & equation, & ops) )
91
+ }
92
+
84
93
fn operator_combinations_for (
85
94
equation : & Equation ,
86
95
options : Vec < Operator > ,
@@ -89,7 +98,7 @@ fn operator_combinations_for(
89
98
}
90
99
91
100
struct Combinations < T > {
92
- // TODO: Don't need to own that
101
+ // TODO: Don't need to own that, but would introduce lifetime woes (probably)
93
102
options : Vec < T > ,
94
103
length : u32 ,
95
104
current : usize ,
@@ -109,19 +118,26 @@ impl<T: Copy> Iterator for Combinations<T> {
109
118
type Item = Vec < T > ;
110
119
111
120
fn next ( & mut self ) -> Option < Self :: Item > {
112
- let k = self . options . len ( ) ;
113
121
self . current += 1 ;
114
- if self . current > k. pow ( self . length ) {
115
- return None ;
116
- }
117
- let mut v: Vec < T > = Vec :: with_capacity ( self . length as usize ) ;
118
- let n = self . current - 1 ;
119
- for i in 0 ..self . length {
120
- // part 1 was easy, we could map bits to operation: n >> i & 1
121
- v. push ( self . options [ ( n / k. pow ( i) ) % k] ) ;
122
- }
123
- Some ( v)
122
+ combinations_for ( self . current - 1 , self . length , & self . options )
123
+ }
124
+ }
125
+
126
+ fn combinations_for < T > ( n : usize , len : u32 , opts : & [ T ] ) -> Option < Vec < T > >
127
+ where
128
+ T : Copy ,
129
+ {
130
+ let k = opts. len ( ) ;
131
+ if n >= k. pow ( len) {
132
+ return None ;
133
+ } ;
134
+
135
+ let mut v: Vec < T > = Vec :: with_capacity ( len as usize ) ;
136
+ for i in 0 ..len {
137
+ // part 1 was easy, we could map bits to operation: n >> i & 1
138
+ v. push ( opts[ ( n / k. pow ( i) ) % k] ) ;
124
139
}
140
+ Some ( v)
125
141
}
126
142
127
143
#[ derive( Debug ) ]
0 commit comments