一个“分而治之”的算法分配

Posted

技术标签:

【中文标题】一个“分而治之”的算法分配【英文标题】:a "divide and conquer" algorithm assignment 【发布时间】:2013-01-12 21:04:22 【问题描述】:

现在我有 N 个不同的整数,我需要在 O(NlogN) 时间内找到一个具有最多数值的区间,其值在区间的端点之间。我称其为“分而治之”问题,因为它属于我期末考试的“分而治之”类别。我已经考虑了 2 周并做了很多实验,但没有一个是正确的(与蛮力算法相比)。有人可以帮我吗?

例子:

8,1,3,4,7。答案是 1-7。

2,6,5,4,9,8。答案是 2-9 或 2-8。

我认为“间隔”这个词并不能表达我的意思。我的意思是找到数组的子序列,它的值在子序列的端点之间的数字最多。 eg.1:“1,3,4,7”有两个数字(3,4),eg.2:“2,6,5,4,9”和“2,6,5,4,9”都有,8" 有三个数字(6,5,4)。

这是我的代码 (O(n^2))。 @Vaughn Cato 我用它来与您的代码进行比较。

#! /usr/bin/env python
#coding=utf-8
import itertools
def n2(numbers):
  a = [0]*len(numbers)
  ans = -1
  l = 0
  r = 0
  for j in range(1,len(numbers)):
    t = 0
      for i in range(j-1,-1,-1):
        if numbers[i]<numbers[j]:
          x = t - a[i]
          if x>ans:
            ans = x
            l = i
            r = j
          t += 1
        else:
          a[i] += 1
  return (numbers[l],numbers[r],ans)

def countBetween(numbers,left,right):
  cnt = 0
  for i in range(left+1,right):
    if numbers[left]<numbers[i]<numbers[right]:
      cnt += 1
  return cnt

for numbers in itertools.permutations(range(5)):
  ans1=n2(numbers)
  ans2=longestInterval(numbers)
if(ans1[2]!=ans2[2]):
  print ans1,ans2,numbers

【问题讨论】:

如果您已经使用了 2 周,您可能已经尝试了一些东西。欢迎分享。 向我们展示您的代码、预期解决方案的问题示例,并告诉我们您未提及的任何问题详细信息/限制。 间隔有什么限制吗?否则你可以在 O(1) 中输出 [MIN_INT, MAX_INT]。 据我了解,您的问题是找到整数列表的最小值和最大值。合并排序(具有 O(N.log(N)) 复杂度),你就完成了......我认为它没有那么简单,所以清楚地解释你的主题 对不起,我已经简化了这个问题,所以我的代码不明确。我希望这些例子能解释清楚。 【参考方案1】:

注意:这实际上不起作用,但它可能会给你一些想法。

这样想:

X 为数字数组。 设s 为子序列开始的索引。 设e 为子序列末尾的索引。

如果你选择一个任意的分区索引p,那么最长的子序列要么穿过这个分区,要么落在那个分区的左边或右边。如果最长的子序列穿过这个分区,那么s &lt; p &lt;= e。要查找s,请查找sp 之间大于X[s] 的数字最多的索引。要查找“e”,请查找在pe 之间小于 X[e] 的数字最多的索引。

可以递归检查左右两边,看能否找到更长的子序列。

如果X 的索引按值排序,则可以在线性时间内找到哪个索引右侧的数字最大或左侧的数字最小:

要查找起始索引,请从已排序的索引列表中的第一个索引开始,并说它是迄今为止最好的。如果下一个索引大于迄今为止最好的索引,那么任何未来的索引都需要比我们当前的最好的更靠左才能成为新的最好的索引,所以我们从最好的索引中减去一个(但记住最好的索引是什么)真的是)。如果下一个索引在我们最好的索引的左边,那么让它成为最好的索引。按顺序对每个索引重复此过程。

您可以执行类似的过程来找到右侧末尾的最佳索引。

剩下的唯一技巧是为我们正在处理的任何范围维护已排序的索引列表。这可以通过最初对整个数字集进行排序并找到它们的索引来完成,然后在递归的每个级别上,我们可以在线性时间内将排序后的索引分成两个子列表。

这是这个想法的python实现:

# Find the index from the given indices that has the most numbers to the
# right of it which are greater in value.  The indices are sorted by
# the value of the numbers at that index. We don't even need to know
# what the numbers are.
def longestLowerSequence(indices):
  best_index=indices[0]
  target_index=best_index
  for i in range(0,len(indices)):
    if indices[i]<target_index:
      best_index=indices[i]
      target_index=best_index
    else:
      target_index-=1
  return best_index

# Find the index from the given indices that has the most numbers to the
# left of it which are less in value.
def longestUpperSequence(indices):
  n=len(indices)
  best_index=indices[n-1]
  target_index=best_index
  for i in range(0,n):
    if indices[n-1-i]>target_index:
      best_index=indices[n-1-i]
      target_index=best_index
    else:
      target_index+=1
  return best_index

# Return the pair of indices which has the most values between it.
def longestRangeFromSortedIndices(numbers,indices,begin,end):
  assert end>begin
  if end-begin<=2:
    return (indices[begin],indices[end-1])
  assert type(indices) is list
  partition=(begin+end)/2
  left_indices=filter(lambda index: index<partition,indices)
  right_indices=filter(lambda index: index>=partition,indices)
  assert len(left_indices)>0
  assert len(right_indices)>0
  left=longestLowerSequence(left_indices)
  right=longestUpperSequence(right_indices)
  left_range=longestRangeFromSortedIndices(numbers,indices,begin,partition)
  right_range=longestRangeFromSortedIndices(numbers,indices,partition,end)
  best_size=countBetween(numbers,left,right)
  best_range=(left,right)
  left_size=countBetween(numbers,left_range[0],left_range[1])
  right_size=countBetween(numbers,right_range[0],right_range[1])
  if left_size>best_size:
    best_size=left_size
    best_range=left_range
  if right_size>best_size:
    best_size=right_size
    best_range=right_range
  return best_range

def sortedIndices(numbers):
  return sorted(range(len(numbers)),key=lambda i: numbers[i])

def longestInterval(numbers):
  indices=sortedIndices(numbers)
  longest_range=longestRangeFromSortedIndices(numbers,indices,0,len(numbers))
  return (numbers[longest_range[0]],numbers[longest_range[1]])

【讨论】:

谢谢!但我认为这种方法可能是错误的。我将您的代码与蛮力代码进行了比较,发现:(1, 3, 4, 0, 2) 您的代码给出了 (1,2) 而答案是 (1,4)。 @amos:我同意。它假设您可以在分区的每一侧独立找到开始和结束索引,但它们实际上是相互关联的。【参考方案2】:

我相信这是maximum subarray problem 的变体。

分治法可以解决如下:

    将整数数组分成相等的两半

    分别计算两半的结果R1R2R1R2是每一半的最大间隔长度,同时存储起点和终点)

    从前半部分获取最小整数MIN,从后半部分获取最大整数MAX,并将结果R3计算为原始数组中MINMAX的距离( MinMax 分别是起点和终点)

    返回R1R2R3中最大的一个作为整个问题的结果

为什么会这样:

最大的间隔来自以下三种情况之一:1)前半部分 2)后半部分 3)跨越两部分。因此,计算三者中的最大值会产生最佳结果。

时间复杂度:

解决重复:

T(n) = 2T(n/2) + O(n)

给出T(n) = O(nlogn)。注意:正如递归所示,我们解决了两个一半大小的子问题(2T(n/2))并在线性时间(O(n))中找到两半中的最小和最大整数。

【讨论】:

谢谢!但我想知道你是否误解了这个问题。 “从前半部分获取最小整数 MIN,从后半部分获取最大整数 MAX,并将结果 R3 计算为原始数组中从 MIN 到 MAX 的距离(Min 和 Max 分别是起点和终点)”是不对的. @amos 我想我忽略了一个事实,即区间中的整数可能不在范围内。我会留下答案,以防有人从中获得灵感。我会继续努力的。

以上是关于一个“分而治之”的算法分配的主要内容,如果未能解决你的问题,请参考以下文章

分而治之的冒泡排序算法

分而治之算法 - 二分搜索变体

计算优势点的分而治之算法?

树的分而治之算法

为啥这个计数器以这种方式增加,而不是在这个分而治之的算法中一个一个增加?

分而治之——为啥有效?