【问题标题】:Determine if a range is contained within a set of ranges确定一个范围是否包含在一组范围内
【发布时间】:2017-08-23 19:40:12
【问题描述】:

我在一个数组中有一组范围。例如(但请注意,我的范围集将大于此):

static const Section arr[] = {
    {
        .start_addr = 0,
        .length = 100,
    },
    {
        .start_addr = 150,
        .length = 25,   
    },
    {
        .start_addr = 175,
        .length = 25,   
    },

    ...
}

数据不一定是排序的,类型都会是整数类型。此外,地址永远不会是负数,因此可以是uint32_t

然后我想通过如下函数查询另一个范围是否包含在所有这些范围的联合中:

bool isEncapsulated(uint32_t addr, size_t len){
    // Here I need some sort of algorithm to determine whether 
    // it is fully encapsulated. 
}

我遇到的主要困难是找到一种方法来解释提供的范围可能超出两个定义范围的边界这一事实。例如。如果start_addr = 170, length = 10 落在两个已定义范围的边界上。

有没有优雅的方法来实现这一点?

编辑:

Here's 我正在尝试做的图形描述。本质上,范围 1、2、3 是我将在我的数组中定义的范围。然后,我将询问是否将任何范围 A-F 封装在其中。所以,D 和 E 是仅有的两个不是。我不只是想要重叠,我希望范围完全包含在预定义的范围内。

【问题讨论】:

  • 这是“重叠间隔”算法或其变体。谷歌一下。
  • 如果addrlen 是相同类型或至少是相同的sign-ness,则portable 答案会更容易。否则int/size_t 有很多极端情况。
  • arr 中的数据是按起始地址可靠排序的,还是只是在样本数据中巧合排序?数据中的任何范围是否重叠,或者它们是否都是不相交的,即使某些范围是相邻的?同样,样本数据暗示“完全不相交”,但这可能会产生误导。这些类型是整数类型,所以我们不必处理浮点问题吗?这些问题的答案会严重影响可能的算法。
  • @JonathanLeffler 数据不一定是排序的,类型都会是整数类型。另外,地址永远不会是负数,所以可以是uint32_t,我的错。
  • @chux start_addr = 170, length = 10 应该返回true,因为.start_addr = 150, ... .start_addr = 175 这两个部分将完全包含它。

标签: c


【解决方案1】:

我遇到的主要困难是找到一种方法来解释提供的范围可能超出两个定义范围的边界

情况更糟。一个范围可能会落在许多定义的范围内,并且询问的顺序可能会划分剩下的匹配项。考虑下面的x 范围首先在a 中找到。后来x的左边部分需要匹配b,右边部分需要匹配c

Range:      xxxxxxxxxxxx
Def range 1 ___aaaaaa___
Def range 2 bbb_________
Def range 3 _________ccc

一些经过轻微测试的代码。主要思想是在addr/len 范围内获取左右地址并测试每个部分中的地址。如果它对一侧微不足道,请继续下一节。否则,缩短addr/len。这可能会分成2部分。然后继续下一节。

typedef struct {
  int start_addr;
  size_t length;
} Section;

// Is point `a` in the section?
// return left:-1, right:1, else 0
static int LeftInRight(intmax_t a, const Section *sec) {
  if (a < sec->start_addr) return -1;
  if (a >= sec->start_addr + (intmax_t) sec->length) return 1;
  return 0;
}

bool isEncapsulated_helper(intmax_t addr, size_t len, const Section *sec, size_t n) {
  for (size_t i = 0; i<n; i++) {
    if (len == 0) return true;
    int LSide =  LeftInRight(addr, &sec[i]);
    if (LSide > 0) continue;  // all of addr/len is to the right of this section
    int RSide =  LeftInRight(addr + (intmax_t) (len - 1), &sec[i]);
    if (RSide < 0) continue;  // all of addr/len is to the left of this section

    if (LSide < 0) {
      // portion of addr/len is to the left of this section
      intmax_t Laddr = addr;
      size_t Llen = (size_t) (sec[i].start_addr - addr);
      if (!isEncapsulated_helper(Laddr, Llen, sec + 1, n-i-1)) {
        return false;
      }
    }
    if (RSide <= 0) return true;
    // portion of addr/len is to the right of this section, continue with that
    intmax_t Raddr = sec[i].start_addr + (intmax_t) sec[i].length;
    size_t Rlen = (size_t) (addr + (intmax_t) len - Raddr);
    addr = Raddr;
    len = Rlen;
  }
  return len == 0;
}

测试代码

static const Section arr[] = { // x
    { .start_addr = 0, .length = 100, }, // x
    { .start_addr = 150, .length = 25, }, // x
    { .start_addr = 175, .length = 25, }, };

#define SECTION_N (sizeof arr/sizeof arr[0])

bool isEncapsulated(int addr, size_t len) {
  return isEncapsulated_helper(addr, len, arr, SECTION_N);
}

int main() {
  printf("%d\n", isEncapsulated(170,10));
}

【讨论】:

  • 注意:如果这些部分按addr排序,则if (!isEncapsulated_helper(Laddr, Llen, sec + 1, n-i-1)) {简化为if (1) {允许计算在O(n)内完成。
【解决方案2】:

如果间隔不多,而您只想完成工作而不考虑算法,只需在O(n) 中线性搜索数组即可。如果列表中的间隔不重叠,请将它们排序并使用binary search (O(log n))。如果它们重叠,interval tree 是通常选择的数据结构(同样是O(log n)),但合并重叠或相邻区间并进行二分搜索会更简单。

如果您的地址是非常小的整数,并且O(log n) 对您来说太慢了(等等,什么?),您可以使用一个包含所有已使用地址的数组来交换一堆空间,以换取O(1) 您的答案问题,但您不太可能需要走这条路。

您可以在预处理期间合并相邻范围(即事先创建联合),或者您只需要处理这种特殊情况。

【讨论】:

  • 预处理const arr[] 是有意义的。
猜你喜欢
  • 2019-02-22
  • 1970-01-01
  • 1970-01-01
  • 2022-10-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多