欧拉计划第14题

Longest Collatz sequence

The following iterative sequence is defined for the set of positive integers:

n→n/2 (n is even)
n→3 * n+1 (n is odd)

Using the rule above and starting with 13, we generate the following sequence:

13→40→20→10→5→16→8→4→2→1

It can be seen that this sequence (starting at 13 and finishing at 1) contains 10 terms. Although it has not been proved yet (Collatz Problem), it is thought that all starting numbers finish at 1.

Which starting number, under one million, produces the longest chain?

NOTE: Once the chain starts the terms are allowed to go above one million. 

最长考拉兹序列

考虑如下定义在正整数集上的迭代规则:

n→n / 2 (若n为偶数)
n→3 * n + 1 (若n为奇数)

从13开始,可以迭代生成如下的序列:

13→40→20→10→5→16→8→4→2→1

可以看出这个序列(从13开始到1结束)共有10项。尽管还未被证明,但普遍认为,从任何数开始最终都能抵达1并结束(这被称为“考拉兹猜想”)。

在小于一百万的数中,从哪个数开始迭代生成的序列最长?

注: 在迭代过程中允许出现超过一百万的项。

         通过对题的理解,这个题就是一个数通过对应的奇数和偶数的处理方式直到变为1需要多少少次这个过程;这个题肯定第一反应就是递归。

        递归是什么?就是通过函数,调用自身,直到达到结束条件,进行一步一步的往回返回,到最初进入函数的时候在返回给调用函数的过程;就相当于把一个大的问题转化为一个小的问题进行一步一步的处理;如果没有学过递归,可以去看看斐波那契数列或者爬楼梯的问题进行用刚刚说的函数调用自身来实现,然后去理解那个过程。

        下面是代码实现,我会把递归过程的每一条代码进行解释:

#include <stdio.h>
#define MAX_N 1000000

long long getlength(long long x) {//这个函数就是求一个数需要经历多少次变为1
    if (x == 1) return 1;//这个就是递归过程结束条件
    if (x & 1) return getlength(x * 3 + 1) + 1;//当x为奇数时,进行对应题目的转化在调用函数,再对步数加一
    else return getlength(x / 2) + 1;//和第二句代码是一样的,进行对应题目要求转化再调用函数,再对步数加一
}


int main() {
    int ans = 0, num = 0;//ans记录当前最大步数,num记录最大步数的数
    for (int i = 2; i <= MAX_N; i++) {
        int len = getlength(i);//求对应数的步数
        if (len > ans) {
            ans = len;
            num = i;
        }
    }
    printf("%d %d\n", ans, num);
    return 0;
}

         上个方法,我在我主机上跑的时间是1.3秒,我觉得一个普通的这种程序需要跑1.3秒那确实这个程序就写的比较差,那如何去改进呢?

        比如2->1需要一步,而4->2->1需要两步,但是你会发现,在求4时会经过2,那就相当于再经历2的过程就求了两次;所有有什么方法可以记录下来,求过一次数的步数呢?

        很简单就是通过数组,你把数带入数组他会返回一个数,而你把一个数带进函数他也会返回你一个结果,你会发现数组和函数有相似的地方,而调用函数肯定比用数组的时间会多,因为数组的时间复杂度就相当于O(1);通过这个过程的理解,函数变为数组的过程,就称为空间换时间;反过来就是时间换空间。

        通过对于上面的这些过程的理解,你也会有思路了但是代码实现还有点困难,没关系可以看看我的代码,然后用你自己的思路去实现代码:

#include <stdio.h>
#define MAX_N 1000000
int cnt[MAX_N + 5];

long long getlength(long long x) {//这个函数就是求一个数需要经历多少次变为1
    if (x == 1) return 1;//这个就是递归过程结束条件
    if (x <= MAX_N && cnt[x]) return cnt[x];//如果x小于数组最大值,并且数组里面有值就返回数组里面记录的值  
    int temp;
    if (x & 1) temp = getlength(3 * x + 1) + 1;
    else temp = getlength(x / 2) + 1;
    if (x <= MAX_N) cnt[x] = temp;//如果x小于数组最大值,就用数组记录当前步数;
    return temp;
}


int main() {
    int ans = 0, num = 0;//ans记录当前最大步数,num记录最大步数的数
    for (int i = 2; i <= MAX_N; i++) {
        int len = getlength(i);//求对应数的步数
        if (len > ans) {
            ans = len;
            num = i;
        }
    }
    printf("%d %d\n", ans, num);
    return 0;
}

而这代码运行时间只有0.03s;

 最终答案为:837799 步数为525

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

初猿°

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值