光明小学六年级选出的男生的1/11和12名女生参加数学竞赛,剩下的男生人数是剩下的女生人数的2倍.已知六年级共有156人,问男、女生各有多少人?
这是一道小学六年级的数学题,大家肯定都会:
设置男生人数为x,女生人数为y,则有两个表达式
x+y=156
(10/11)x=(y-12)*2
(`*`是乘号,x是男生人数不是乘号)
然后一波计算,x=99,y=57
那么,用C#应该怎么写呢?
C#可以使用穷举
的方式,来获得答案。和之前一样,先确定算法:
男生/女生的人数大于等于0,小于等于156。
我们将男生的人数从0递增到156(循环),而将女生人数设置为156-男生人数。
当(10/11)男生人数=(女生人数-12)2的时候,输出结果。
所以代码如下:
for(int boysNumber=0; boysNumber<=156; boysNumber++)
{
var girlsNumber = 156 - boysNumber;
//双等号表示“等于”
//10.0/ 11.0不能改成10/ 11,涉及精度问题
if ((10.0/ 11.0) * boysNumber == ((girlsNumber - 12) * 2))
{
Console.WriteLine($"男生人数为{boysNumber},女生人数为{girlsNumber}");
break;
}
}
当然笨一点可以写两个循环,外层循环男生人数,内层循环女生人数,也可以得到正确答案。
同时,我们需要在if语句中多加了一个条件,“girlsNumber + boysNumber == 156”并且两个条件取交集,即同时满足才能为真。具体可以看C# 运算符中的逻辑运算符
。
for (int boysNumber = 0; boysNumber <= 156; boysNumber++)
{
for(int girlsNumber=0; girlsNumber<=156; girlsNumber++)
{
if ((girlsNumber + boysNumber == 156) &&
(10.0 / 11.0) * boysNumber == ((girlsNumber - 12) * 2))
{
Console.WriteLine($"男生人数为{boysNumber},女生人数为{girlsNumber}");
}
}
}
这样写的效率会比第一种差一些, 有以下几种因素:
1.我们做了两层循环;
2.我们穷举的可能性比第一种算法多;
3.第一种方法在得到一种可能性后立即break停止执行,第二种会一直循环完毕(当然第二种也可以在得到一种可能性后停止循环,我为了不混淆重点没有这样写)。
至此,我们明白了对待有限种可能性的问题,我们可以使用穷举的思想来解决。
类似问题
输出所有的“水仙花数”,所谓“水仙花数”是指一个三位数,其中各位数字立方和等于该数字本身。例如:153是一个“水仙花数”,因为153=111+555+333。
具体的实现,自己尝试写一下。我的示例代码里面可以找到答案。
延展:精度问题
刚刚提到,在C#中,(10.0 / 11.0)和(10/ 11)是不一样的。
我们先讲下在数学、生活中的“10”和“10.0”。“10”和“10.0” 是相等的,看起来也是“一样”的。但实际上,我们认为“10”是整数,“10.0”是小数,这点应该没有问题吧。那么在C#中,“10”和“10.0”是严格区分的,整数就是整数,小数就是小数,不同的类型采用不同的存储方式(小数有多种存储方式,在此不拓展讲)。
以上的观点,就是说明C#是强类型语言
,它的每一个对象/变量都需要一个明确的类型
,类型的资料可以看这里。
举个例子,一个变量被定义为int(整数类型),它就不能再设置为其他类型,比如浮点数,也不能为其赋值小数。
所以(10.0 / 11.0)会按照数学中小数的运算方式,算出一个无限循环小数,并且因为计算机的限制,在最末位四舍五入得到一个接近真正答案的有限小数。(也因为这种运算方式的答案不“精确”,像银行这样的金融机构并不会使用这样的方式计算。)
而(10/ 11)则会根据整型的计算方式,得到答案“0”。整型的计算方式,可以理解为只计算整数部分,不取小数位,不进行四舍五入,可以试试(11/5)、(11/3)的计算。
这个问题初学可能理解有点难,如果想要深入理解,需要理解计算机中整数、小数的多种存储方式,以及“除法”是怎么实现的。比较复杂,我觉得记个答案算了,然后日常写代码需要注意小数的精度问题。另外不同语言在小数、除法的处理上也有差别,这点需要格外注意。