【问题标题】:Binary Search in C Not working correctly [closed]C中的二进制搜索无法正常工作[关闭]
【发布时间】:2013-02-03 16:15:58
【问题描述】:
int recurbinarysearch(int * oringalary, int low, int high, int target) {

    if (low > high) {
        return -1;
    } else {
        int mid = (low + high) / 2;
        if (target == * (oringalary + mid)) return mid;
        if (target > * (oringalary + mid)) return recurbinarysearch(oringalary, mid + 1, high, target);
        if (target < * (oringalary + mid)) return recurbinarysearch(oringalary, low, mid - 1, target);
    }
}

有人看到我的递归二分搜索算法有错误吗?偶尔它会返回一个不正确的索引(通常它会偏离一个),但偶尔会偏离。然而,通常它是正确的。我没有看到问题,希望得到帮助。

【问题讨论】:

  • 您确定该数组已排序?
  • 是的,几乎 100% 的数组都被打印出来了,而且似乎排序正确。
  • 除法 (low + high)/2 是否可能存在某种累积舍入误差?
  • 同样,如果找不到数字,则返回 0,而不是 -1..
  • 我只想指出* (oringalary + mid) 是一种相当奇怪和丑陋的写oringalary[mid] 的方式。

标签: c search binary


【解决方案1】:

编辑这是被接受的,所以我想我真的应该试着把它变成一个正确的答案。

我最初假设(请参阅下面的“注意”)该问题使用的是半开边界。它实际上(正确地)使用 inclusive 边界。在最初的通话中,low=0high=n-1

使用包含边界通常被认为是一件坏事 - 请参阅 Dijkstra 的经典著作 (PDF)。在 C 系列语言中,半开边界是一种常见的约定,甚至对 for (i = 0; i &lt; n; i++) 的偏好超过 for (i = 0; i &lt;= n-1; i++)。但是,鉴于使用了包含边界,问题中的代码似乎是正确的。

不过,正如 WhozCraig 在 cmets 中发现的那样,调用代码不遵守该约定,并且传递了错误的边界 - 包括搜索范围内的越界垃圾项。因为那个额外的项目是垃圾,所以范围内的项目已排序的假设也可能无效。大多数搜索不会找到该垃圾项(因为您不太可能搜索它具有的任何垃圾值),但它会误导搜索。


注意这可能不是答案,但评论太长了。

你的界限是包容的、排他的还是半开放的?

我将假设为半开放式 - 包含 low,独占 high。如果是这样,这行看起来不对...

if (target < * (oringalary + mid))
    return recurbinarysearch(oringalary, low, mid - 1, target);

原因是您在mid 上检查了该项目,但您使用mid - 1 作为新的独占 上限。这意味着mid - 1 处未检查的项目已意外从搜索中排除。该行应该是...

if (target < * (oringalary + mid))
    return recurbinarysearch(oringalary, low, mid, target);

这会将位于mid - 1 的项目保留在要搜索的范围内。 mid 处的项目将不再被搜索,因为上限是独占的。

在二分搜索中搞乱边界是一个常见问题,它会导致比看起来应该的错误更多。

但是,这本身并不能解释您的症状 - 它应该有时(可能大约 50% 的搜索量) 找不到很多项,但它不应该报告错误的搜索位置成功了。

二进制搜索中错误边界的常见症状是无限循环(同一项目被重复检查,因为它没有被排除在边界之外)或搜索未能找到存在的项目(因为项目被排除在搜索范围之外未检查)。

说实话,我看不出您的症状是如何出现的。函数退出的所有可能方式都应该给出正确的成功结果,否则会给出-1 失败结果。我能想到的唯一可能的例外是在这段代码之外——误解了结果,例如未能检查 -1 结果。

顺便说一句 - 这是我代表我对one-comparison-per-iteration binary search的问题和回答的绝佳机会。

编辑我想我发现了边界的另一个问题 - 仍然假设半开,这条线是错误的......

if (low > high) {

应该是……

if (low >= high) {

原因是对于半开边界,如果边界相等,则中间没有要检查的项目——即使是下限项目也是无效的,因为上限等于它并且是独占的。这使您仍然可以测试

【讨论】:

  • +1 当我第一次看到问题时,实际上我认为索引看起来不成熟。我习惯于使用单个指针和上限来执行此操作,即bs(ar,len) 计算中间值,然后在发现时返回 true,或者返回 bs(ar, mid)bs(ar+mid, len-mid),但这更多是用于确定存在而不是获取实际的。感谢您了解这个。
  • 我认为界限在这个特定的实现中是包容性的。在这种情况下,如果最初输入包含边界,它们可以正常工作。正如 OP 所指出的,他在半开界时错误地传球。
【解决方案2】:

如需全面讨论二分搜索,请参阅 Jon Bentley 的 Programming Pearls

这是您的代码的测试工具,非常受 Programming Pearls 的启发,以及您的代码的检测版本。我所做的唯一更改是在二进制搜索中添加(现已注释掉)调试打印。测试代码的输出几乎是完美的(工具表明一切都通过了,但并不完全正确):

N =  0: 
search for  0 in  0 entries - returned  0 found  0 PASS
N =  1: [0] = 1;
search for  0 in  1 entries - returned -1          PASS
search for  1 in  1 entries - returned  0 found  1 PASS
search for  2 in  1 entries - returned -1          PASS
N =  2: [0] = 1;[1] = 3;
search for  0 in  2 entries - returned -1          PASS
search for  1 in  2 entries - returned  0 found  1 PASS
search for  2 in  2 entries - returned -1          PASS
search for  3 in  2 entries - returned  1 found  3 PASS
search for  4 in  2 entries - returned -1          PASS
N =  3: [0] = 1;[1] = 3;[2] = 5;
search for  0 in  3 entries - returned -1          PASS
search for  1 in  3 entries - returned  0 found  1 PASS
search for  2 in  3 entries - returned -1          PASS
search for  3 in  3 entries - returned  1 found  3 PASS
search for  4 in  3 entries - returned -1          PASS
search for  5 in  3 entries - returned  2 found  5 PASS
search for  6 in  3 entries - returned -1          PASS
N =  4: [0] = 1;[1] = 3;[2] = 5;[3] = 7;
search for  0 in  4 entries - returned -1          PASS
search for  1 in  4 entries - returned  0 found  1 PASS
search for  2 in  4 entries - returned -1          PASS
search for  3 in  4 entries - returned  1 found  3 PASS
search for  4 in  4 entries - returned -1          PASS
search for  5 in  4 entries - returned  2 found  5 PASS
search for  6 in  4 entries - returned -1          PASS
search for  7 in  4 entries - returned  3 found  7 PASS
search for  8 in  4 entries - returned -1          PASS
N =  5: [0] = 1;[1] = 3;[2] = 5;[3] = 7;[4] = 9;
search for  0 in  5 entries - returned -1          PASS
search for  1 in  5 entries - returned  0 found  1 PASS
search for  2 in  5 entries - returned -1          PASS
search for  3 in  5 entries - returned  1 found  3 PASS
search for  4 in  5 entries - returned -1          PASS
search for  5 in  5 entries - returned  2 found  5 PASS
search for  6 in  5 entries - returned -1          PASS
search for  7 in  5 entries - returned  3 found  7 PASS
search for  8 in  5 entries - returned -1          PASS
search for  9 in  5 entries - returned  4 found  9 PASS
search for 10 in  5 entries - returned -1          PASS
N =  6: [0] = 1;[1] = 3;[2] = 5;[3] = 7;[4] = 9;[5] = 11;
search for  0 in  6 entries - returned -1          PASS
search for  1 in  6 entries - returned  0 found  1 PASS
search for  2 in  6 entries - returned -1          PASS
search for  3 in  6 entries - returned  1 found  3 PASS
search for  4 in  6 entries - returned -1          PASS
search for  5 in  6 entries - returned  2 found  5 PASS
search for  6 in  6 entries - returned -1          PASS
search for  7 in  6 entries - returned  3 found  7 PASS
search for  8 in  6 entries - returned -1          PASS
search for  9 in  6 entries - returned  4 found  9 PASS
search for 10 in  6 entries - returned -1          PASS
search for 11 in  6 entries - returned  5 found 11 PASS
search for 12 in  6 entries - returned -1          PASS
N =  7: [0] = 1;[1] = 3;[2] = 5;[3] = 7;[4] = 9;[5] = 11;[6] = 13;
search for  0 in  7 entries - returned -1          PASS
search for  1 in  7 entries - returned  0 found  1 PASS
search for  2 in  7 entries - returned -1          PASS
search for  3 in  7 entries - returned  1 found  3 PASS
search for  4 in  7 entries - returned -1          PASS
search for  5 in  7 entries - returned  2 found  5 PASS
search for  6 in  7 entries - returned -1          PASS
search for  7 in  7 entries - returned  3 found  7 PASS
search for  8 in  7 entries - returned -1          PASS
search for  9 in  7 entries - returned  4 found  9 PASS
search for 10 in  7 entries - returned -1          PASS
search for 11 in  7 entries - returned  5 found 11 PASS
search for 12 in  7 entries - returned -1          PASS
search for 13 in  7 entries - returned  6 found 13 PASS
search for 14 in  7 entries - returned -1          PASS
N =  8: [0] = 1;[1] = 3;[2] = 5;[3] = 7;[4] = 9;[5] = 11;[6] = 13;[7] = 15;
search for  0 in  8 entries - returned -1          PASS
search for  1 in  8 entries - returned  0 found  1 PASS
search for  2 in  8 entries - returned -1          PASS
search for  3 in  8 entries - returned  1 found  3 PASS
search for  4 in  8 entries - returned -1          PASS
search for  5 in  8 entries - returned  2 found  5 PASS
search for  6 in  8 entries - returned -1          PASS
search for  7 in  8 entries - returned  3 found  7 PASS
search for  8 in  8 entries - returned -1          PASS
search for  9 in  8 entries - returned  4 found  9 PASS
search for 10 in  8 entries - returned -1          PASS
search for 11 in  8 entries - returned  5 found 11 PASS
search for 12 in  8 entries - returned -1          PASS
search for 13 in  8 entries - returned  6 found 13 PASS
search for 14 in  8 entries - returned -1          PASS
search for 15 in  8 entries - returned  7 found 15 PASS
search for 16 in  8 entries - returned -1          PASS
N =  9: [0] = 1;[1] = 3;[2] = 5;[3] = 7;[4] = 9;[5] = 11;[6] = 13;[7] = 15;[8] = 17;
search for  0 in  9 entries - returned -1          PASS
search for  1 in  9 entries - returned  0 found  1 PASS
search for  2 in  9 entries - returned -1          PASS
search for  3 in  9 entries - returned  1 found  3 PASS
search for  4 in  9 entries - returned -1          PASS
search for  5 in  9 entries - returned  2 found  5 PASS
search for  6 in  9 entries - returned -1          PASS
search for  7 in  9 entries - returned  3 found  7 PASS
search for  8 in  9 entries - returned -1          PASS
search for  9 in  9 entries - returned  4 found  9 PASS
search for 10 in  9 entries - returned -1          PASS
search for 11 in  9 entries - returned  5 found 11 PASS
search for 12 in  9 entries - returned -1          PASS
search for 13 in  9 entries - returned  6 found 13 PASS
search for 14 in  9 entries - returned -1          PASS
search for 15 in  9 entries - returned  7 found 15 PASS
search for 16 in  9 entries - returned -1          PASS
search for 17 in  9 entries - returned  8 found 17 PASS
search for 18 in  9 entries - returned -1          PASS

几乎所有这些都很好;唯一的问题 child 是第一次搜索,它应该会失败,因为在空数组中不应该找到任何值。

测试代码为:

#include <stdio.h>

int recurbinarysearch(int *oringalary, int low, int high, int target)
{
    //printf("-->> %d..%d: ", low, high);
    if (low > high)
    {
        //printf("<<-- %d ", -1);
        return -1;
    }
    else
    {
        int mid = (low + high) / 2;
        if (target == * (oringalary + mid))
        {
            //printf("<<-- %d ", mid);
            return mid;
        }
        if (target > * (oringalary + mid))
        {
            int r = recurbinarysearch(oringalary, mid + 1, high, target);
            //printf("<<-- %d ", r);
            return r;
        }
        if (target < * (oringalary + mid))
        {
            int r = recurbinarysearch(oringalary, low, mid - 1, target);
            //printf("<<-- %d ", r);
            return r;
        }
    }
}

int main(void)
{
    for (int i = 0; i < 10; i++)
    {
        int a[i+1]; // No zero-size arrays in C
        printf("N = %2d: ", i);
        for (int j = 0; j < i; j++)
        {
            a[j] = 2 * j + 1;
            printf("[%d] = %d;",j, a[j]);
        }
        putchar('\n');

        for (int j = 0; j < 2*i+1; j++)
        {
            int f = recurbinarysearch(a, 0, i, j);
            //putchar('\n');  // debug
            printf("search for %2d in %2d entries - returned %2d",
                    j, i, f);
            if (f >= 0 && f <= i)
            {
                printf(" found %2d", a[f]);
                printf(" %s", (a[f] == j) ? "PASS" : "FAIL");
            }
            else
                printf(" %8s %s", "", (j % 2 == 0) ? "PASS" : "FAIL");
            putchar('\n');
        }
    }
    return(0);
}

我将留给你解决如何处理空数组的情况。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-30
    • 1970-01-01
    • 1970-01-01
    • 2016-09-10
    • 2021-10-25
    • 2020-10-25
    相关资源
    最近更新 更多