Haskell, 204 271 bytes
Edit: golfed much further by updating the convex hull as a list (but with the same complexity as the ungolfed version), using "split (x+1)" instead of "splitLookup x", and removing all qualified function calls like Predule.foldl.
This creates a function f which expects the list of (a,b) pairs and a list of x values.
I guess it will be blown away length-wise by anything in the APL family using the same ideas, but here it goes:
import Data.Map
r=fromListWith max
[]%v=[(0,v)]
i@((p,u):j)%v|p>v#u=j%v|0<1=(v#u,v):i
(a,b)#(c,d)=1+div(b-d)(c-a)
o i x=(\(a,b)->a*x+b)$snd$findMax$fst$split(x+1)$r$foldl'(%)[]$r$zip(fmap fst i)i
f=fmap.o
Sample usage:
[1 of 1] Compiling Main ( linear-min.hs, interpreted )
Ok, modules loaded: Main.
λ> f [(2,8), (4,0), (2,1), (1,10), (3,3), (0,4)] [1..5]
[11,12,14,16,20]
λ> f [(1,20), (2,12), (3,11), (4,8)] [1..5]
[21,22,23,24,28]
It works in O(n log n) time; see below for analysis.
Edit: Here's an ungolfed version with the big-O analysis and a description of how it all works:
import Prelude hiding (null, empty)
import Data.Map hiding (map, foldl)
-- Just for clarity:
type X = Int
type Y = Int
type Line = (Int,Int)
type Hull = Data.Map.Map X Line
slope (a,b) = a
{-- Take a list of pairs (a,b) representing lines a*x + b and sort by
ascending slope, dropping any lines which are parallel to but below
another line.
This composition is O(n log n); n for traversing the input and
the output, log n per item for dictionary inserts during construction.
The input and output are both lists of length <= n.
--}
sort :: [Line] -> [Line]
sort = toList . fromListWith max
{-- For lines ax+b, a'x+b' with a < a', find the first value of x
at which a'x + b' exceeds ax + b. --}
breakEven :: Line -> Line -> X
breakEven p@(a,b) q@(a',b') = if slope p < slope q
then 1 + ((b - b') `div` (a' - a))
else error "unexpected ordering"
{-- Update the convex hull with a line whose slope is greater
than any other lines in the hull. Drop line segments
from the hull until the new line intersects the final segment.
split is used to find the portion of the convex hull
to the right of some x value; it has complexity O(log n).
insert is also O(log n), so one invocation of this
function has an O(log n) cost.
updateHull is recursive, but see analysis for hull to
account for all updateHull calls during one execution.
--}
updateHull :: Line -> Hull -> Hull
updateHull line hull
| null hull = singleton 0 line
| slope line <= slope lastLine = error "Hull must be updated with lines of increasing slope"
| hull == hull' = insert x line hull
| otherwise = updateHull line hull''
where (lastBkpt, lastLine) = findMax hull
x = breakEven lastLine line
hull' = fst $ x `split` hull
hull'' = fst $ lastBkpt `split` hull
{-- Build the convex hull by adding lines one at a time,
ordered by increasing slope.
Each call to updateHull has an immediate cost of O(log n),
and either adds or removes a segment from the hull. No
segment is added more than once, so the total cost is
O(n log n).
--}
hull :: [Line] -> Hull
hull = foldl (flip updateHull) empty . sort
{-- Find the highest line for the given x value by looking up the nearest
(breakpoint, line) pair with breakpoint <= x. This uses the neat
function splitLookup which looks up a key k in a dictionary and returns
a triple of:
- The subdictionary with keys < k.
- Just v if (k -> v) is in the dictionary, or Nothing otherwise
- The subdictionary with keys > k.
O(log n) for dictionary lookup.
--}
valueOn :: Hull -> X -> Y
valueOn boundary x = a*x + b
where (a,b) = case splitLookup x boundary of
(_ , Just ab, _) -> ab
(lhs, _, _) -> snd $ findMax lhs
{-- Solve the problem!
O(n log n) since it maps an O(log n) function over a list of size O(n).
Computation of the function to map is also O(n log n) due to the use
of hull.
--}
solve :: [Line] -> [X] -> [Y]
solve lines = map (valueOn $ hull lines)
-- Test case from the original problem
test = [(2,8), (4,0), (2,1), (1,10), (3,3), (0,4)] :: [Line]
verify = solve test [1..5] == [11,12,14,16,20]
-- Test case from comment
test' = [(1,20),(2,12),(3,11),(4,8)] :: [Line]
verify' = solve test' [1..5] == [21,22,23,24,28]
It looks to me like the expected results should be [11 12 12 15 4]. ??? – Bob Jarvis - Reinstate Monica – 2015-03-08T00:14:06.143
@BobJarvis It's the maximum of ax+b for the corresponding x, but for all (a,b) in the list. Changed to make the example less misleading. – jimmy23013 – 2015-03-08T00:17:39.787
total input length = length of (a,b) pairs plus length of array of x ? – Optimizer – 2015-03-08T06:39:09.257
@Optimizer Right. – jimmy23013 – 2015-03-08T06:50:53.887
Why is it clear that it is even possible in
O(n log(n))
? Can you provide a reference algorithm? – flawr – 2015-03-08T19:43:44.247And how do we have to expect the numbers to be distributed? – flawr – 2015-03-08T19:56:07.767
@flawr Find the part of the convex hull of the points (a,b), from the topmost to rightmost point, and do binary search on the slope (or find the maximum for unimodal function) for each x. – jimmy23013 – 2015-03-08T20:07:22.303
@flawr You can assume each number is in a basic integer type. The "expected complexity" is for the randomness in your program, and it cannot depend on the input distribution. – jimmy23013 – 2015-03-08T20:13:13.283
Can we assume that X is sorted on input? – Keith Randall – 2015-03-08T20:27:00.857
@KeithRandall No. – jimmy23013 – 2015-03-08T20:27:29.600
@user23013 The asympotic runtime of many algorithms depends on assumptions on the input distributions, thats why I am asking. – flawr – 2015-03-08T20:35:30.637
@flawr It's basically just the worst-case complexity allowing the randomized quicksort (and things likely). – jimmy23013 – 2015-03-08T20:41:33.690
@user23013 Then please change that in the question, because you wrote the expected complexity should be
O(n log(n))
=) – flawr – 2015-03-08T20:43:05.987@flawr What I get from this page is that "average-case complexity" is usually for the average input, and "expected complexity" is for the randomness. If I misstood that, I'm not sure what term to use then.
– jimmy23013 – 2015-03-08T20:53:26.297Well, seeing the history, that line is just edited to the Wikipedia page recently. Edited and tried to not mention that. And I'm a bit confused... – jimmy23013 – 2015-03-08T21:08:58.790
You want expected complexity. Expected over the random choices of the algorithm, not over any input distribution. – Keith Randall – 2015-03-08T21:13:00.507
@KeithRandall Rollbacked the edit... – jimmy23013 – 2015-03-08T21:15:28.803