Project Euler | Problem 014

Problem 14


Longest Collatz sequence

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→3n+1 (若n为奇数)

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

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

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

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

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


首先,最容易想到的是暴力运算 

/*
*	Program 1
* 思路:
*	1.逐个计算,暴力求解
* 收获:
*	1.1.位运算取代四则运算
* 评价:
*	优点:
*		1.代码量小
*	缺点:
*		1.运行时间长,运算量高
*/

#define _CRT_SECURE_NO_WARNINGS
#define UPPER_LIMIT 1000000

#include <stdio.h>

int GetCollatzsequence(int num)
{
	int count = 1;
	while (num != 1)
	{
		if (num & 1)// num为奇数
		{
			num = 3 * num + 1;
		}
		else// num为偶数
		{
			num >>= 1; // 收获1.1
		}
		count++;
	}
	return count;
}

int main()
{
	unsigned int aim = 1;
	unsigned int length = 0;
	unsigned int intest = 0;
	for (unsigned int i = 1; i <= UPPER_LIMIT; i++)
	{
		length = GetCollatzsequence(i);
		if (intest < length)
		{
			intest = length;
			aim = i;
		}
	}
	printf("%u\n", aim);
	return 0;
}

显然,上述暴力运算不可取,于是引入日志辅助运算。

/*
*	Program 2
* 思路:
*	1.鉴于存在大量重复计算,因此创建一个数组用于记录计算结果
*	2.用空间换效率
* 收获:
*	2.1.运用日志,提高运算效率
*	2.2.高数量级的递归将导致栈溢出
* 评价:
*	优点:
*		1.运用日志,用空间换效率
*		2.考虑到迭代生成的数字可能突破 int类型承载上限
*	缺点:
*		1.循环,代码重复,不够简洁
*		2.递归有栈溢出的风险
*		3.如果日志进一步扩大,会提高效率,但会相应地占用更多空间
*/

#define _CRT_SECURE_NO_WARNINGS
#define UPPER_LIMIT 1000000

#include <stdio.h>
#include <assert.h>

//日志,用于记录计算历史
int log[UPPER_LIMIT] = { 0,1 }; //收获2.1

用循环的方式实现计算考拉兹序列长度

//用循环的方式实现计算考拉兹序列长度
unsigned int GetCollatzSequence(unsigned int num)
{
	int count = 0;
	unsigned int tmp = num;
	//计算
	while (tmp)
	{
		if (tmp < UPPER_LIMIT && log[tmp])
		{
			count += log[tmp];
			break;
		}
		else
		{
			count++;
			if (tmp % 2)// num是奇数
			{
				tmp = tmp * 3 + 1;
			}
			else        // num是偶数
			{
				tmp /= 2;
			}
		}
	}
	//重新迭代并录入
	tmp = count;
	while (num)
	{
		if (num < UPPER_LIMIT && log[num])
		{
			break;
		}
		else
		{
			if (num < UPPER_LIMIT)
			{
				log[num] = tmp;
			}
			if (num % 2)
			{
				num = num * 3 + 1;
			}
			else
			{
				num /= 2;
			}
			tmp--;
		}
	}
	return count;
}

用递归的方式实现计算考拉兹序列长度

//运用递归的方式计算考拉兹序列长度
//注意:递归使用过多将导致栈溢出
unsigned int GetCollatzSequence(unsigned int num)
{
	assert(log);
	unsigned int count = 0;
	if (num < UPPER_LIMIT && log[num])//如果已记录,直接使用
	{
		count += log[num];
	}
	else if (num <= 0)
	{
		return 0;
	}
	else //如果未记录,算,然后记下来
	{
		if (num % 2) // num是奇数
		{
			count = 1 + GetCollatzSequence(num * 3 + 1);
		}
		else         // num是偶数
		{
			count = 1 + GetCollatzSequence(num / 2);
		}
		if (num < UPPER_LIMIT)//防止数组越界访问
		{
			log[num] = count;
		}
	}
	return count;
}

int main()
{
	unsigned int length = 0;
	unsigned int longest = 0;
	int aim = 0;
	for (int i = 0; i < UPPER_LIMIT; i++)
	{
		length = GetCollatzSequence(i);
		if (longest < length)
		{
			longest = length;
			aim = i;
		}
	}
	printf("%d\n", aim);
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值