用 C 语言编写一个程序,该程序使用递归来确定一个数字是不是为素数。大量出现堆栈溢出错误
Posted
技术标签:
【中文标题】用 C 语言编写一个程序,该程序使用递归来确定一个数字是不是为素数。大量出现堆栈溢出错误【英文标题】:Writing a program in C that uses recursion to determine if a number is prime or not. Getting a stack overflow error at high numbers用 C 语言编写一个程序,该程序使用递归来确定一个数字是否为素数。大量出现堆栈溢出错误 【发布时间】:2020-01-03 05:21:20 【问题描述】:用 C 编写一个程序,该程序使用递归来确定一个数字是否为素数。在您尝试使用高于 9431 的素数之前,它一直有效。任何高于此的数都会导致堆栈溢出错误。我想知道是否有办法解决这个问题。
我没有真正尝试过,只是看看它在哪个数字上失败,每次都不同。
//Remove scanf error
#define _CRT_SECURE_NO_WARNINGS
//Preprocessor directives
#include<stdio.h>
#include<stdlib.h>
//Recursion function
int PrimeCheck(int choice, int i)
//Check if integer i is reduced to 1
if (i == 1)
return 0;
else
//Check to see if number choice is divisible by value i
if (choice % i == 0)
return 1;
//Call the function again but reduce the second variable by 1
else
return PrimeCheck(choice, i - 1);
//End PrimeCheck function
//Main function
main()
//Assign needed variables
int choice, num;
//ask for user input
printf("Please enter a number between 2 and %i:", INT_MAX);
scanf("%i", &choice);
//Check for numbers outside the range
if (choice < 2 || choice > INT_MAX)
printf("Please try again and enter a valid number.\n");
system("pause");
return 0;
//Call the PrimeCheck "looping" function
num = PrimeCheck(choice, choice / 2);
//Display result for the user
if (num == 0)
printf("%i is a prime number.\n", choice);
else
printf("%i is NOT a prime number.\n", choice);
system("pause");
//End main
输出应该是“____ 是一个素数”或“____ 不是一个素数” 9431以上的实际输出是堆栈溢出错误。
【问题讨论】:
(a) 这个程序不包括<limits.h>
,所以我们期望INT_MAX
没有被定义并且编译失败。这是您正在编译的确切源代码吗? (b) 您正在使用哪个编译器,您使用什么开关进行编译?
顺便说一句,choice > INT_MAX
不可能计算为真,因为choice
是int
,因此它的最大可能值为INT_MAX
。
无论如何,如果你用-O3
(甚至-O2
)编译,GCC 会很高兴地优化那个尾调用并将你的递归函数编译成一个简单的循环。没有更多的递归堆栈溢出:P
当您为超过INT_MAX
的数字输入数字时,scanf
会静默失败;它不会将数字的值放在choice
中。它不能,因为choice
不能容纳这样的数字。根据 C 2018 7.21.6.2 10,当结果无法在对象中表示时,行为未定义。
@DanielNudelman 代码非常错误。整数溢出恰好使它看起来正确,因为int
是一个有符号值,所以2147483648 == -1 < 2
。
【参考方案1】:
一个帮助,减少测试。
PrimeCheck(choice, choice / 2);
迭代大约 choice/2
次,而只需要 sqrt(choice)
次。
而不是从choice/2
开始
PrimeCheck(choice, sqrt(choice));
更好的代码会避免小的舍入误差和整数截断
PrimeCheck(choice, lround(sqrt(choice)));
或者如果您可以访问整数平方根函数:
PrimeCheck(choice, isqrt(choice));
对于9431
,这将使堆栈深度减少大约 50 倍 - 并加快程序速度。
速度性能提示。而不是从 choice / 2
或 sqrt(choice)
down 迭代到 1。从 2 到 sqrt(choice)
up。非素数的检测速度会更快。
样本
#include <stdbool.h>
#include <stdio.h>
bool isprimeR_helper(unsigned test, unsigned x)
// test values too large, we are done
if (test > x/test )
return true;
if (x%test == 0)
return false; // composite
return isprimeR_helper(test + 2, x);
bool isprimeR(unsigned x)
// Handle small values
if (x <= 3)
return x >= 2;
// Handle other even values
if (x %2 == 0)
return false;
return isprimeR_helper(3, x);
int main(void)
for (unsigned i = 0; i < 50000; i++)
if (isprimeR(i))
printf(" %u", i);
printf("\n");
输出
2 3 5 7 11 13 17 19 ... 49991 49993 49999
实现说明
不要使用if (test*test > x)
。使用test > x/test
test*test
可能会溢出。 x/test
不会。
优秀的编译器会看到附近的x/test
和x%test
,并将两者作为一个操作有效地计算。因此,如果代码有x%test
,那么x/test
的成本通常可以忽略不计。
【讨论】:
我明白了。谢谢你。对于像我这样的菜鸟来说,这都是很好的信息。对此,我真的非常感激。 :)以上是关于用 C 语言编写一个程序,该程序使用递归来确定一个数字是不是为素数。大量出现堆栈溢出错误的主要内容,如果未能解决你的问题,请参考以下文章