We can start by eliminating $C$ from your equations, as it's trivial to solve for once you have the value of $V$$v$. I'm also going to simplify some of your variable names:
$$ c \sum_i \left|a_i - Vb_i\right| + V = \sum_i a_i $$$$ c \sum_i \left|a_i - vb_i\right| + v = \sum_i a_i $$
Defining $S_i$$s_i$ as:
$$ S_i = \left\{ \begin{array}{rl} 1 & : a_i - Vb_i > 0 \\ -1 & : a_i - Vb_i < 0 \end{array} \right. $$$$ s_i = \left\{ \begin{array}{rl} 1 & : a_i - vb_i > 0 \\ -1 & : a_i - vb_i < 0 \end{array} \right. $$
$$ c \sum_i S_i\left(a_i - Vb_i\right) + V = \sum_i a_i $$$$ c \sum_i s_i\left(a_i - vb_i\right) + v = \sum_i a_i \\ f(v) = c \sum_i s_i a_i - \sum_i a_i - v \left(c \sum_i s_i b_i - 1\right) = 0 $$
Now if we know the $s_i$, we can solve for $v$ explicitly:
$$ v = \frac{c \sum_i s_i a_i - \sum_i a_i}{c \sum_i s_i b_i - 1} = \frac{\sum_i \left(c s_i - 1\right) a_i}{\sum_i cs_i b_i - 1} $$
This matches your solution, with the $s_i$ negated.
We can rewrite $S_i$$s_i$ to make something a little more clear:
$$ \begin{align} S_i &= \left\{ \begin{array}{rl} 1 & : Vb_i < a_i \\ -1 & : Vb_i > a_i \end{array} \right. \\ &= \left\{ \begin{array}{rl} 1 & : V < a_i/b_i \oplus b_i < 0 \\ -1 & : V > a_i/b_i \oplus b_i < 0 \end{array} \right. \end{align} $$$$ \begin{align} s_i &= \left\{ \begin{array}{rl} 1 & : vb_i < a_i \\ -1 & : vb_i > a_i \end{array} \right. \\ &= \left\{ \begin{array}{rl} 1 & : v < a_i/b_i \oplus b_i < 0 \\ -1 & : v > a_i/b_i \oplus b_i < 0 \end{array} \right. \end{align} $$
We can now see that we don't have to try every combination of the $S_i$$s_i$, since they will change one by one as $V$$v$ increases. We can now write a simple $\text{O}(n)$ algorithm to find the solution.
Next, we calculate the breakpoints for Vv
(where the S[[i]]s[[i]]
change sign):
Vbvb = {-Infinity} ~Join~ (a/b) ~Join~ {+Infinity}
Now we loop over each interval in Vbvb
. For each one we will compute Ss
and solve for Vv
:
m = Length[a]
Vi
vi = Table[
V /. First @ Solve[
With[{s c= (2Sign[b] Boole[
Join @@ ConstantArray @@@ Thread[Thread[i{{-1, <i}, Range[m]]{1, ~Xor~m Thread[b- <i}} 0]]},
](c s - 1).(a - V/(c s.b) + V == Total[a],
- V1)
],
{i, 0, m}
]
Now we select only the Vivi
that are in the proper range:
Pick[Visolns = Pick[vi, Thread[Most[Vb]Thread[Most[vb] <= Vivi <= Rest[Vb]]]Rest[vb]]]
If you have very large a
and b
lists, there is a trick that you can use to speed up finding the solutions. Note that the equation we are solving$f(v)=0$ is piecewise linear and piecewise continuous. When $V=-\infty$$v=-\infty$, $S_i$$s_i$ is just $\text{sign}(b_i)$. This means that all of the linear terms $S_i(-V b_i)$$-v s_i b_i)$ are negative.
As $V$$v$ increases, the signs flip one by one until they are all positive. This means that the slope is always nondecreasing; therefore the function$f(v)$ is concave up (unless $c$ is negative, in which case it is concave down), and there can be only two solutions. (There is a special case where one segment of the function is colinear with the x-axis, but when a
and b
are real numbers the probability of this is zero, i.e. it is almost certain not to happen.) You can do a modified binary search to quickly find the two by looking at the sign of the residual to figure out where in the function you are.