欧拉计划 第十四题

本文探讨了Collatz问题中寻找小于一百万的起始数所生成的最长序列的方法。介绍了两种算法实现方式:递归求解法和记忆化求解法,并对比了它们的效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

n → n/2 (n is even)n → 3n + 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个项。虽然尚未证实(Collatz问题),但认为所有起始数字都是1。

哪个起始编号低于一百万,产生最长的链?

注意:一旦链条启动允许超过一百万。

思路
1.递归求解法

(1)求每个数字的数字链长度,考虑边界条件num == 1是求到链的终点,否则返回相应的算式链并+1

(2)从1遍历到max的,不断更新ans和ans_len;

用时:3475ms

#include <stdio.h>
#include <inttypes.h>

#define max 1000000

int clain_len(int64_t num){
	if(num == 1) return 1;
	if(num % 2 == 0){
		return clain_len(num / 2) + 1;
	}else {
		return clain_len(num * 3 + 1) + 1;
	}
}
int main(){
	int ans = 0;
	int ans_len = 0;
	for(int i = 1; i < max; i++){
		int store = clain_len(i);
		if(ans_len < store){
			ans_len = store;
			ans = i;
		}
	}
	printf("%d %d\n", ans_len, ans);
	return 0;
}


2.记忆化求解法

1.记忆化:我们可以发现5->16->8->4->2->1链的长度为6;

10->5->16->8->4->2->1链的长度为7,实际上把链重新遍历了一遍

这里我们建立一个数组keep来存储遍历过的链长,这样再次查找的时候就不用多次递归调用以节省时间

2.初始化keep[keep_max] = 0;当keep[num] 不为0时,我们可以直接返回keep数组里存的链长,为0时我们更新keep

用时:125ms

#include <stdio.h>
#include <inttypes.h>

#define max 1000000
#define keep_max 5000000

int keep[keep_max + 5] = {0};

int clain_len(int64_t num){
	int ans_len = 0;
	if(num == 1) return 1;
	if(num <= keep_max && keep[num]){
		return keep[num];
	}else{
		if(num % 2 == 0){
			ans_len = clain_len(num / 2) + 1;
		}else {
			ans_len = clain_len(num * 3 + 1) + 1;
		}
	}
	if(num < keep_max){
			keep[num] = ans_len;
		}
	return ans_len;
}
int main(){
	int ans = 0;
	int ans_len = 0;
	for(int i = 1; i < max; i++){
		int store = clain_len(i);
		if(ans_len < store){
			ans_len = store;
			ans = i;
		}
	}
	printf("%d %d\n", ans_len, ans);
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值