3n+1 的递归解

Recursive solution for 3n+1

本文关键字:递归 3n+1      更新时间:2023-10-16

我只是喜欢做简单的递归。对于一个数字,如果它是偶数,那么它将接近(数字/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)会导致分段错误。
我想知道为什么?

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

为什么它取决于数组大小,因为我花了很长时间作为数组元素数据类型,所以它应该正确输出这些输入?

有了999,你就可以达到11392

有了907,你达到了13120

这些数字是越界的。

现场示例

您正在为数组编制索引

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

  • 如果你试图记住这个函数,我建议使用std::map而不是固定数组。这是一个关联数组,它存储键值对(在本例中为每个记忆的输入-输出对),并且可以快速检索与给定键关联的值。地图非常适合此应用程序,因为它们可以使用实际需要的内存来存储此数据,并根据需要自动增长。

  • 您也可以使用 std::vector ,但不建议这样做。向量类似于数组,但它们可以动态调整大小,避免索引越界的问题。这种方法的缺点是,对于某些输入,可能需要非常大量的内存,可能是几千兆字节。如果程序编译为 32 位二进制文件而不是 64 位二进制文件,则当程序无法为矢量分配足够的内存时,程序可能会在运行时崩溃。

使用地图实现

#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];
}

使用矢量实现

#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];
}

分段错误来自数组溢出。你做下面这样的事情怎么样?

void rec(int n, int* steps) {
  ++(*steps);
  printf("%dn", 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 = %dn", steps);
  return 0;
}

输出:

10
5
16
8
4
2
1
Steps = 7

这里提供的代码(当然)同意Jarod的答案。

由于序列值超过 9999,导致数组溢出。

如果您只想知道它需要的进程数,则不应使用阵列存储。

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 调用的)。