【问题标题】:Why is the code throwing segmentation fault?为什么代码抛出分段错误?
【发布时间】:2020-01-29 07:24:29
【问题描述】:

问题是找到最长的collat​​z系列存在的数字i

事实上,collat​​z 系列对于所有数字总是以 1 结束。

因此,任何数字都不会在其 collat​​z 系列中重复。使用这个事实,我编写了以下代码 逻辑: 我开始一个while循环,一直到n,并且对于每次迭代,我都为那个i存储系列的长度。 如果 i 出现在一些 n >= r > i 的序列中,则 i 终止循环并将 i 的长度添加到 r。 例如,假设 3 的序列是 3、10、5、16、8、4、2、1。现在对应于 2 的长度已经存储在 series_length 数组中,所以我使用该值。

然后是旁边的 for 循环,找到最长的系列并显示答案。

准确地说,n

代码:

#include <stdio.h>

int length = 0, series_length[500000], maxlength = 0;

void store_length(int n) {
    while(n > 1 && series_length[n] == 0) {
        length++;
        if(n%2 == 0) {
            n = n/2;
        }
        else {
            n = 3*n + 1;
        }
    }
    length += series_length[n];
}

int main() {
    int n, i = 1, result;
    scanf("%d", &n);
    series_length[1] = 1;//redundant statement

    while(i <= n) {
        store_length(i);
        series_length[i] = length;
        length = 0;
        i++;
    }

    for(int i = 1;i <= n; i++) {
        if(maxlength <= series_length[i]) {
            maxlength = series_length[i];
            result = i;
        }
    }

    printf("%d %d\n", result, maxlength);

    return 0;
}

输入- 10 输出- 9 20(如预期)

输入- 100000 输出- 分段故障 预期的- 77031 351

【问题讨论】:

  • 很可能是数组访问越界;在所有数组索引周围使用if 语句进行简单检查。
  • 您似乎相信 series_length[500000] 创建了一个索引从 1 到 500000,而索引从 0 到 499999 的数组。
  • 您的store_length 函数没有检查n 是否已超过50000(实际上您的代码最多只能处理49999,这是您的数组支持的最大索引)。输入 1819,n 最终变为 52487,此时您的索引超出了数组的末尾。当您的索引 >= 50000(或 > 50000,如果您将数组大小增加一)时,您需要添加一个检查以停止访问数组。
  • 使用值 100000 运行代码会溢出数组 series_length。 n 达到值 504466。您必须增加 series_length 数组的大小或更改算法。
  • 再测试一下,我发现对于 n = 500000,该值可以大于 1,047,216,490 !我没有进一步探索。你肯定超过了 32 位整数的大小。您需要使用long long int 作为n 的类型。这种大小的数组变得巨大。当n 为 500000 时,您应该探查您获得的最大值。

标签: c arrays segmentation-fault collatz


【解决方案1】:

n 的值超出范围。

你在函数store_length中有一行n = 3*n + 1;

使用输入为100000 的gdb 运行此程序会得到

 Thread 1 received signal SIGSEGV, Segmentation fault.
 0x0000000000401545 in store_length (n=532060) at 29_01.c:6
6           while(n > 1 && series_length[n] == 0) {
(gdb) p n
$1 = 532060

【讨论】:

  • 但是增加数组大小或使 n long 仍然不起作用
  • 你知道数组大小的限制吗?如果没有,您可以在需要时查看动态内存分配和realloc。即使这样,您也可能会超出可用内存限制,在这种情况下,您需要对算法进行返工
【解决方案2】:
  • 仅在合适的情况下存储它
  • ...如果已经计算过了就使用它
  • 避免使用全局变量
  • 首选无符号值
  • [使用描述性变量名称]

#include <stdio.h>

#define THE_SIZE 500000

unsigned series_length[THE_SIZE]= {0,};

unsigned get_length(unsigned val) {
    unsigned steps;
    for (steps=0; val > 1 ; steps++) {
        if (val < THE_SIZE && series_length[val]) { steps += series_length[val]; break; }
        if(val %2 ) val = 3*val + 1;
        else val /= 2;
    }
    return steps;
}

int main( int argc, char **argv) {
    unsigned top, val , result;
    unsigned  best,maxlength ;

    sscanf(argv[1], "%u", &top);

    series_length[1] = 1;//redundant statement

    best = maxlength = 0;
    for(val=1;val <= top; val++) {
        result = get_length(val);
        // store it if it fits;
        if(val<THE_SIZE) series_length[val] = result;
        if (result < maxlength) continue;
        best = val; maxlength = result;
    }


    printf("%u %u\n", best, maxlength);

    return 0;
}

最后,为了好玩,把数组变小

#define THE_SIZE 500

,对于给定的值,程序应该给出相同的结果。 (确实如此)

【讨论】:

    【解决方案3】:

    你得到最大值 24,648,077,896,n = 487039。

    因此,您必须为n 使用类型long long int,并且您应该使用包含24,648,077,896 个整数的数组以避免分段错误。不幸的是,我从未成功分配 100GB 的块。因此,您的优化不可行。

    如果不进行数组优化,我可以在 265 毫秒内扫描所有 500000 个 n 值。

    这是我的代码:

    #include <stdio.h>
    
    int collatz_length(int n) {
        int length = 0;
        long long int v = (long long int)n;
        while (v > 1) {
            if ((v&1) == 0)
                v = v / 2;
            else
                v = v*3 + 1;
            length++;
        }
        return length;
    }
    
    int main() {
        int max_i, max_l = 0;
        for (int i = 500000; i > 0; i--) {
            int l = collatz_length(i);
            if (l > max_l){
                max_l = l;
                max_i = i;
            }
        }
        printf("i: %d l: %d\n", max_i, max_l);
        return 0;
    }
    

    【讨论】:

      猜你喜欢
      • 2021-07-24
      • 2021-03-04
      • 2023-02-03
      • 1970-01-01
      • 2013-10-20
      • 1970-01-01
      • 2010-10-19
      • 1970-01-01
      相关资源
      最近更新 更多