有一个经典的小学数学题,小孩买糖。形式很多,我举一个粒子。
(资料图)
小孩有10块钱。糖1块钱1颗。每3张糖纸可以兑换1颗糖。问小孩能吃到多少糖。
引导小学生做题时,要一步一步来。
第一步。花10块钱买10颗糖,吃完了剩10张糖纸。
第二步。10张糖纸拿出9张换3颗糖,吃完了剩4张糖纸。
第三步。4张糖纸拿出3张换1颗糖,吃完了剩2张糖纸。一共吃了14颗糖。
一般到这里就可以结束了,但是比较聪明的小孩的话会有下一步。
第四步。问店老板借1张糖纸,和原本持有的2张糖纸一起换1颗糖。吃完了糖把糖纸还给店老板。这样一共是15颗糖。
能想到第四步的,也就接近了这道题的本质。1块钱买的糖里其实包含了1颗糖果和张糖纸。因为糖纸可以兑换成糖果,所以糖纸可以看成是找零。然后就可以去计算真正的糖果的价值。
为了方便书写,下面糖果用c表示,糖纸用s表示。
2个未知数就需要2个独立的方程。
首先根据定价,可以写出第一个方程。
然后根据糖纸的兑换规则,写出第二个方程。
然后就可以解出,c=2/3,s=1/3。
也就是说糖果的单价是2/3块钱,那么10块钱自然就能得到15个糖果。
所以,这题的本质是一个二元一次方程组的问题。可以将它一般化。
假设糖的单价是p,每n张糖纸可以兑换1颗糖。拥有的钱是m 。能吃到的糖果是d颗。
其中,p和m>0 ,而且为了符合实际情况,p和m应该是整数或有限小数。而n需要大于1的整数(否则就得到无穷多的糖果)。
当然,如果d的计算结果不是整数,需要向下取整。
像这样的问题,具体化的可以作为小学的思考题,一般化的可以作为初中的普通题。
不过,这种一般化的计算会存在一个漏洞,后面会讲到。
如果将问题再复杂化一些,就是小孩买啤酒问题。
大概是这样:
家里来客人了,爸爸差遣儿子去买些啤酒来招待,给了他m块钱。儿子到了店里,看到啤酒p块钱1瓶。并且i个酒瓶能换1瓶啤酒,j个瓶盖也能换1瓶啤酒。那么孝顺的儿子能让爸爸和客人们喝到多少啤酒?
因为啤酒=酒+酒瓶+瓶盖,所以本质和买糖问题一样,只是未知数多了一个,是个三元一次方程组问题。
我们定义酒的单价是b,酒瓶的单价是g,瓶盖的单价是c
根据啤酒的单价和兑换规则就能写出3个独立的方程:
酒的总量L就是:
然后我们看一个实例。假如钱是20块,啤酒4块钱1瓶,3个酒瓶换1瓶啤酒,6个瓶盖换1瓶啤酒。代入计算可得10瓶酒。
但是如果实际推演一下的话,就会出现漏洞。
第一步。花完29块钱买到5瓶啤酒。此时得到5酒,剩余5瓶+5盖。
第二步。用3个酒瓶换1瓶啤酒。此时得到6酒,剩余3瓶+6盖。
第三步。用3个瓶盖+6个瓶盖换2瓶啤酒。此时得到8酒,剩余2瓶+2盖。
第四步。借1个酒瓶,与持有的2个酒瓶一起换1瓶啤酒,然后归还1个酒瓶。此时得到9酒,剩余0瓶+3盖。
到这里似乎已经停止了,那么第10瓶酒如何来呢?这里就是漏洞所在,因为之前设计规则的时候没有排除“组合付款”的情况。
比如在这个实例里,酒瓶的价值是瓶盖的2倍。所以如果去借1个酒瓶+1个瓶盖之后,1瓶+4盖的价值在数学上与3瓶或6盖相当,可以“组合付款”兑换1瓶啤酒,然后归还1个酒瓶+1个瓶盖。
上面的买糖问题也一样,可能会出现现金+糖纸的组合付款方式。
但是我想现实世界中绝大多数商家都不会允许这种兑换方式。
那么如果考虑禁止组合付款的情况该如何一般化呢?
还是用上面买啤酒的例子。假如最后能得到l瓶啤酒,那么能够用来交换啤酒的有m块钱,L个酒瓶和L个瓶盖。并且这3种交换相互独立,于是就有:
成立。
int(x)是excel中向下取整的函数,这里拿来套用。
因为有向下取整的存在,不是很好计算。但是l的范围还是很容易确定的。L的值至少是只用钱能买到的量,但最大不超过允许组合付款时的量。也就是Lmin和Lmax是很好计算的。限定范围以后,因为l值会比较接近Lmax,所以从Lmax开始逐个尝试就可以了。
没错,本质上这还是在推演,只不过从顺推变成倒推。而且,步数大大下降了。
我写了一个可以在excel上运行的VBA代码来实现这个推演。用的具体数值就是上面案例里的。运行结果为9 。
Sub buybeer()
Dim m As Double
Dim i As Double
Dim j As Double
Dim p As Double
Dim L As Long
Dim Lmax As Long
Dim Lmin As Long
‘这里定义计算需要的变量
m = 20
i = 3
j = 6
p = 4
‘设置初始变量
Lmin = Int(m / p)
Lmax = Int(m / p / (1 - 1 / i - 1 / j))
‘计算上下限
For L = Lmax To Lmin Step -1
If Int(m / p) + Int(L / i) + Int(L / j) = L Then
Exit For
End If
Next L
‘从最大值开始,检查到满足规则的值时跳出的for循环
Cells(1, 1).Value = L
‘将结果输出到A1单元格
End Sub