14天阅读打卡-03-一棋盘的麦子

 14天阅读挑战赛,努力是为了不平庸

        通过前面的内容我们已经知道了,什么是算法,以及算法的复杂性、如何比较两个算法的好坏,那么今天我们我们就从一道真正的算法题目入手,来实践它吧。有位伟人曾经说过,实践出真知。这个题目叫做一棋盘的麦子,它是一道经典的算法题,来源于一个著名的故事:

        有一个古老的传说,一位国王的女儿不幸落水,水中有很多鳄鱼,国王情急之下下令:“ 如果有谁能够活着把公主救上来,那么就把女儿嫁给他。 ” 但是很多人看着水里的鳄鱼还是选择纷纷退让,就在这时,有一个年轻的勇士挺身而出,冒着生命危险把公主救了上来,但是国王看到此人是个穷小子,家里一清二白的,就想反悔,不把女儿嫁给他,于是故意说:“ 除了女儿,你要什么都可以。 ”勇士说:“ 好吧我只要一棋盘的麦子。您在第 1 个格子里放上 1 粒麦子,在第 2 个格子里放上放上 2 粒,在第 3 个格子里放上 4 粒,在第 4 个格子里放 8 粒。以此类推,每一个格子里麦子的粒数都是前一格麦子粒数的两倍。把这 64 个格子放满了就行,我就要这么多。”

                国王一听,这不有手就行,顿时仰天大笑,认为这个人的要求很容易满足,随即满口答应,然后吩咐大臣去照办了。但是后面发现,即使把全国所有的麦子都拿来,也不能够按要求完全放满这 64 个格子。国王非常无奈,因为无法满足勇士的要求,只好把自己的宝贝女儿嫁给了他。

题目解析

        那么你知道如果要按勇士的要求放满棋盘上的 64 个格子一共需要多少粒麦子吗?我们可以来计算一下,首先,把每一个格子里需要放的麦子粒数加起来,其总和为S,则:

S=1+2^{1}+2^{2}+2^{3}+2^{4}+...+2^{63} ①

对 ① 进行处理,两边乘以2,则:

2S=2^{1}+2^{2}+2^{3}+2^{4}+...+2^{64} ②

用式子 ② 减去 ①,得出:

S=2^{64}-1 = 18 446 744 073 709 551 615

据专家统计,每颗麦粒的平均重要约为 41.9 毫克,这些麦粒的总重量则为:

18 446 744 073 709 551 615 *41.9 = 772 918 576 688 430 212 668.5 (毫克)

        大概约等于 7729 000 (亿千克),假设全世界人口按 77 亿计算,那么每个人差不多可以分到 100 吨的麦子,这么多的麦子,吃到世界末日也不一定吃的完

代码实现

C

/** checkerboardGrainProblem.c -- 计算棋盘上的麦粒问题*/
#include <stdio.h>
#include <math.h>

int main(int argc, const char *argv[])
{
	/*
		 程序初始化,因为数据量比较大,所以
		 使用无符号的 long long int 类型,
		 定义 i 用于遍历每个格子,sum 用于记录总的麦粒数
	*/
	unsigned long long int i, sum = 0L;
	for (i = 1L; i <= 64L; i++)
	{
		// 循环计算每个格子上的麦粒数,并累加
		unsigned long long int r = pow(2L, i - 1L);
		sum += r;
	}
	// 打印输出计算得到的麦粒数
	printf("%llu\n", sum);

	return 0;
}

Java

/**
 * 计算棋盘上的麦粒问题
 */
public class CheckerboardGrainProblem {
	public static void main(String[] args) {
		// 初始化 sum 为 0
		BigDecimal sum = new BigDecimal("0.0");
		// 循环计算每个格子上的麦粒数并相加
		for (double i = 1.0D; i <= 64.0D; i++) {
			sum = sum.add(BigDecimal.valueOf(Math.pow(2.0D, i - 1D)));
		}
		System.out.println("sum = " + sum);
	}
}

这是一种最简单的解法,但是时间复杂度相对较高,所有还有一种更好的解法:

C

/** checkerboardGrainProblem.c -- 计算棋盘上的麦粒问题*/
#include <stdio.h>
#include <math.h>

int main(int argc, const char *argv[])
{
	// 计算并输出总和
	printf("sum = %.0f\n", pow(2.0d, 64.0d) - 1.0d);
	return 0;
}

Java

/**
 * 计算棋盘上的麦粒问题
 */
public class CheckerboardGrainProblem {
	public static void main(String[] args) {
		// 计算并输出总和
        System.out.println("sum = " + BigDecimal.valueOf(Math.pow(2.0D, 64.0D) - 1.0D));
	}
}

        如果使用循环处理上面的题目需要大量的计算,我们称这样的函数为爆炸式增量函数。想一想,如果算法的复杂度是 o(2^{n}) 会怎么样?想也知道,这样的程序很有可能会爆掉内存。就像我们经常看见有些算法本地调试没有问题,运行一段时间也还好,但是就是在关键的时候宕机(shutdown)。

        所以在设计算法的时候,我们需要考虑到时间复杂度的问题,哪种算法更适合当前场景,尽可能优化代码,实现最佳的时间复杂度。关于什么是算法的复杂性?如何对算法的性能进行比较?如何对算法的复杂度进行分析?这些问题可以参考我上一篇文章:14天阅读打卡-02-算法的复杂性

往期打卡

技术文章

1、14天阅读打卡-01-初识算法

2、14天阅读打卡-02-算法的复杂性

3、14天阅读打卡-03-一棋盘的麦子

4、14天阅读打卡-04-斐波那契数列

冷知识

      因为一切原因或意外而导致的死机,一些数据库服务器的死锁,服务器的某些服务停止运行而导致计算机无法正常工作,都可以称为宕机

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值