【问题标题】:Debug Longest Increasing Subsequence- Ruby调试最长递增子序列——Ruby
【发布时间】:2017-07-07 16:56:57
【问题描述】:

我正在通过 Leetcode 解决以下问题:

给定一个未排序的整数数组,求最长递增子序列的长度。

例如,给定 [10, 9, 2, 5, 3, 7, 101, 18],最长的 递增子序列为 [2, 3, 7, 101],因此长度为 4。 请注意,可能有多个 LIS 组合,它只是 您需要返回长度。

您的算法应该以 O(n2) 复杂度运行。

跟进:您能否将其提高到 O(n log n) 时间复杂度?

尝试实现第一个蛮力解决方案,它本质上是一种递归方式,可以生成每个递增的子序列,然后抓取长度最大的子序列。在我实现这个之后,我将使用动态规划来优化。我发现自己对蛮力的实现有点困惑——这是我的代码:

def length_of_lis(nums)
    1 + max_len(nums, 1, nums.first)
end

def max_len(nums, index, prev_number)
    current_num = nums[index]
    max = 0 
    return 0 if index >= nums.length     
    if current_num > prev_number
        max = [1 + max_len(nums, index + 1, current_num), max].max
    else
        max = [max_len(nums, index + 1, prev_number), max].max
    end 
    max = [1 + max_len(nums, index + 1, current_num), max].max

    max
end

现在我知道这显然是不正确的,但我想要的是,在每个数字上,都会发生几件事。要么 1) 它从前一个数字传递一个函数,如果这个当前数字大于前一个数字,则继续 LIS 的该函数。 2) 在当前编号处创建一个新的 LIS 子序列。

如您所知,这会创建两条相同的行,并且我不确定如何构造代码,以便发生两件不同的事情并且 max 变量包含最终值。关于如何相应地调整此代码的任何想法?

【问题讨论】:

  • 我没有回答你的问题,因为我无法理解你在做什么。 (前两行代码特别奇怪。也许是工件?)但是,我确实提供了一个动态编程解决方案(我看到你的下一步)。我认为DP绝对是去这里的方式。是 O(n^2),但我认为没有更快的解决方案。
  • 前两个实际上应该被注释掉——它们是为了让网站的用户知道输入和输出应该是什么。我当然会分析您提供的解决方案,但它有助于我了解我在思考过程中出错的地方,而不是简单地查看解决方案/阅读其解释。我删除了前两行——我可以为您编辑其他内容或解释一下吗?

标签: ruby algorithm subsequence


【解决方案1】:

我使用dynamic programming 来获得最佳解决方案。

代码

def construct_sequence(longest, max_i)
  a = []
  loop do
    a << max_i
    max_i = longest[max_i][:next]
    return a if max_i.nil?
  end
end      

def longest_increasing_sequence(arr)
  longest = Array.new(arr.size)
  longest[arr.size-1] = { length: 1, next: nil }
  (arr.size-2).downto(0) do |i|
    best_seq = { length: 1, next: nil }
    (i+1).upto(arr.size-1) do |j| 
      next if arr[j] <= arr[i]
      h = longest[j]      
      best_seq = { length: 1 + h[:length], next: j } if h[:length] >= best_seq[:length]
    end
    longest[i] = best_seq
  end
  max_i = (0..arr.size-1).max_by { |i| longest[i][:length] }
  construct_sequence(longest, max_i)
end

示例

arr = [10, 9, 2, 5, 3, 7, 101, 18]
a = longest_increasing_sequence(arr)
  #=> [2, 3, 5, 6] 
a.each_with_object({}) { |n,h| h[n] = arr[n] }
  #=> {2=>2, 3=>5, 5=>7, 6=>101}

对于第二个示例,我构造了以下 20 个元素的伪随机数组。

arr = (1..99).to_a.sample(20)
  #=> [80, 75, 13, 12, 85, 16, 41, 89, 93, 56, 74, 18, 37, 24, 27, 63, 47, 83, 25, 44]

longest_increasing_sequence 返回构成最长递增序列的arr 索引数组。

a = longest_increasing_sequence(arr)
  #=> [2, 5, 11, 13, 14, 15, 17]
a.each_with_object({}) { |n,h| h[n] = arr[n] }
  #=> {2=>13, 5=>16, 11=>18, 13=>24, 14=>27, 15=>63, 17=>83}

说明

数组的每个元素都有一个stage。 状态变量是最长递增序列(“LIS”)开始的数组的索引。我们从数组的最后一个元素开始,在上面的例子中是arr[19]。如果递增序列(“IS”)从那里开始,它也在那里结束。该序列的长度为1。

然后我们回到第 18 阶段。有两种可能性:从那个阶段开始的 LS 的长度为 1,或者在第 19 阶段继续(如果序列增加),在这种情况下它的长度为 @ 987654331@.

更一般地,如果 LIS 从索引 i 开始,它可能会在此结束或继续,j 是 LIS 中的下一个索引,其中 i+1 &lt;= j &lt;= arr.size-1 和 arr[i] &lt; arr[j]。对于任何这样的j,如果序列从索引j 开始,我们已经计算了LIS,所以我们知道,从索引j,i 和j 共享相同的LIS 如果下一个从i 开始的LIS 元素是j。因此,为了获得从i 开始的LIS,我们取i+1 和arr.size-1 之间的j 的最大LIS 的大小,其中arr[i] &lt; arr[j] 并添加1,除非没有索引j其中arr[i] &lt; arr[j],在这种情况下i 的LIS 以i 结束。

动态规划解决方案基于最优性原则,即如果索引i 是LIS 的成员,则索引j, j &gt; i 的集合也是成员该 LIS 不依赖于作为该 LIS 成​​员的索引 j, j &lt; i。换句话说,从索引i 开始的最佳方式并不取决于我们是如何到达那里的。

为了显示计算的详细信息,我向longest_increasing_sequence 添加了一些 puts 语句:

def longest_increasing_sequence(arr)
  longest = Array.new(arr.size)
  longest[arr.size-1] = { length: 1, next: nil }
  puts "longest[#{arr.size-1}]=#{longest[arr.size-1]}"
  (arr.size-2).downto(0) do |i|
    best_seq = { length: 1, next: nil }
    puts "i=#{i}"
    puts "  initial best_seq=#{best_seq}"
    (i+1).upto(arr.size-1) do |j| 
      puts "    j=#{j}, arr[#{i}]=#{arr[i]}, arr[#{j}]=#{arr[j]}"   
      next if arr[j] <= arr[i]
      h = longest[j]      
      puts "    h=#{h}"
      puts "    j=#{j} provides new best_seq=#{{length: 1 + h[:length], next: j }}" if
        h[:length] >= best_seq[:length]
      best_seq = { length: 1 + h[:length], next: j } if h[:length] >= best_seq[:length]
    end
    longest[i] = best_seq
  end
  longest.each_index { |i| puts "i=#{i}: #{longest[i]}" }
  max_i = (0..arr.size-1).max_by { |i| longest[i][:length] }
  construct_sequence(longest, max_i)
end

arr = [60, 29, 56, 46, 37, 57, 28, 44]
longest_increasing_sequence(arr)

longest[7]={:length=>1, :next=>nil}
i=6
  initial best_seq={:length=>1, :next=>nil}
    j=7, arr[6]=28, arr[7]=44
    h={:length=>1, :next=>nil}
    j=7 provides new best_seq={:length=>2, :next=>7}
i=5
  initial best_seq={:length=>1, :next=>nil}
    j=6, arr[5]=57, arr[6]=28
    j=7, arr[5]=57, arr[7]=44

i=4
  initial best_seq={:length=>1, :next=>nil}
    j=5, arr[4]=37, arr[5]=57
    h={:length=>1, :next=>nil}
    j=5 provides new best_seq={:length=>2, :next=>5}
    j=6, arr[4]=37, arr[6]=28
    j=7, arr[4]=37, arr[7]=44
    h={:length=>1, :next=>nil}
i=3
  initial best_seq={:length=>1, :next=>nil}
    j=4, arr[3]=46, arr[4]=37
    j=5, arr[3]=46, arr[5]=57
    h={:length=>1, :next=>nil}
    j=5 provides new best_seq={:length=>2, :next=>5}
    j=6, arr[3]=46, arr[6]=28
    j=7, arr[3]=46, arr[7]=44
i=2
  initial best_seq={:length=>1, :next=>nil}
    j=3, arr[2]=56, arr[3]=46
    j=4, arr[2]=56, arr[4]=37
    j=5, arr[2]=56, arr[5]=57
    h={:length=>1, :next=>nil}
    j=5 provides new best_seq={:length=>2, :next=>5}
    j=6, arr[2]=56, arr[6]=28
    j=7, arr[2]=56, arr[7]=44

i=1
  initial best_seq={:length=>1, :next=>nil}
    j=2, arr[1]=29, arr[2]=56
    h={:length=>2, :next=>5}
    j=2 provides new best_seq={:length=>3, :next=>2}
    j=3, arr[1]=29, arr[3]=46
    h={:length=>2, :next=>5}
    j=4, arr[1]=29, arr[4]=37
    h={:length=>2, :next=>5}
    j=5, arr[1]=29, arr[5]=57
    h={:length=>1, :next=>nil}
    j=6, arr[1]=29, arr[6]=28
    j=7, arr[1]=29, arr[7]=44
    h={:length=>1, :next=>nil}
i=0
  initial best_seq={:length=>1, :next=>nil}
    j=1, arr[0]=60, arr[1]=29
    j=2, arr[0]=60, arr[2]=56
    j=3, arr[0]=60, arr[3]=46
    j=4, arr[0]=60, arr[4]=37
    j=5, arr[0]=60, arr[5]=57
    j=6, arr[0]=60, arr[6]=28
    j=7, arr[0]=60, arr[7]=44
i=0: {:length=>1, :next=>nil}
i=1: {:length=>3, :next=>2}
i=2: {:length=>2, :next=>5}
i=3: {:length=>2, :next=>5}
i=4: {:length=>2, :next=>5}
i=5: {:length=>1, :next=>nil}
i=6: {:length=>2, :next=>7}
i=7: {:length=>1, :next=>nil}
  #=> [1, 2, 5] 

【讨论】:

    猜你喜欢
    • 2013-07-03
    • 1970-01-01
    • 2020-04-15
    • 1970-01-01
    • 2013-11-27
    • 2018-11-01
    相关资源
    最近更新 更多