Algorithm 最大化序列中数字之间的差异
我需要一些帮助来找到解决以下问题的算法的总体思路。这个问题已在作业中交给我了。看起来它应该可以通过贪婪的方法来解决,但我无法找到一个简单的解决方案。以下是问题描述: 您将获得一系列N数字Algorithm 最大化序列中数字之间的差异,algorithm,language-agnostic,recurrence,Algorithm,Language Agnostic,Recurrence,我需要一些帮助来找到解决以下问题的算法的总体思路。这个问题已在作业中交给我了。看起来它应该可以通过贪婪的方法来解决,但我无法找到一个简单的解决方案。以下是问题描述: 您将获得一系列N数字a\u 1。。。a_n使得0=a_1
a\u 1。。。a_n
使得0=a_1
。您必须消除这些数字中最多M,以使任意两个连续数字之间的最小差值a_i+1-a_i
最大化
不能删除第一个和最后一个元素,a\u 0
和a\u n
。此外,您必须消除尽可能少的数字:如果消除M-1
您得到的最短距离为D
,而消除M
您仍然有相同的最小差值,则不得消除最后一个数字
我不是在要求这个问题的完整解决方案。关于算法的外观,只有一些指导原则
编辑:一些测试样本。请记住,可能存在多个有效的解决方案
Remove at most 7 from:
0 3 7 10 15 18 26 31 38 44 53 60 61 73 76 80 81 88 93 100
Solution:
0 7 15 26 31 38 44 53 60 73 80 88 93 100
使用动态规划 线索X(i,j)包含与前i个元素的最小距离,其中j已选定(即未删除) 这将为您提供精确的解决方案。复杂性=O(MN^2),因为对于每个i值,只有M个有效的j值,并且每个函数调用都需要进行O(M)工作 设元素为A1、A2、…、An 更新公式为:
X(i+1,j+1)=Max(Min(A(i+1)-Ak,Xkj)对于k我想我得到了解决方案。它至少在两个样本集上有效。它不一定返回与答案相同的集合,但它返回的集合具有相同的最小值。它也是迭代和贪婪的,这很好,有很多方法可以优化它。看起来它是MLog(N) 重要的是要意识到数字并不重要——只有它们之间的差异才重要。当你“删除一个数字”,您实际上只是将两个相邻的差异组合起来。我的算法将重点关注这些差异。返回导致这些差异的项目并在执行时删除是一件简单的事情 算法
注意:根据组合相同X值的想法进行编辑,以避免这样做,SauceMaster提出的1、29、30、31、59案例。[编辑:我最初声称这是错误的,但现在我相信这不仅是正确的,而且与我的(后来的)案例几乎相同回答:-P对我的口味来说还是有点简洁!] 这里有一个精确的O(NM^2)-时间,O(NM)-空间算法,它可以在毫秒内获得所有OP示例的正确答案。其基本思想是:
Remove at most 8 from:
0 3 7 10 15 26 38 44 53 61 76 80 88 93 100
Solution:
0 15 38 53 76 88 100
f(i, j) = max(g(i, j, d)) over all 0 <= d <= min(j, N-i-2)
g(i, j, d) = min(x[i+d+1] - x[i], f(i+d+1, j-d))
f(N-1, 0) = infinity (this has the effect of making min(f(N-1), 0), z) = z)
f(N-1, j > 0) = 0 (this case only arises if M > N - 2)
Problem: M = 1, X = [10 15 50 55].
f(2, 0) = (5, 0) (leaving [50 55])
f(1, 1) = (40, 1) (delete 50 to leave [15 55]); *locally* this appears better
than not deleting anything, which would leave [15 50 55] and yield
a min-gap of 5, even though the latter would be a better choice for
the overall problem)
f(0, 1) = max(min(5, f(1, 1)), min(40, f(2, 0))
= max(min(5, 40), min(40, 5))
= (5, 1) (leaving either [10 15 55] or [10 50 55])
*Main> compareAllCombos 7 4 4
Nothing
1. Group the differences: [0, 6, 11, 13, 22] => [[6],[5],[2],[9]]
2. While enough removals remain to increase the minimum difference, extend the
minimum difference to join adjacent groups in all possible ways:
[[6],[5],[2],[9]] => [[6],[5,2],[9]] and [[6],[5],[2,9]]...etc.
Choose the highest minimum difference and lowest number of removals.
import Data.List (minimumBy, maximumBy, groupBy, find)
import Data.Maybe (fromJust)
extendr ind xs =
let splitxs = splitAt ind xs
(y:ys) = snd splitxs
left = snd y
right = snd (head ys)
in fst splitxs ++ [(sum (left ++ right), left ++ right)] ++ tail ys
extendl ind xs =
let splitxs = splitAt ind xs
(y:ys) = snd splitxs
right = snd y
left = snd (last $ fst splitxs)
in init (fst splitxs) ++ [(sum (left ++ right), left ++ right)] ++ tail (snd splitxs)
extend' m xs =
let results = map (\x -> (fst . minimumBy (\a b -> compare (fst a) (fst b)) $ x, x)) (solve xs)
maxMinDiff = fst . maximumBy (\a b -> compare (fst a) (fst b)) $ results
resultsFiltered = filter ((==maxMinDiff) . fst) results
in minimumBy (\a b -> compare (sum (map (\x -> length (snd x) - 1) (snd a))) (sum (map (\x -> length (snd x) - 1) (snd b)))) resultsFiltered
where
solve ys =
let removalCount = sum (map (\x -> length (snd x) - 1) ys)
lowestElem = minimumBy (\a b -> compare (fst a) (fst b)) ys
lowestSum = fst lowestElem
lowestSumGrouped =
map (\x -> if (fst . head $ x) == 0
then length x
else if null (drop 1 x)
then 1
else if odd (length x)
then div (length x + 1) 2
else div (length x) 2)
$ filter ((==lowestSum) . fst . head) (groupBy (\a b -> (fst a) == (fst b)) ys)
nextIndices = map snd . filter ((==lowestSum) . fst . fst) $ zip ys [0..]
lastInd = length ys - 1
in if sum lowestSumGrouped > m - removalCount || null (drop 1 ys)
then [ys]
else do
nextInd <- nextIndices
if nextInd == 0
then solve (extendl (nextInd + 1) ys)
else if nextInd == lastInd
then solve (extendr (nextInd - 1) ys)
else do
a <- [extendl nextInd ys, extendr nextInd ys]
solve a
pureMaxDiff m xs =
let differences = map (:[]) $ tail $ zipWith (-) xs ([0] ++ init xs)
differencesSummed = zip (map sum differences) differences
result = extend' m differencesSummed
lowestSum = fst result
removalCount = sum (map (\x -> length (snd x) - 1) (snd result))
in if null (filter ((/=0) . fst) differencesSummed)
then (0,0)
else (removalCount, lowestSum)
-- __j_random_hacker's stuff begins here
-- My algorithm from http://stackoverflow.com/a/15478409/47984.
-- Oddly it seems to be much faster when I *don't* try to use memoisation!
-- (I don't really understand how memoisation in Haskell works yet...)
jrhMaxDiff m xs = fst $ fromJust $ find (\(x, y) -> snd x > snd y) resultPairsDesc
where
inf = 1000000
n = length xs
f i j =
if i == n - 1
then if j == 0
then inf
else 0
else maximum [g i j d | d <- [0 .. min j (n - i - 2)]]
g i j d = min ((xs !! (i + d + 1)) - (xs !! i)) (f (i + d + 1) (j - d))
resultsDesc = map (\i -> (i, f 0 i)) $ reverse [0 .. m]
resultPairsDesc = zip resultsDesc (concat [(tail resultsDesc), [(-1, -1)]])
-- All following code is for looking for different results between my and groovy's algorithms.
-- Generate a list of all length-n lists containing numbers in the range 0 - d.
upto 0 _ = [[]]
upto n d = concat $ map (\x -> (map (\y -> (x : y)) (upto (n - 1) d))) [0 .. d]
-- Generate a list of all length-maxN or shorter lists containing numbers in the range 0 - maxD.
generateAllDiffCombos 1 maxD = [[x] | x <- [0 .. maxD]]
generateAllDiffCombos maxN maxD =
(generateAllDiffCombos (maxN - 1) maxD) ++ (upto maxN maxD)
diffsToNums xs = scanl (+) 0 xs
generateAllCombos maxN maxD = map diffsToNums $ generateAllDiffCombos maxN maxD
-- generateAllCombos causes pureMaxDiff to produce an error with (1, [0, 0]) and (1, [0, 0, 0]) among others,
-- so filter these out to look for more "interesting" differences.
--generateMostCombos maxN maxD = filter (\x -> length x /= 2) $ generateAllCombos maxN maxD
generateMostCombos maxN maxD = filter (\x -> length x > 4) $ generateAllCombos maxN maxD
-- Try running both algorithms on every list of length up to maxN having gaps of
-- size up to maxD, allowing up to maxDel deletions (this is the M parameter).
compareAllCombos maxN maxD maxDel =
find (\(x, maxDel, jrh, grv) -> jrh /= grv) $ map (\x -> (x, maxDel, jrhMaxDiff maxDel x, pureMaxDiff maxDel x)) $ generateMostCombos maxN maxD
-- show $ map (\x -> (x, jrhMaxDiff maxDel x, pureMaxDiff maxDel x)) $ generateMostCombos maxN maxD