ax + bの最大値を見つける


14

a、b)のリストとxのリストが与えられます。各xの最大ax + bを計算します。ab、およびxが非負の整数であると仮定できます。

(あなたのコードがそれを必要とする場合は、入力、ランダムにではない)あなたのプログラムや機能が期待される中で実行する必要がありますO(Nログ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に対してax + bの最大値ですが、リスト内のすべての(a、b)に対してです。例の誤解を少なくするように変更されました。
-jimmy23013

合計入力長=(a、b)ペアの長さとxの配列の長さ?
オプティマイザー

@Optimizer正しい。
jimmy23013

でそれが可能であることはなぜ明らかO(n log(n))ですか?参照アルゴリズムを提供できますか?
-flawr

回答:


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

stdinを介してコンマで区切られた2つのコンマ区切りリストを取ります。

ここで試してみてください

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形式を使用Xがの交点の座標であるとします。-1,0,x1,a1,b1,x2,a2,b2,x2,...,xn,an,bnxia{i-1},b{i-1}ai,bi

次に、入力xsを並べ替えて繰り返し処理し、凸包を切り捨てて、進行に合わせて維持します。

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:修正済み
キースランドール

@KeithRandall最後のステップでは、のHxを線形に検索しXますよね。両方のリストが同じ長さである場合、O(n ^ 2)の複雑さがある可能性はありませんか?
コアダンプ

1
@coredump:H各を線形に検索しxますが、xsを順番に実行するため、最後の検索が停止した場所を覚えて、そこで次の検索を開始します。そのため、内側のwhileループは、すべてのユーザーで最大O(n)回x実行できます(個々のユーザーに対してO(n)回実行する場合もありますx)。
キースランドール

@coredump:while最初のforループの内側のループでも同じことが起こることに注意してください。
キースランドール

@KeithRandall私はそれを逃した、ありがとう。賢い!
コアダンプ

6

Haskell、204 271バイト

編集:凸包をリストとして更新し(ただし、非ゴルフバージョンと同じ複雑さ)、「splitLookup x」の代わりに「split(x + 1)」を使用し、Preduleなどのすべての修飾された関数呼び出しを削除します。 foldl。

これにより、(a、b)ペアのリストとx値のリストを期待する関数fが作成されます。同じアイデアを使用して、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

Common 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、b)をaでソートし、次に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はアップツーたライン値である斧を+ Bの最大であるX(このリストはでソートされ、Xの前のステップのおかげで)、 - O(N)
  • そのリストが与えられたら、その入力に対して間隔チェックを実行し、最大値を計算するラムダを構築します-バイナリツリーは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)の複雑さの推定値を提供することはできません。たとえば、nが漸近的に無視できるwrt kである場合、総複雑度は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ログN + k)は勿論である O((N + K)ログ(N + K))。
jimmy23013

どのインタープリターを使用していますか?イデオンは与える(LIST* A B C R) should be a lambda expression
jimmy23013

@ user23013申し訳ありませんが、(use-package :optima) (編集中...)を忘れました
コアダンプ

@ user23013 Ideoneに外部ライブラリをロードさせることはできません。テストのために、SBCL(または別の場合もありますが、私はSBCLでのみテストしました)をダウンロードし、quicklispをインストールしてください。その後、(ql:quickload:optima)をダウンロードしてインストールできますoptima。最後に、私が提供したコードは評価可能でなければなりません。
コアダンプ

返さ(MAPCAR (EVAL (LAMBDA (X) ...れた結果が答えになります。デバッグコードをそこに残しましたか?
jimmy23013
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.