求ax + b的最大值


14

您将获得(a,b)列表和x列表。计算每个x的最大ax + b。您可以假设abx为非负整数。

您的程序或函数必须在预期的时间内运行(如果您的代码涉及到该随机性,而不是输入,则为随机性)O(n log n)时间,其中n为总输入长度(两个列表的总和或最大值)。

这是代码高尔夫球。最短的代码获胜。

[[2 8] [4 0] [2 1] [1 10] [3 3] [0 4]] [1 2 3 4 5]

输出:

[11 12 14 16 20]

说明:

11 = 1*1 + 10
12 = 1*2 + 10 = 2*2 + 8
14 = 2*3 + 8
16 = 2*4 + 8 = 4*4 + 0
20 = 4*5 + 0

注意复杂性:

如果您使用具有良好平均大小复杂度的内置函数,并且可以将其随机化以从理论上轻松获得预期的复杂度,则可以假设您的语言做到了这一点。

这意味着,如果您的程序可以通过O(n log n)进行测试,可能是由于语言的实现而导致边缘情况异常,但是在您自己的代码中不能以逻辑方式看到,则我们将其称为O(n log n)。


在我看来,预期结果应该是[11 12 12 15 4]。???
鲍勃·贾维斯

@BobJarvis对于相应的x,但对于列表中的所有(a,b),这都是ax + b的最大值。进行了更改,以使示例减少误导。
jimmy23013 2015年

总输入长度=(a,b)对的长度加上x?数组的长度
优化器

@Optimizer对。
jimmy23013 2015年

为什么说它甚至有可能成为O(n log(n))现实?可以提供参考算法吗?
瑕疵的

Answers:


1

Pyth - 99 98字节

这几乎是@KeithRandall的Python答案的直接翻译。绝对可以打更多的高尔夫球。我将尽快发布解释

K[_1Z;FNShQAkdNW&>K2>+*k@K_3d+*@K_2@K_3eK=K<K_3)~K[c-eKd-k@K_2kd;FNSeQW&>K2>N@K2=K>K3)aY+*hKN@K1;Y

接受两个逗号分隔的列表,通过标准输入以逗号分隔。

在这里尝试

K                  K=
 [  )              A List containing
  _1               Negative 1
  Z                Zero
FN                 For N in
 ShQ               Sorted first input
Akd                Double assign k and d
 N                 To N
 W                 While
  &                Logical And
   >K2             K>2
   >               Greater Than
    +*k@K_3d       K[-3]*k+d
    +              Plus
     *@K_2@K_3     K[-2]*K[-3]
     eK            K[-1]
  =K               K=
   <K_3            K[:-3]
  )                Close while loop
 ~K                K+=
  [      )         List constructor
   c               Float division
    -              Minus
     eK            K[-1]
     d             d
    -              Minus
     k             k
     @K_2          K[-2]
   k               k
   d               d
 ;                 End list and for loop
FN                 For N in
  SeQ              Sorted second input
  W                While loop
   &               Logical and
    >K2            K[2:]
    >              Greater than
     N             N
     @K2           K[2]
   =K              K=
   >K3             K[3:]
  )                Close while loop
  aY               Y.append - Y is empty list
   +               Plus
    *hKN           (K+1)*N
    @K1            K[1]
;                  Close out everything
Y                  Print Y

10

Python,214个字节

S=sorted
def M(L,X):
 H=[-1,0];R={}
 for a,b in S(L):
    while H[2:]and a*H[-3]+b>H[-2]*H[-3]+H[-1]:H=H[:-3]
    H+=[1.*(H[-1]-b)/(a-H[-2]),a,b]
 for x in S(X):
    while H[2:]and x>H[2]:H=H[3:]
    R[x]=H[0]*x+H[1]
 return R

通过a,b以递增a顺序遍历输入来计算凸包。凸包被记录在H使用格式-1,0,x1,a1,b1,x2,a2,b2,x2,...,xn,an,bn其中xi是的交点的x坐标a{i-1},b{i-1}ai,bi

然后,我按输入x顺序对输入进行了迭代,将凸包截断以跟上。

除了O(n lgn)之外,其他所有东西都是线性的。

像这样运行:

>>> print M([[2,8],[4,0],[2,1],[1,10],[3,3],[0,4]], [1,2,3,4,5])
{1: 11, 2: 12, 3: 14, 4: 16, 5: 20}

@ user23013:已修复
Keith Randall

@KeithRandall在最后一步中,您将H线性搜索每个xin X,不是吗?当两个列表的长度相同时,难道我们就没有O(n ^ 2)的复杂性吗?
coredump

1
@coredump:我H线性搜索每个x,但是因为我按xs的顺序进行,所以我记得上一次搜索在哪里停止,然后在那开始下一个搜索。因此,内while循环可以在所有循环上最多执行O(n)次x(即使它可能对任何个人都执行O(n)次x)。
基思·兰德尔2015年

@coredump:请注意,while在第一个for循环中,内部循环也会发生相同的情况。
基思·兰德尔2015年

@KeithRandall我错过了,谢谢。聪明!
coredump

6

Haskell,204271字节

编辑:通过将凸包更新为列表(但与非高尔夫版本具有相同的复杂性),使用“ split(x + 1)”而不是“ splitLookup x”,并删除所有像Predule这样的合格函数调用,可以进一步打高尔夫球。折叠

这将创建一个函数f,该函数期望一个(a,b)对的列表和一个x值的列表。我想,使用相同的想法,APL系列中的任何东西都将在长度方向上将其吹灭,但实际上是这样:

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

用法示例:

[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]

它的工作时间为O(n log n);请参阅下面的分析。

编辑:这是一个带有big-O分析的简单版本,并对其全部工作方式进行了描述:

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]

2

普通Lisp-648 692

使用实际的二进制搜索。

(use-package :optima)(defun z(l e)(labels((i(n m)(/(-(cadr m)(cadr n))(-(car n)(car m))))(m(l)(match l((list* a b c r)(if(<(i a b)(i b c))(list* a(m(list* b c r)))(m(list* a c r))))(_ l)))(f(x &aux(x(cdr x)))`(+(*,(car x)x),(cadr x)))(g(s e)(let*((q(- e s))(h(+ s(floor q 2)))d)(if(> q 1)(let((v(g s h))(d(pop l)))`(if(< x,(car d)),v,(g(1+ h)e)))(cond((not(car (setq d (pop l))))(f d))((> q 0)`(if(< x,(car d)),(f d),(f(pop l))))(t(f d)))))))(setq l(loop for(a b)on(m(remove-duplicates(#3=stable-sort(#3# l'< :key'cadr)'< :key'car):key 'car)) by #'cdr collect`(,(when b(i a b)),(car a),(cadr a))))`(mapcar(eval(lambda(x),(g 0(1-(length l)))))',e)))

(z '((2 8) (4 0) (2 1) (1 10) (3 3) (0 4)) '(1 2 3 4 5))
=> (11 12 14 16 20)

说明

n为(a,b)的长度,k为点的长度。

  • 用a排序(a,b),然后b- O(n.ln(n))
  • 删除重复项a(平行线)的条目,仅保留最大的平行线,该平行线b始终大于另一条平行线(在计算交集时,我们将其除以零)-O(n)
  • 压缩结果-O(n):当排序列表中有连续元素(a0,b0)(a1,b1)(a2,b2)时,使得(a0,b0)与(a1,b1 )大于(a1,b1)和(a2,b2)中的一个,则可以安全地忽略(a1,b1)。
  • 建立一个(xab)元素的列表,其中x是直到x的最大值ax + b对于x而言是最大值(由于先前的步骤,此列表按x排序)-O(n)
  • 给定该列表,构建一个lambda对其输入进行间隔检查并计算最大值-二叉树内置于O(n)中(请参阅/programming//a/4309901/124319)。将要应用的二进制搜索具有O(ln(n))复杂度。使用示例输入,我们构建以下函数(然后编译该函数):

    (LAMBDA (X)
      (IF (< X 4)
          (IF (< X 2)
              (IF (< X -6)
                  (+ (* 0 X) 4)
                  (+ (* 1 X) 10))
              (+ (* 2 X) 8))
          (+ (* 4 X) 0)))
    
  • 对所有元素应用该函数-O(k.ln(n))

产生的复杂度:在最坏的情况下为O((n + k)(ln n)))

由于kn是独立的,因此我们无法提供输入总数(n + k)的复杂度估算。例如,如果nk的渐近可忽略,则总复杂度将为O(k)

但是,如果我们假设k = O(n),则结果复杂度为O(n.ln(n))

其他例子

(z '((1 10) (1 8) (1 7)) '(1 2 3 4 5))
=> (11 12 13 14 15)

而且,如果我们移回引号以查看正在计算的内容,则可以看到我们甚至不需要进行任何比较(一旦对第一个列表进行了预处理):

(MAPCAR (LAMBDA (X) (+ (* 1 X) 10)) '(1 2 3 4 5))

这是另一个示例(摘自评论):

(z '((1 20) (2 12) (3 11) (4 8)) '(1 2 3 4 5))
=> (21 22 23 24 28)

有效功能:

(MAPCAR
  (LAMBDA (X)
    (IF (< X 4)
        (+ (* 1 X) 20)
        (+ (* 4 X) 8)))
  '(1 2 3 4 5))

O(n log n + k)当然 O((n + k)log(n + k))。
jimmy23013 2015年

您正在使用哪个口译员?伊迪恩给予(LIST* A B C R) should be a lambda expression
jimmy23013 2015年

@ user23013对不起,我忘了(use-package :optima) (编辑...)
coredump

@ user23013恐怕我无法让Ideone加载外部库。为了进行测试,请下载SBCL(或者可能是另一个,尽管我仅在SBCL上进行了测试)并安装quicklisp。然后,您可以(ql:quickload:optima)下载并安装optima。最后,我提供的代码应该是可评估的。
coredump

它返回(MAPCAR (EVAL (LAMBDA (X) ...,评估为答案。您是否在其中留下了一些调试代码?
jimmy23013 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.