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?
f4iwithWith[{hs = s/2}, 2 First@ListConvolve[Take[v, hs], Take[v, -hs]]]. Similarly forf3i. $\endgroup$2increases performance! On symbolic expressions this introduces the typically insignificant distinction2(bc+ad)versus2bc+2ad, while on numeric expressions a possible change in roundoff doesn't matter because the various OP versions anyhow probably already disagreed in that regard. $\endgroup$