连续this问题:
我在c ++中有一个一遍又一遍地调用自己的函数。就是这样:
#include <iostream>
#include <math.h>
#include <stdio.h>
#include <cmath>
using namespace std;
double g( double a, double x){
if (x>=a) return (g(a,x-1)+g(a,x-a));
else if (x<a) return 1;
return 0; //Never Reached
}
int main(){
cout << (unsigned long)g(sqrt(90),90) <<endl; // outputs 7564511
cout << (unsigned long)g(sqrt(10000019),10000019)<<endl; // Signal: SIGSEGV (Segmentation fault)
}
我想知道如何将此函数转换为某种迭代或尾循环(或任何阻止段错误的东西),但更重要的是我需要知道如何自己实际执行此操作。
注意: 如果这是一个微不足道的问题,我会提前道歉。
答案 0 :(得分:3)
Memorization可以是限制计算所需的递归调用次数的有效方法。尝试评估一些简单输入的函数,例如g(2, 8)
,您将看到最终一遍又一遍地评估相同值的函数。通过在第一次计算时为每组输入缓存结果,可以使递归短路并显着减小问题的大小。
要使函数迭代而不是递归,您可以使用的一种策略是尝试转换定义并从下往上迭代。考虑Fibonacci函数:
fib(n) = fib(n-1) + fib(n-2)
要迭代计算fib(n),我们从基本案例fib(1) + fib(0)
开始,并迭代到fib(n)
。这使您可以随时累积值,而不必在计算中间值时记住您的位置(一遍又一遍)。所以fib()
的迭代定义如下:
fib(n) {
a = 1;
b = 0;
fib = 0;
i = 1;
while (i < n) {
fib = a + b;
b = a;
a = fib;
i++;
}
return fib;
}
您应该可以使用g()
功能执行类似的操作。我没有时间玩它,但我敢打赌,如果您尝试手动评估一些a, x
对,您会注意到一种模式,让您重写我以上面对fib()
所做的方式以迭代形式运行。
答案 1 :(得分:1)
正如许多人已经说过的那样,这不能直接转换为尾递归或迭代函数,因为浮点函数参数使迭代构建结果变得困难。但是,通过一些思考,可以非常有效地计算函数。
首先,因为所有递归都是求和的并以1结尾,所以该函数基本上计算了递归结束的路径数。例如,对于g(5,2),一条路径将是g(2,5) - > g(2,3) - > g(2,1)(这里返回1)和另一路径g(5,2) - &gt; g(4,2) - &gt; g(3,2) - &gt; g(2,2) - &gt;克(0,2)。因此,为了计算g,我们需要计算可能路径的数量。
让我们从我们总是减去x的路径开始。显然,我们只有一条这样的道路。接下来,考虑我们一旦选择减去1的路径和减去a的其他时间的情况,我们有楼层((xa)/ a)位置来选择1.因此,有楼层((xa)/ a )在这种情况下可能的路径。在下一次迭代中,我们想要选择步骤2两次。存在n *(n-1)/ 2组合,其中n = floor((x-1-a)/ a)并且n *(n-1)/ 2是二项式系数\ binom {n,2}。下一步有三个,有\ binom {n,3}组合,其中n现在= floor((x-2-a)/ a)等。
如果预先计算二项式系数,则算法为O(x),因此它也可能计算g(sqrt(10000019),10000019)。但是,最大的本机c ++整数类型(unsigned long long)溢出已经在g(sqrt(500),500)附近。您可以使用long double来获得稍大输入的近似值。或者你可以使用boost Multiprecision Library来获得更多的数字,但我猜你在得到g(sqrt(10000019),10000019)的答案之前会耗尽内存。
带溢出检查的源代码来计算g()
#include <iostream>
#include <vector>
#include <limits>
#include <algorithm>
#include <cstdlib>
unsigned long long binomial(unsigned int n, unsigned int m) {
if (n - m < m) m = n - m;
std::vector<unsigned long long>bc(m+1, 0);
bc[0] = 1;
for (unsigned int i = 0;i <= n;++i) {
for (unsigned int j = std::min(i, m);j > 0;j--) {
if (std::numeric_limits<unsigned long long>::max()-bc[j-1] < bc[j]) {
std::cout << "Error: too large binomial coefficient(" << n << "," << m << ")" << std::endl;
exit(1);
}
bc[j] += bc[j - 1];
}
}
return bc[m];
}
unsigned long long g(double a, double x) {
unsigned long long r = 1;
int n = 0;
for (int i = static_cast<int>(x);i >= a;--i) {
++n;
int m = static_cast<int>((i - a) / a);
unsigned long long b = binomial(m + n, n);
if (std::numeric_limits<unsigned long long>::max() - b < r) {
std::cout << "Error: too large sum " << b << "+" << r << std::endl;
exit(1);
}
r += b;
}
return r;
}
int main(){
std::cout << g(sqrt(90), 90) << std::endl;
std::cout << g(sqrt(10000019), 10000019) << std::endl;
}
答案 2 :(得分:0)
我想仅供参考,这里的实现没有递归。我不确定它是否会以给定的输入完成:
#include <stack>
double gnr(double a, double x) {
std::stack<double> stack;
double result = 0;
stack.push(x);
while (!stack.empty()) {
x = stack.top();
stack.pop();
//cout << stack.size() << " " << x << endl;
if (x < a) {
result++;
} else {
stack.push(x - 1);
stack.push(x - a);
}
}
return result;
}