3n + 1的递归解决方案

时间:2015-01-04 22:50:56

标签: c++ algorithm dynamic-programming

我只想做简单的递归。 对于一个数字,如果它是偶数那么它将接近(数字/ 2)并且如果奇数然后接近(3 *数字+ 1)。到达1时会发生多少次。

为10
  10→ 5→ 16-> 8-> 4 - > 2 - > 1
  总进程6

long arr[10000];
long dp(int n)  
{ 
    if(n==2|| n==1) return 1;
    if(arr[n]) return arr[n];

    if(n%2==0) return arr[n]=1+dp(n/2);
    else if(n%2==1) return arr[n]=1+dp(3*n+1);
    return arr[n];
}

我已经创建了这样的函数,对于像999或907这样的输入会导致分段错误 我想知道为什么?

如果我增加数组大小,那么它的输出正确。
我也想知道为什么呢?

为什么它取决于数组大小,因为我把它作为数组元素数据类型,所以它应该正确输出那些输入?

5 个答案:

答案 0 :(得分:3)

999,您到达11392

907,您到达13120

这些数字不受限制。

Live example

答案 1 :(得分:1)

您正在为数组编制索引

  • 对于您正在使用的输入,变量n在执行期间将超过数组大小10000。这会导致程序访问超出其边界的内存,从而导致分段错误。

  • 如果您尝试memoize该功能,我建议使用std::vector而不是固定数组。可以动态调整向量的大小,避免索引超出范围的问题。但是,对于某些输入,可能需要非常大量的内存(可能需要几千兆字节,因此请考虑编译为64位二进制文​​件),否则程序可能无法分配内存并完全崩溃。

  • 更先进的记忆技术将std::map用作键值存储,允许有效存储和检索输入及其相关输出。使用映射会大大减少所需的内存和执行时间,因为它可以避免为未使用的输入分配内存。

使用vector

实现
#include <iostream>
#include <vector>

using namespace std;

long long dp(unsigned long long);

int main() {
    unsigned long long n;

    while(true) {
        cout << "Enter a number, or 0 to exit: ";
        cin >> n;

        if(!cin) {
            cin.clear();
            cin.ignore(numeric_limits<streamsize>::max(), '\n');
            cerr << "Invalid input, please try again." << endl;
            continue;
        }

        if(n == 0)
            return 0;
        else
            cout << dp(n) << endl;
    }

    return 0; // Unreachable
}


long long dp(unsigned long long n) {
    static vector<long long> arr;

    if(arr.size() <= n)
        arr.resize(n + 1);

    if(n == 2 || n == 1) return 1;
    if(arr[n]) return arr[n];

    if(n % 2 == 0) return arr[n] = 1 + dp(n / 2);
    else if(n % 2 == 1) return arr[n] = 1 + dp(3 * n + 1);
    return arr[n];
}

使用地图

实施
#include <iostream>
#include <map>

using namespace std;

long long dp(unsigned long long);

int main() {
    unsigned long long n;

    while(true) {
        cout << "Enter a number, or 0 to exit: ";
        cin >> n;

        if(!cin) {
            cin.clear();
            cin.ignore(numeric_limits<streamsize>::max(), '\n');
            cerr << "Invalid input, please try again." << endl;
            continue;
        }

        if(n == 0)
            return 0;
        else
            cout << dp(n) << endl;
    }

    return 0; // Unreachable
}


long long dp(unsigned long long n) {
    static map<long long, long long> memo;

    if(n == 2 || n == 1) return 1;
    if(memo[n]) return memo[n];

    if(n % 2 == 0) return memo[n] = 1 + dp(n / 2);
    else if(n % 2 == 1) return memo[n] = 1 + dp(3 * n + 1);
    return memo[n];
}

答案 2 :(得分:0)

分段错误来自数据溢出。你怎么做以下的事情?

void rec(int n, int* steps) {
  ++(*steps);
  printf("%d\n", n);
  // termination step if 'n' equals 1
  if(n == 1)
    return;
  if (n % 2 == 0)
    rec(n/2, steps);
  else
    rec(3*n+1, steps);
}

int main(void) {
  int steps = 0;
  rec(10, &steps);
  printf("Steps = %d\n", steps);
  return 0;
}

输出:

10
5
16
8
4
2
1
Steps = 7

此处提供的代码(当然)与Jarod的answer达成一致。

答案 3 :(得分:-1)

由于序列值超过9999,您正在溢出数组。

答案 4 :(得分:-1)

如果您只想知道所需的进程数,则不应使用数组存储。

int numProcesses = 0;

int hailstone(int n) {

    if (n == 1) {
        return 1;
    } // base case

    ++numProcesses; // if it wasn't the base case the method will do some work so increment numProcesses

    if (n % 2 == 0) {
        return hailstone(n / 2);
    } // n is even
    else {
        return hailstone(3 * n + 1);
    } // n is odd
}

这是未经测试但我认为它应该有效,并且在它最终返回后numProcesses应该等于调用该方法的次数(只要它没有使用参数1调用)。