【问题标题】:How to find the kth largest element in the union of two sorted arrays?如何在两个排序数组的并集中找到第 k 个最大的元素?
【发布时间】:2012-07-26 23:13:10
【问题描述】:

我需要在两个排序后的数组中找到k 最大的元素,但要有所不同。

This 算法假定k<=max(m,n) 并且当k>max(m,n) 时索引出错。在我的问题中 我知道那将永远是k>(m+n)/2,因此是k>min(m,n),所以我需要稍微改变一下 Jules Olléon 的答案……我只是看不出是哪一点:~

我在第 3 页找到了这个 link,但是那里有错误(实施时,它不会返回正确的答案)

我知道一个快速的解决方法是将两个数组都乘以 -1 并取其中的 k 个最小值 联合并将答案乘以 -1,但这会使代码不可读。

不是家庭作业。

好吧,我想我误解了尼尔的回答或其他什么,因为这是我给“他”的东西

#include <algorithm>
#include <fstream>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <vector>

#include <Eigen/Dense>
using namespace Eigen;
using Eigen::VectorXf;
using Eigen::VectorXi;

float getNth(VectorXf& v1,VectorXf& v2,int& n){
        int step=(n/4),i1=(n/2),i2=(n-i1);
        while(!(v2(i2)>=v1(i1-1) && v1(i1)>v2(i2-1))){                   
            if(v1(i1-1)>=v2(i2-1)){
                i1-=step;
                i2+=step;
            } else {
                i1+=step;
                i2-=step;
            }
            step/=2;
            if(!step) step=1;
        }
        if(v1(i1-1)>=v2(i2-1))
            return v1(i1-1);
            else
            return v2(i2-1);    
}
int main(){  
    int p,q,n,k,l;
    float sol;
    std:: cout << "enter p " << std::endl;
    std::cin >> p; 
    std:: cout << "enter q " << std::endl;
    std::cin >> q;
    n=p+q;
    std:: cout  << " enter k larger than " << std::min(p,q) << " and smaller than " << n-1 << std::endl;
    std::cin >> k;
    
    k=n-k-1;
    
    srand(time(NULL));
    VectorXf v1=VectorXf::Random(p);
    srand(time(NULL));
    VectorXf v2=VectorXf::Random(q);
    VectorXf v3(n);
    v3 << v1, v2;
    std::sort(v3.data(),v3.data()+v3.size(),std::greater<float>()); //std::greater<float>()
    std::sort(v1.data(),v1.data()+v1.size(),std::greater<float>());
    std::sort(v2.data(),v2.data()+v2.size(),std::greater<float>());
    
    sol=getNth(v1,v2,k);
    std::cout << sol << std::endl;
    std::cout << v3(k) <<   std::endl;
    return 0;  
}  

这就是我得到的:

enter p 
12
enter q 
32
 enter k larger than 12 and smaller than 43
13
nthoftwo: /Desktop/work/p1/geqw4/vi3/out/sp/ccode/eigen/Eigen/src/Core/DenseCoeffsBase.h:409: Eigen::DenseCoeffsBase<Derived, 1>::Scalar& Eigen::DenseCoeffsBase<Derived, 1>::operator()(Eigen::DenseCoeffsBase<Derived, 1>::Index) [with Derived = Eigen::Matrix<float, -0x00000000000000001, 1>, Eigen::DenseCoeffsBase<Derived, 1>::Scalar = float, Eigen::DenseCoeffsBase<Derived, 1>::Index = long int]: Assertion `index >= 0 && index < size()' failed.
Aborted (core dumped)

如果你对eigen不熟悉:错误是getNth(v1,v2,k)引起的index out of bound错误

编辑:

这是对 J.F. Sebastian 下面简单而优雅的解决方案的一个非常小的修改——所有错误都是我的错误,但它似乎有效。目的是使用原始索引(即我不确定 Neil 的想法是否不可或缺)。

#include <algorithm>
#include <fstream>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <vector>
#include <cassert>
#include <iterator>

#include <Eigen/Dense>
using namespace Eigen;
using Eigen::VectorXf;
using Eigen::VectorXi;

template<class RandomIterator,class Compare>
typename std::iterator_traits<RandomIterator>::value_type
nsmallest(RandomIterator firsta,RandomIterator lasta,RandomIterator firstb,RandomIterator lastb,size_t n,Compare less) {
  assert(n<static_cast<size_t>((lasta-firsta)+(lastb-firstb)));
  if (firsta==lasta) return *(firstb+n);
  if (firstb==lastb) return *(firsta+n);

  size_t mida=(lasta-firsta)/2;
  size_t midb=(lastb-firstb)/2;
  if ((mida+midb)<n)
    return less(*(firstb+midb),*(firsta+mida))?
      nsmallest(firsta,lasta,firstb+midb+1,lastb,n-(midb+1),less):
      nsmallest(firsta+mida+1,lasta,firstb,lastb,n-(mida+1),less);
  else
    return less(*(firstb+midb),*(firsta+mida))?
      nsmallest(firsta,firsta+mida,firstb,lastb,n,less):
      nsmallest(firsta,lasta,firstb,firstb+midb,n,less);
}
int main(){  
    int p,q,n,k,l;
    float sol;
    std:: cout << "enter p " << std::endl;
    std::cin >> p; 
    std:: cout << "enter q " << std::endl;
    std::cin >> q;
    n=p+q;
    std:: cout  << " enter k larger than " << std::min(p,q) << " and smaller than " << n-1 << std::endl;
    std::cin >> k;
    
    srand(time(NULL));
    VectorXf v1=VectorXf::Random(p);
    srand(time(NULL));
    VectorXf v2=VectorXf::Random(q);
    VectorXf v3(n);
    v3 << v1, v2;
    std::sort(v3.data(),v3.data()+v3.size()); 
    std::sort(v1.data(),v1.data()+v1.size());
    std::sort(v2.data(),v2.data()+v2.size());
    
    sol=nsmallest(v1.data(),v1.data()+v1.size(),v2.data(),v2.data()+v2.size(),k,std::less<float>());
//if it works, these two should return the same.
    std::cout << sol << std::endl;  
    std::cout << v3(k) << std::endl;
    return 0;  
}  

【问题讨论】:

    标签: c++ algorithm language-agnostic


    【解决方案1】:

    根据您的 cmets,我了解到您希望找到给定 2 个反向排序数组的第 k 个最小值,例如,对于 a={5,4,3}, b={2,1,0};k=1,预期结果是 0,即最小值 - 第一个最小值值(表示k1开始计算)。

    鉴于 nsmallest() 函数适用于已排序的数组并接受自定义比较器,您可以:

    #include <functional> // greater<>
    #include <iostream>
    
    #define SIZE(a) (sizeof(a) / sizeof(*a))
    
    int main() {
      int a[] = {5,4,3};
      int b[] = {2,1,0};
      int k = 1; // find minimum value, the 1st smallest value in a,b
    
      int i = k - 1; // convert to zero-based indexing
      int v = nsmallest(a, a + SIZE(a), b, b + SIZE(b),
                SIZE(a)+SIZE(b)-1-i, std::greater<int>());
      std::cout << v << std::endl; // -> 0
      return v;
    }
    

    我用过@Neil's suggestion to fix the index@lambdapilgrim's answer for the algorithm

    #include <cassert>
    #include <iterator>
    
    template<class RandomIterator, class Compare>
    typename std::iterator_traits<RandomIterator>::value_type
    nsmallest(RandomIterator firsta, RandomIterator lasta,
              RandomIterator firstb, RandomIterator lastb,
              size_t n,
              Compare less) {
      assert(n < static_cast<size_t>((lasta - firsta) + (lastb - firstb)));
      if (firsta == lasta) return *(firstb + n);
      if (firstb == lastb) return *(firsta + n);
    
      size_t mida = (lasta - firsta) / 2;
      size_t midb = (lastb - firstb) / 2;
      if ((mida + midb) < n)
        return less(*(firstb + midb), *(firsta + mida)) ?
          nsmallest(firsta, lasta, firstb + midb + 1, lastb, n - (midb + 1), less) :
          nsmallest(firsta + mida + 1, lasta, firstb, lastb, n - (mida + 1), less);
      else
        return less(*(firstb + midb), *(firsta + mida)) ?
          nsmallest(firsta, firsta + mida, firstb, lastb, n, less) :
          nsmallest(firsta, lasta, firstb, firstb + midb, n, less);
    }
    

    【讨论】:

    • wao:这样一个优雅而强大的解决方案:这个太棒了:),你已经大大改进了原始实现。非常感谢您。我已经稍微修改了您的代码以使用原始 k - 请参阅问题编辑。
    • @user189035:上述代码的好处在于它是一个尾递归函数。一些编译器可以执行尾调用优化 (TCO)。 A manually written iterative version 仅比上述递归变体快一点。
    • 如果我错了,请纠正我 - mida 应该是 (firsta+lasta)/2...类似 mid2
    • @DJ':两个迭代器的差异可以解释为它们之间的距离。你如何解释他们的总和?总和可能会产生编译错误(如果ab 都是迭代器,则未定义a+b)。
    • 我的错-我认为它们是数组中的索引位置
    【解决方案2】:

    kth 最大元素也是 m + n + 1 - kth* 最小元素,因此您可以尝试以这种方式解决问题。

    *从 1 开始计数。如果 k 从 0 开始计数,请改用 m + n - 1 - k

    【讨论】:

    • 你提出的基本上是我在问题第四段中写的解决方案。我并不是说这很糟糕......我只是希望有一个更简单的方法:)
    • @user189035:不,您的解决方案涉及乘以-1,尼尔的答案没有
    • @J.F.Sebastian 但我链接到的代码假设观察结果按升序排序....如果您不乘以 -1 而是按降序排序,则它不起作用;回到一号广场。
    • @user189035: 你能提供一个例子a, b, k 这样nsmallest(a,b,m+n-1-k) != nlargest(a,b,k) 其中a,b 排序和0 &lt;= k &lt; (len(a) + len(b)),注意:@9876543 @, nlargest(a, b, k) == sorted(a+b,reverse=True)[k].
    • 这不是我真正遇到问题的地方...问题是如果您将 a、b 按降序排列,getsmallest 就会崩溃...
    【解决方案3】:

    我相信您想要类似于 mergesort 的合并步骤,在该步骤中,您逐步比较 m 的第 i 个元素与 n 的第 j 个元素 - 但不是将值存储在数组中,您只是在寻找第 k 个最小 - 所以当你找到它时,返回那个值(和/或其索引)并退出函数。

    【讨论】:

      【解决方案4】:

      我不确定 k>max(m,n) 是怎么回事!

      一个简单的解决方案:

      def find(v1, start1, end1, v2, start2, end2, k):
          i = (start1+end1)/2
          j = binsearchrank(v2, start2, end2, v1[i])
          ranki = (i-start1+1) + (j-start2)
          if ranki > k:
              return find(v2, start2, j, v1, start1, i, k)
          elif ranki < k:
              return find(v2, j, end2, v1, i+1, end1, k-ranki)
          else:
              return v1[i]
      

      复杂度为 O(log^2n)

      【讨论】:

      • 这可能只是因为我不是程序员,这些事情对于未经训练的头脑来说更加复杂。回到你的提议——什么是 binsearchrank()——从你的 O() 数字我假设它是二分搜索,对吧? ——我对名称后面的“等级”有点困惑——此外,这通常并不重要,但只是指出在我的情况下 log(n) 大于 10,而 J.F. Sebastian 的解决方案是 O(log(n)) :) -- 10 倍的差异是有问题的,因为这是应用程序的一部分,它花费了一半的时间在这个发现部分上。
      • @user189035 是的。 binsearchrank 确实返回排名。你说的对。 O(logn) 更适合您的情况,您应该使用它。
      猜你喜欢
      • 2014-06-07
      • 2017-03-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多