Ruby 算法-最长摆动子序列
算法: 一个数字序列称为摆动序列,如果 在连续的数字之间,严格地在正数和正数之间交替 消极的第一个差异(如果存在)可能是正的 或者是否定的。少于两个元素的序列通常是一个 摆动序列 例如,[1,7,4,9,2,5]是一个摆动序列,因为 差异6,-3,5,-7,3交替为正和负。在里面 相反,[1,4,7,2,5]和[1,7,4,5,5]不是摆动序列 首先是因为它的前两个差异是正的,第二个是负的 因为它的最后一个差是零 给定一个整数序列,返回最长整数的长度 作为摆动序列的子序列。子序列通过以下方式获得: 最终删除一定数量的元素,并且从 原始序列,将其余元素保留在原始序列中 秩序 示例:Ruby 算法-最长摆动子序列,ruby,algorithm,recursion,Ruby,Algorithm,Recursion,算法: 一个数字序列称为摆动序列,如果 在连续的数字之间,严格地在正数和正数之间交替 消极的第一个差异(如果存在)可能是正的 或者是否定的。少于两个元素的序列通常是一个 摆动序列 例如,[1,7,4,9,2,5]是一个摆动序列,因为 差异6,-3,5,-7,3交替为正和负。在里面 相反,[1,4,7,2,5]和[1,7,4,5,5]不是摆动序列 首先是因为它的前两个差异是正的,第二个是负的 因为它的最后一个差是零 给定一个整数序列,返回最长整数的长度 作为摆动序列的子序列。子序列通过以下方式获得
Input: [1,7,4,9,2,5]
Output: 6
The entire sequence is a wiggle sequence.
Input: [1,17,5,10,13,15,10,5,16,8]
Output: 7
There are several subsequences that achieve this length. One is [1,17,10,13,10,16,8].
Input: [1,2,3,4,5,6,7,8,9]
Output: 2
我的解决方案:
def wiggle_max_length(nums)
[ build_seq(nums, 0, 0, true, -1.0/0.0),
build_seq(nums, 0, 0, false, 1.0/0.0)
].max
end
def build_seq(nums, index, len, wiggle_up, prev)
return len if index >= nums.length
if wiggle_up && nums[index] - prev > 0 || !wiggle_up && nums[index] - prev < 0
build_seq(nums, index + 1, len + 1, !wiggle_up, nums[index])
else
build_seq(nums, index + 1, len, wiggle_up, prev)
end
end
这适用于较小的输入,例如[1,1,1,3,2,4,1,6,3,10,8]和所有样本输入,但对于很难调试的非常大的输入,它失败了,例如:
3.3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 0 0 0 0 0 0 0 0 0 0 0 6 6 6 6 6 6 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 23,52,38,74,15]
应该有输出:67,但我的soln输出57。有人知道这里出了什么问题吗?尝试的方法是贪婪的解决方案,因为如果当前元素满足摆动条件,它总是使用当前元素,但这并不总是有效的。 我将尝试用一个更简单的反例来说明这一点:1100 99 6 7 4 5 2 3 一个最好的子序列是:1100 6 7 4 5 2 3,但来自该算法的两个build_seq调用将生成以下序列: 1 100 99 1.
编辑:稍微修改过的贪婪方法确实有效-请参阅,谢谢Peter de Rivaz。尝试过的方法是贪婪解决方案,因为如果满足摆动条件,它总是使用当前元素,但这并不总是有效的。 我将尝试用一个更简单的反例来说明这一点:1100 99 6 7 4 5 2 3 一个最好的子序列是:1100 6 7 4 5 2 3,但来自该算法的两个build_seq调用将生成以下序列: 1 100 99 1.
编辑:稍微修改的贪婪方法确实有效-请参见,谢谢Peter de Rivaz。让Wp[i]成为从元素i开始的最长摆动序列,其中第一个差为正。设Wn[i]相同,但第一个差为负 然后: 这给出了一个关于^2的动态规划解决方案,这里是伪代码
Wp = [1, 1, ..., 1] -- length n
Wn = [1, 1, ..., 1] -- length n
for k = n-1, n-2, ..., 0
for k' = k+1, k+2, ..., n-1
if A[k'] > A[k]
Wp[k] = max(Wp[k], Wn[k']+1)
else if A[k'] < A[k]
Wn[k] = max(Wn[k], Wp[k']+1)
result = max(max(Wp[i], Wn[i]) for i = 0, 1, ..., n-1)
设Wp[i]是从元素i开始的最长摆动序列,其中第一个差值为正。设Wn[i]相同,但第一个差为负 然后: 这给出了一个关于^2的动态规划解决方案,这里是伪代码
Wp = [1, 1, ..., 1] -- length n
Wn = [1, 1, ..., 1] -- length n
for k = n-1, n-2, ..., 0
for k' = k+1, k+2, ..., n-1
if A[k'] > A[k]
Wp[k] = max(Wp[k], Wn[k']+1)
else if A[k'] < A[k]
Wn[k] = max(Wn[k], Wp[k']+1)
result = max(max(Wp[i], Wn[i]) for i = 0, 1, ..., n-1)
动态规划可用于获得最优解 注意:我在看到@peterderarivaz提到的。当动态规划在2上运行时,本文提出了一种优于贪婪算法的方法5,它也比动态规划解决方案更容易编码。我添加了实现该方法的第二个答案 代码 例子 最长摆动的长度为6,由指数0、1、2、3、5和7处的元素组成,即[1、4、2、6、3、5] 第二个示例使用问题中给出的较大数组
arr = [33, 53, 12, 64, 50, 41, 45, 21, 97, 35, 47, 92, 39, 0, 93, 55, 40, 46,
69, 42, 6, 95, 51, 68, 72, 9, 32, 84, 34, 64, 6, 2, 26, 98, 3, 43, 30,
60, 3, 68, 82, 9, 97, 19, 27, 98, 99, 4, 30, 96, 37, 9, 78, 43, 64, 4,
65, 30, 84, 90, 87, 64, 18, 50, 60, 1, 40, 32, 48, 50, 76, 100, 57, 29,
arr.size 63, 53, 46, 57, 93, 98, 42, 80, 82, 9, 41, 55, 69, 84, 82, 79, 30, 79,
18, 97, 67, 23, 52, 38, 74, 15]
#=> 100
longest_wiggle(arr).size
#=> 67
longest_wiggle(arr)
#=> [0, 1, 2, 3, 5, 6, 7, 8, 9, 10, 12, 14, 16, 17, 19, 21, 22, 23, 25,
# 27, 28, 29, 30, 32, 34, 35, 36, 37, 38, 39, 41, 42, 43, 44, 47, 49, 50,
# 52, 53, 54, 55, 56, 57, 58, 62, 63, 65, 66, 67, 70, 72, 74, 75, 77, 80,
# 81, 83, 84, 90, 91, 92, 93, 95, 96, 97, 98, 99]
如图所示,最大摆动由67个arr元素组成。溶解时间基本上是瞬时的
这些指数的arr值如下所示
[33, 53, 12, 64, 41, 45, 21, 97, 35, 47, 39, 93, 40, 46, 42, 95, 51, 68, 9,
84, 34, 64, 6, 26, 3, 43, 30, 60, 3, 68, 9, 97, 19, 27, 4, 96, 37, 78, 43,
64, 4, 65, 30, 84, 18, 50, 1, 40, 32, 76, 57, 63, 53, 57, 42, 80, 9, 41, 30,
79, 18, 97, 23, 52, 38, 74, 15]
[33, 53, 12, 64, 41, 45, 21, 97, 35, 92, 0, 93, 40, 69, 6, 95, 51, 72, 9, 84, 34, 64, 2, 98, 3, 43, 30, 60, 3, 82, 9, 97, 19, 99, 4, 96, 9, 78, 43, 64, 4, 65, 30, 90, 18, 60, 1, 40, 32, 100, 29, 63, 46, 98, 42, 82, 9, 84, 30, 79, 18, 97, 23, 52, 38, 74]
arr = [3, 4, 4, 5, 2, 3, 7, 4]
enum1 = arr.each_cons(2)
#=> #<Enumerator: [3, 4, 4, 5, 2, 3, 7, 4]:each_cons(2)>
解释
我本来打算对算法及其实现进行解释,但后来了解到有一种更好的方法,请参见我在回答开头的说明,我决定不这样做,但当然很乐意回答任何问题。除其他外,我的说明中的链接解释了如何在此处使用动态规划。动态规划可用于获得最佳解决方案 注意:我在看到@peterderarivaz提到的。当动态规划在2上运行时,本文提出了一种优于贪婪算法的方法5,它也比动态规划解决方案更容易编码。我添加了实现该方法的第二个答案 代码 例子 最长摆动的长度为6,由指数0、1、2、3、5和7处的元素组成,即[1、4、2、6、3、5] 第二个示例使用问题中给出的较大数组
arr = [33, 53, 12, 64, 50, 41, 45, 21, 97, 35, 47, 92, 39, 0, 93, 55, 40, 46,
69, 42, 6, 95, 51, 68, 72, 9, 32, 84, 34, 64, 6, 2, 26, 98, 3, 43, 30,
60, 3, 68, 82, 9, 97, 19, 27, 98, 99, 4, 30, 96, 37, 9, 78, 43, 64, 4,
65, 30, 84, 90, 87, 64, 18, 50, 60, 1, 40, 32, 48, 50, 76, 100, 57, 29,
arr.size 63, 53, 46, 57, 93, 98, 42, 80, 82, 9, 41, 55, 69, 84, 82, 79, 30, 79,
18, 97, 67, 23, 52, 38, 74, 15]
#=> 100
longest_wiggle(arr).size
#=> 67
longest_wiggle(arr)
#=> [0, 1, 2, 3, 5, 6, 7, 8, 9, 10, 12, 14, 16, 17, 19, 21, 22, 23, 25,
# 27, 28, 29, 30, 32, 34, 35, 36, 37, 38, 39, 41, 42, 43, 44, 47, 49, 50,
# 52, 53, 54, 55, 56, 57, 58, 62, 63, 65, 66, 67, 70, 72, 74, 75, 77, 80,
# 81, 83, 84, 90, 91, 92, 93, 95, 96, 97, 98, 99]
如图所示,最大摆动由67个arr元素组成。溶解时间基本上是瞬时的
这些指数的arr值如下所示:
接着
解释
我本来打算对算法及其实现进行解释,但后来了解到有一种更好的方法,请参见我在回答开头的说明,我决定不这样做,但当然很乐意回答任何问题。我的笔记中的链接解释了动态编程在这里的应用。在对@quertyman的回答的评论中,@Peterdererivaz提供了一个链接,该链接考虑了解决最长摆动子序列问题的各种方法。我已经实现了方法5,它的时间复杂度为On 该算法简单快速。第一步是从每对相等的连续元素中删除一个元素,然后继续删除,直到没有相等的连续元素为止。例如,[1,2,2,3,4,4]将转换为[1,2,3,4]。最长摆动子序列包括结果数组的第一个和最后一个元素a,以及每个元素a[i],0a[i+1]ora[i-1]>a[i]>a[i+1]。换句话说,它包括第一个和最后一个元素以及所有的峰谷底。这些元素是A,D,E,G,H,I,在下面的图表中,在许可的情况下取自上述参考文章 代码 范例 解释 步骤如下
[33, 53, 12, 64, 41, 45, 21, 97, 35, 47, 39, 93, 40, 46, 42, 95, 51, 68, 9,
84, 34, 64, 6, 26, 3, 43, 30, 60, 3, 68, 9, 97, 19, 27, 4, 96, 37, 78, 43,
64, 4, 65, 30, 84, 18, 50, 1, 40, 32, 76, 57, 63, 53, 57, 42, 80, 9, 41, 30,
79, 18, 97, 23, 52, 38, 74, 15]
[33, 53, 12, 64, 41, 45, 21, 97, 35, 92, 0, 93, 40, 69, 6, 95, 51, 72, 9, 84, 34, 64, 2, 98, 3, 43, 30, 60, 3, 82, 9, 97, 19, 99, 4, 96, 9, 78, 43, 64, 4, 65, 30, 90, 18, 60, 1, 40, 32, 100, 29, 63, 46, 98, 42, 82, 9, 84, 30, 79, 18, 97, 23, 52, 38, 74]
arr = [3, 4, 4, 5, 2, 3, 7, 4]
enum1 = arr.each_cons(2)
#=> #<Enumerator: [3, 4, 4, 5, 2, 3, 7, 4]:each_cons(2)>
继续,除去每组连续相等的元素中的一个以外的所有元素
d = enum1.reject { |a,b| a==b }
#=> [[3, 4], [4, 5], [5, 2], [2, 3], [3, 7], [7, 4]]
e = d.map(&:first)
#=> [3, 4, 5, 2, 3, 7]
添加最后一个元素
f = e.push(arr.last)
#=> [3, 4, 5, 2, 3, 7, 4]
接下来,找到山峰和谷底
enum2 = f.each_cons(3)
#=> #<Enumerator: [3, 4, 5, 2, 3, 7, 4]:each_cons(3)>
enum2.to_a
#=> [[3, 4, 5], [4, 5, 2], [5, 2, 3], [2, 3, 7], [3, 7, 4]]
g = enum2.select { |triple| [triple.min, triple.max].include? triple[1] }
#=> [[4, 5, 2], [5, 2, 3], [3, 7, 4]]
h = g.map { |_,n,_| n }
#=> [5, 2, 7]
在对@quertyman的回答的评论中,@PeterdeRivaz提供了一个链接,链接到一个考虑解决最长摆动子序列问题的各种方法的应用程序。我已经实现了方法5,它的时间复杂度为On 该算法简单快速。第一步是从每对相等的连续元素中删除一个元素,然后继续删除,直到没有相等的连续元素为止。例如,[1,2,2,3,4,4]将转换为[1,2,3,4]。最长摆动子序列包括结果数组的第一个和最后一个元素a,以及每个元素a[i],0a[i+1]ora[i-1]>a[i]>a[i+1]。换句话说,它包括第一个和最后一个元素以及所有的峰谷底。这些元素是A,D,E,G,H,I,在下面的图表中,在许可的情况下取自上述参考文章 代码 范例 解释 步骤如下
[33, 53, 12, 64, 41, 45, 21, 97, 35, 47, 39, 93, 40, 46, 42, 95, 51, 68, 9,
84, 34, 64, 6, 26, 3, 43, 30, 60, 3, 68, 9, 97, 19, 27, 4, 96, 37, 78, 43,
64, 4, 65, 30, 84, 18, 50, 1, 40, 32, 76, 57, 63, 53, 57, 42, 80, 9, 41, 30,
79, 18, 97, 23, 52, 38, 74, 15]
[33, 53, 12, 64, 41, 45, 21, 97, 35, 92, 0, 93, 40, 69, 6, 95, 51, 72, 9, 84, 34, 64, 2, 98, 3, 43, 30, 60, 3, 82, 9, 97, 19, 99, 4, 96, 9, 78, 43, 64, 4, 65, 30, 90, 18, 60, 1, 40, 32, 100, 29, 63, 46, 98, 42, 82, 9, 84, 30, 79, 18, 97, 23, 52, 38, 74]
arr = [3, 4, 4, 5, 2, 3, 7, 4]
enum1 = arr.each_cons(2)
#=> #<Enumerator: [3, 4, 4, 5, 2, 3, 7, 4]:each_cons(2)>
继续,除去每组连续相等的元素中的一个以外的所有元素
d = enum1.reject { |a,b| a==b }
#=> [[3, 4], [4, 5], [5, 2], [2, 3], [3, 7], [7, 4]]
e = d.map(&:first)
#=> [3, 4, 5, 2, 3, 7]
添加最后一个元素
f = e.push(arr.last)
#=> [3, 4, 5, 2, 3, 7, 4]
接下来,找到山峰和谷底
enum2 = f.each_cons(3)
#=> #<Enumerator: [3, 4, 5, 2, 3, 7, 4]:each_cons(3)>
enum2.to_a
#=> [[3, 4, 5], [4, 5, 2], [5, 2, 3], [2, 3, 7], [3, 7, 4]]
g = enum2.select { |triple| [triple.min, triple.max].include? triple[1] }
#=> [[4, 5, 2], [5, 2, 3], [3, 7, 4]]
h = g.map { |_,n,_| n }
#=> [5, 2, 7]
什么是预期的时间复杂度?不确定,但不是我关心的atm-现在我在寻找准确度Yi:预期的时间复杂度是什么?不确定,但不是我关心的atm-现在我在寻找准确度Yi:这无论如何都不是答案,应该作为评论。@mudasobwa为什么不?问题似乎是,有人知道这里出了什么问题吗?i、 使用给定的算法。好吧,的确,也许。值得注意的是,一个稍微不同的贪婪算法确实有效:如果我对我的备忘录添加有其他问题,我应该重新发布吗?这无论如何都不是答案,应该作为注释。@mudasobwa为什么不?问题似乎是,有人知道这里出了什么问题吗?i、 用给定的算法。好吧,的确,也许。也许值得注意的是,一个稍微不同的贪婪算法确实有效:如果我对我的备忘录添加有其他问题,我应该重新发布吗?