算法其实很有趣之——穷举法、递推、递归、分治、概率(算法需有通用性)

穷举法

鸡兔同笼问题:今有鸡兔同笼,上有35头,下有94足,问鸡兔各几何?


这个问题曾经我的一个商人朋友跟我讲起过,像大多数人一样,我从数学的角度出发,设鸡有 x 只,兔有 y 只, x + y = 35 并且 2*x + 4*y = 94,正当我忙于计算出结

果的时候,我的一位商人同学跟我说鸡有 23 只,兔有 12只。对于计算的速度让我感到惊讶,然后我就问他,你怎么算这么快?这时,他一本正经的跟我说,你们这些读书

人,脑子都被固化了,思维形成了一种固定的模式,我们生意人就不会这么想。我好奇地问:那你们怎么想?他继续说:我们假设这些动物都训练有素,这是我吹了一下口哨

,然后,所有动物都抬起一只脚,这时鸡1只脚站立,兔子3只脚站立,那就减少了35只脚,这时我再次吹一次口哨,假想这时鸡腾空了,兔子还有2只脚站立在地上,这时还剩

下 94 - 2*35 = 24 只,而兔子只有两只脚站立地上, 24/2 = 12 就是兔子的只数, 35 - 12 = 23 不就是鸡的只数了吗!!!听完后,我恍然大悟,赞叹道:不愧是商人啊!



加入这就是一道实际的应用题,我们也不用太多的追究其解法和算法,可作为一名程序员我们需要知道和了解这样一道问题,如何用代码的形式表现出来,这才是我们追逐的点。

那就让我们一起来看看吧!

#include "stdafx.h"
#include <iostream>
using namespace std;

int qiongju(int headNum, int footNum, int* chicken, int* rabbit)
{
	int result = 0;
	// i 代表鸡的 数量        j 代表兔子的数量
	for (int i = 0; i <= headNum; i++) 
	{
		for (int j = 0; j <= headNum; j++)
		{
			if ( (i+j == headNum) && (2*i + 4*j == footNum) )
			{
				result = 1;
				*chicken = i;
				*rabbit = j;
			}
		}
	}
	return result;
}

int _tmain(int argc, _TCHAR* argv[])
{
	system("color a");
	int headNum = 0;
	int footNum = 0;
	int chicken = 0;
	int rabbit = 0;

	printf("请输入兔子和鸡 头 的总数:");
	scanf("%d", &headNum);
	printf("请输入兔子和鸡 脚 的总数:");
	scanf("%d", &footNum);
	int result = qiongju(headNum, footNum, &chicken, &rabbit);

	if (result)
	{
		printf("鸡的数量是%d只, 兔子的数量是%d只\n", chicken, rabbit);
	}
	else
	{
		printf("此题无解\n");
	}
	return 0;
}





递推算法思想:根据已有的数据和关系,逐步推到而得到结果。其执行过程如下
1>根据已知结果和关系,求解中间结果。
2>判断是否达到要求,如果没有达到,则继续根据已知结果和关系求解中间结果。如果满足要求,则表示寻找到一个正确答案。


示例:如果一对两个月大的兔子以后每一个月可以生一对小兔子,而一对新生的兔子出生两个月后才可以生小兔子。也就是说,
1月份出生,3月份才可以产仔。那么假定一年内没有产生兔子死亡事件,那么1年后共有多少对兔子?


递推算法:问题分析
第一个月:1对兔子
第二个月:1对兔子
第三个月:2对兔子
第四个月:3对兔子
第五个月:5对兔子
……
∴ 第n个月兔子总数 F(n) = F(n-2) + F(n-1)    注:()内信息为F的下标。


这个问题属于斐波那契数列问题,所以根据上面的分析我们可以写一个算法。

int Fibonacci(int n)
{
	if (n==1 || n==2)
	{
		return 1; 
	}
	else
	{
		return (Fibonacci(n-1) + Fibonacci(n-2));
	}
}

通过测试我们可以知道:当参数 n 的值越大,所消耗的时间也就明显增大,通过一个变量我们也可以记录函数执行的次数来观察这个
函数的执行过程。使用算法要考虑效率问题,具体问题还得具体分析。






递归算法基本思想:调用自身

分类:
直接递归:在函数中调用自身
间接递归:在函数中调用另外一个函数,然后在另外一个函数中再调用该函数。用得不多。
注:编写递归函数时,必须使用if语句强制函数在未执行递归调用前返回。否则,在调用函数后,它永远不会返回,这是一个很容易犯
的错误。

优点:程序代码简洁,可读性更好。
缺点:大部分递归过程没有明显地减少代码规模和节省内存空间。附加的函数调用增加了时间开销,所以递归函数比非递归函数运行速
度要慢一些,例如执行一系列的压栈出栈操作。在此要特别注意堆栈溢出问题。

通过一个简单的实例让我们来了解一下递归的应用:
求 n 的阶乘问题:
long fact(int n)
{
	if (n<=1)
		return 1;
	else
		return n*fact(n-1);
}





分治算法基本思想:将一个计算复杂的问题分为规模较小,计算简单的小问题求解,然后综合各个小问题,得到最终问题的答案。
执行过程:
1>对于一个规模为 N 的问题,若该问题可以容易地解决(比如说规模 N 较小),则直接解决,否则执行下面的步骤。
2>将该问题分解为 M 个规模较小的子问题,这些子问题互相独立,并且与原问题形式相同。
3>递归的解子问题。
4>然后,将各子问题的解合并到原问题的解。

分治算法示例:
问题的提出:
一个袋子里有 30 个硬币,其中一枚是假币,并且假币和真币一模一样,肉眼很难分辨,目前只知道假币比真币重量轻一点。
请问如何区分出假币?

问题分析:
1>首先为每个银币编号,然后将所有的银币等分为两份,放在天平的两边。这样就将区分 30 个硬币的问题,变为区分两堆硬币的问题。
2>因为假银币的分量较轻,因此天平较轻的一侧中一定包含假银币。
3>再将较轻的一侧的硬银币分为两份,重复上述做法。
4>直到剩下 2 枚银币,可用天平直接找出假银币来。

问题解决:

#include "stdafx.h"
#include <iostream>
using namespace std;


#define MAXNUM 30
/*
 *		para: coinWeigth 硬币重量数组		 low 寻找的起始硬币编号		high 寻找的结束硬币编号
*/
int FalseCoin(int coinWeigth[], int low, int high)
{
	int sum1, sum2, sum3;
	int re;
	sum1 = sum2 = sum3 = 0;
	if (low+1 == high)
	{
		if (coinWeigth[low] < coinWeigth[high])
			re = low + 1;
		else
			re = high + 1;
		return re;
	}

	if ( (high - low + 1) % 2 == 0 )			// coin number is even number(偶数)
	{
		for (int i = low; i <= low+(high-low)/2; i++)
		{
			sum1 = sum1 + coinWeigth[i];
		}
		for (int i = low+(high-low)/2; i <= high; i++)
		{
			sum2 = sum2 + coinWeigth[i];
		}
		if (sum1 > sum2)
		{
			re = FalseCoin(coinWeigth, (high-low)/2+1, high);
			return re;
		}
		else 
		{
			re = FalseCoin(coinWeigth, low, low+(high-low)/2);
			return re;
		}
	}
	else										// coin number is odd number(奇数)
	{
		for (int i = low; i <= low+(high-low)/2-1; i++)
		{
			sum1 = sum1 + coinWeigth[i];
		}
		for (int i = low+(high-low)/2+1; i <= high; i++)
		{
			sum2 = sum2 + coinWeigth[i];
		}
		sum3 = coinWeigth[low+(high-low)/2];
		if (sum1 > sum2)
		{
			re = FalseCoin(coinWeigth, (high-low)/2+1, high);
			return re;
		}
		else 
		{
			re = FalseCoin(coinWeigth, low, low+(high-low)/2-1);
			return re;
		}

		if (sum1 + sum3 == sum2 + sum3)
		{
			re = low + (high - low) / 2 + 1;
			return re;
		}
	}
}

int _tmain(int argc, _TCHAR* argv[])
{
	system("color a");
	
	int coinWeight[MAXNUM];
	int coinNum;
	int pos;
	cout<<"分治算法求解假币问题!"<<endl;
	cout<<"请输入银币总的个数:";
	scanf("%d", &coinNum);
	cout<<"请输入银币的真假:";
	for (int i = 0; i < coinNum; i++)
	{
		scanf("%d", &coinWeight[i]);
	}
	pos = FalseCoin(coinWeight, 0, coinNum-1);
	cout<<"在上述"<<coinNum<<"个银币中,第"<<pos<<"个银币是假币!"<<endl;

	return 0;
}



概率算法

对于概率算法呢,我个人认为适当的了解一下就可以了

蒙特卡罗算法计算 π 示例:(不了解的话,可以去百度百科熟悉一下,这里暂时只给出代码示例)


#include "stdafx.h"
#include <iostream>
#include <time.h>
using namespace std;

double MontePI(int n)					// 蒙特卡罗算法
{
	double PI;
	double x, y;
	int sum = 0;

	srand(time(NULL));
	for (int i = 0; i < n; i++)
	{
		x = (double)rand()/RAND_MAX;	// 产生0~1之间的随机数
		y = (double)rand()/RAND_MAX;	// 产生0~1之间的随机数
		if ( (x * x + y * y) <= 1)
		{
			sum++;
		}
	}
	PI = 4.0*sum/n;					// 计算 PI
	return PI;
}

int _tmain(int argc, _TCHAR* argv[])
{
	system("color a");
	
	int n;
	double PI;
	printf("蒙特卡罗概率算法计算 π:\n");
	printf("输入点的数量:");
	scanf("%d", &n);
	PI = MontePI(n);
	printf("PI=%f\n", PI);

	return 0;
}


  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值