What is the most efficient way to compute v.Reverse[v]
or ListConvolve[v,v]
? In particular, since most of the products occur twice, could there be a 2x speed improvement? My attempt:
f1[v_] := v . Reverse[v] f2[v_] := First@ListConvolve[v, v] f3[v_] := f3i[v, Length[v]] f3i[v_, s_?EvenQ] := With[{hs = s/2}, (2 Take[v, hs]) . Reverse[Take[v, -hs]]] f3i[v_, s_?OddQ] := f3i[v, s - 1] + v[[(s + 1)/2]]^2 f4[v_] := f4i[v, Length[v]] f4i[v_, s_?EvenQ] := With[{hs = s/2}, First@ListConvolve[2 Take[v, hs], Take[v, -hs]]] f4i[v_, s_?OddQ] := f4i[v, s - 1] + v[[(s + 1)/2]]^2
First, a sanity check:
v = {{}, {a}, {a, b}, {a, b, c}, {a, b, c, d}}; {f1[#], f1[#] === f2[#] === f3[#] === f4[#]} & /@ v
{{0, True}, {a^2, True}, {2 a b, True}, {b^2 + 2 a c, True}, {2 b c + 2 a d, True}}
Timing on symbols:
v = Table[StringForm["a``", i], {i, 5000000}]; First@RepeatedTiming[f1[v]] First@RepeatedTiming[f2[v]] First@RepeatedTiming[f3[v]] First@RepeatedTiming[f4[v]]
2.16298 $\quad$ (plain
v.Reverse[v]
)2.48454 $\quad$ (plain
ListConvolve[v,v]
)1.56501 $\quad$ (modified
v.Reverse[v]
)1.43643 $\quad$ (modified
ListConvolve[v,v]
)
Not a 2x improvement, but an improvement nonetheless! Notice how plain v.Reverse[v]
beats ListConvolve[v,v]
, but the opposite is true with the modified versions. Why?
Timing on integers:
v = Table[RandomInteger[100000], 5000000]; First@RepeatedTiming[f1[v]] First@RepeatedTiming[f2[v]] First@RepeatedTiming[f3[v]] First@RepeatedTiming[f4[v]]
0.00252993 $\quad$ (plain
v.Reverse[v]
)0.563211 $\quad$ (plain
ListConvolve[v,v]
)0.00335379 $\quad$ (modified
v.Reverse[v]
)0.287567 $\quad$ (modified
ListConvolve[v,v]
)
With integers, v.Reverse[v]
dramatically beats ListConvolve[v,v]
, but trying to improve v.Reverse[v]
just makes it worse; on the other hand, the sought 2x improvement on ListConvolve[v,v]
seems achievable in this case.
Timing on reals:
v = Table[RandomReal[], 5000000]; First@RepeatedTiming[f1[v]] First@RepeatedTiming[f2[v]] First@RepeatedTiming[f3[v]] First@RepeatedTiming[f4[v]]
0.00255105 $\quad$ (plain
v.Reverse[v]
)0.0221051 $\quad$ (plain
ListConvolve[v,v]
)0.00357253 $\quad$ (modified
v.Reverse[v]
)0.0133805 $\quad$ (modified
ListConvolve[v,v]
)
We see v.Reverse[v]
speed is the same for integers and reals (and again the attempt at improving it just makes it worse), but ListConvolve[v,v]
is much faster with reals than integers, and can again be improved, but that improvement is no longer 2x.
Question/Challenge: What's happening under the hood to explain all this? Can you construct a variant of v.Reverse[v]
that's twice as fast on integers and reals?
f4i
withWith[{hs = s/2}, 2 First@ListConvolve[Take[v, hs], Take[v, -hs]]]
. Similarly forf3i
.$\endgroup$