【问题标题】:Merge Sort in RR中的合并排序
【发布时间】:2014-09-28 01:16:47
【问题描述】:

我正在自学书"Introduction to Algorithms" by Cormen et alli. 在他们的书中,他们使用伪代码,假设数组是通过指针(通过引用)传递的。这与 R 不同(对象通过值传递),所以我在尝试尽可能接近地翻译它们的伪代码时遇到了一些困难,尤其是在涉及递归时。大多数时候,我必须以不同的方式实现。

例如,使用合并排序算法,他们定义了合并函数(我认为我已经正确翻译了)和递归合并排序函数(直接翻译到 R 不起作用)。

伪代码中的合并函数如下: A 是一个数组,p、q 和 r 是数组的索引,使得 p

Merge(A, p, q, r)
n1 = q - p + 1
n2 = r - q
let L[1...n1+1] and R[1...n2+1] be new arrays
for i = 1 to n1
    L[i] = A[p+i-1]
for j = 1 to n2
    R[j] = A[q+j]
L[n1+1] = infinite
R[n2+1] = infinite
i=1
j=1
for k = p to r
    if L[i] <= R[j]
        A[j] = L[i]
        i = i + 1
    else
        A[k] = R[j]
        j = j + 1

我已将其翻译为 R:

Merge <- function(a, p, q, r){
  n1 <- q - p + 1
  n2 <- r - q
  L <- numeric(n1+1)
  R <- numeric(n2+1)
  for(i in 1:n1){
    L[i] <- a[p+i-1]
  }
  for(j in 1:n2){
    R[j] <- a[q+j]
  }
  L[n1+1] <- Inf
  R[n2+1] <- Inf
  i=1
  j=1
  for(k in p:r){
    if(L[i] <= R[j]){
      a[k] <- L[i]
      i <- i +1
    }else{
      a[k] <- R[j]
      j <- j+1
    }
  }
  a
}

而且它似乎工作正常。

Merge(c(1,3,5, 2,4,6), 1, 3, 6)
[1] 1 2 3 4 5 6

现在MergeSort函数的伪代码定义如下:

MergeSort(A, p, r)
if p < r
   q = (p+r)/2
   MergeSort(A, p, q)
   MergeSort(A, q+1, r)
   Merge(A, p, q, r)

这假设 A 是通过引用传递的,并且每个更改对每个递归调用都是可见的,这在 R 中是不正确的。

那么,给定上面定义的Merge 函数,你将如何在R 中实现MergeSort 函数以获得正确的结果? (如果可能,并且更可取,但不是必需的,有点类似于伪代码)

【问题讨论】:

  • 试试 envir = .GlobalEnv
  • envir = .GlobalEnv 将使您的变量在每个递归调用中可见。但是,我不确定如何使用它来解决您的问题。查看此内容并搜索其他示例:stackoverflow.com/questions/22412620/…

标签: r algorithm sorting recursion mergesort


【解决方案1】:

试图对为一种语言编写的伪代码进行字面翻译,这种语言允许在不支持它的语言中进行引用传递,这是一个糟糕的想法。 R 并不适用于函数内的数组切片。这只是不恰当的翻译。伪代码应该传达算法的精神,然后您将其翻译成适当的语言。这是将合并排序的精神翻译成 R 的一种可能。

mmerge<-function(a,b) {
    r<-numeric(length(a)+length(b))
    ai<-1; bi<-1; j<-1;
    for(j in 1:length(r)) {
        if((ai<=length(a) && a[ai]<b[bi]) || bi>length(b)) {
            r[j] <- a[ai]
            ai <- ai+1
        } else {
            r[j] <- b[bi]
            bi <- bi+1          
        }
    }
    r
}
mmergesort<-function(A) {
    if(length(A)>1) {
        q <- ceiling(length(A)/2)
        a <- mmergesort(A[1:q])
        b <- mmergesort(A[(q+1):length(A)])
        mmerge(a,b)
    } else {
        A
    }
}

你可以运行它

x<-c(18, 16, 8, 7, 6, 3, 11, 9, 15, 1)
mmergesort(x)

在这个版本中,事物通过引用被替换:所有函数都返回新值。另外,我们不是传入幻灯片索引,而是简单地对向量进行子集化并将它们整体传递给函数。

当然,由于在中间步骤发生的所有内存重新分配,此版本的性能可能会受到影响。由于语言的设计方式,在基础 R 中您无能为力。如果您愿意,您可以编写 C/C++ 代码并通过 foreign language interfaces 调用它。

如果您想保持 Merge 原样(并忽略 R 方式做事),那么您可以这样做...

MergeSort<-function(A, p, r) {
    if(p < r) {
        q <- floor((p+r)/2)
        A <- MergeSort(A, p, q)
        A <- MergeSort(A, q+1, r)
        Merge(A, p, q, r)
    } else {
        A
    }
}
x <- c(18, 16, 8, 7, 6, 3, 11, 9, 15, 1)
MergeSort(x, 1, length(x))

更新:

包括基准测试工具

m1<-function() {
    x<-sample(1000, 250);
    mmergesort(x)
}

m2<-function() {
    x<-sample(1000, 250);
    MergeSort(x, 1, length(x))
}

microbenchmark(m1(), m2())

【讨论】:

  • 谢谢 MrFlick,但我不是在寻找在 R 中实现合并排序的替代方法,因为有很多公式很容易获得。我正在寻找的是尽可能类似于伪代码的答案,即使在现实生活中这将是一个糟糕的主意。
  • 目标是:给定Merge函数,你将如何在R中实现MergeSort函数以获得正确的结果,尽可能与伪代码相似。跨度>
  • @MrFlick: 有没有 envir = .GlobalEnv 的解决方案?
  • @rnso 你会在哪里使用那个参数?我们在这里没有使用assign()。现在,您可以向parent.frame() 添加一些分配,但因为它不包含在伪代码中,我猜它也不会通过测试。即使使用切换环境,也不可能只更新向量的一部分。如果您更新任何元素,您将获得一个全新的矢量
  • 我包含了一个不涉及 Merge 函数的更新。
【解决方案2】:

这个解决方案运行时只获得一次长度和更简单的逻辑。而merge是在mergesort内部实现的:

  mergesort = function(x){
  l = length(x)
  if(l==1)
  {
    return(x)
  }
  else
  {
    a = mergesort(x[1:((l - l %% 2)/2)])
    b = mergesort(x[((l + 2 - l %% 2)/2):l])
    a = c(a, Inf)
    b = c(b, Inf)
    for(el in 1:l){
      if(a[1]>=b[1]){
        x[el] = b[1]
        b = b[-1]
      }
      else{
        x[el] = a[1]
        a = a[-1]
      }
    }
    return(x)
  }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-04
    • 1970-01-01
    • 1970-01-01
    • 2023-04-10
    • 2016-08-09
    相关资源
    最近更新 更多