胜兵先胜而后求战,败兵先战而后求胜。 – 孙子兵法
遇到问题
我的女朋友最近生病了,周三下了班就回了家,顺带着还把在公司的一部分工作带了回来。
等我回到家,她已经卧在床上睡一觉了。
过了一会她睡醒了,坐起来跟我说:“工作没做完,你帮我做一下吧。很简单,就是把这张表格里的数字,以10000为单位,上或下取整,填到它下面的格子里。”说罢,就又睡了过去。
怎么计算
我粗略地看了一眼,这是一个excel表格文件,就是下面的这张表格。
简单查看了一下excel的基础知识。这一个一个的格子,叫作“单元格”,哈哈,形象吧。暂时我们只需要这个概念。
这里面有黑色内容的单元格和红色内容的单元格。红色内容的单元格是计算上面的黑色单元格的内容(物料的待收数据)得到的结果。
我们的目标是,输入这个文件,然后让计算机自己经过一系列的计算过程,将表格中的还未计算过的数据按给定的规则计算,将计算结果写到下面一个单元格,并帮我们把处理后表格保存下来。
这个目标实在激动人心!我跟我女朋友确认过,以这次的数据量,如果人工来做,可能需要花费3-4个小时的时间。而对计算机来说,在一分钟内就可以完成,而且可以确保结果的准确性。需要的知识也浅显易懂,如果对相关知识熟练,完全可以在半小时内将我们想要做的事情清楚地告诉计算机。
当然,学习是循序渐进的。在这篇文章中,我们先把目标降低一些:告诉计算机一个待收数量,和一条规则(以10000为单位上取整),让它帮我们计算实际的代收数量。这是我们完成整个目标的第一步,这个技巧也完全可以用在其它类似的工作上。
获取输入
首先,我们需要告诉计算机待收数量。这当然很简单,我们在前面已经说过了。假设待收数量是1234:
1 | 1234 |
但是,这样会显得很仓促:1234代表什么呢?我们现在当然知道,它就是我们要求值的数据。但当我们写入到一个文件时,如何把1234传进去呢?
显然,我们可以直接写到文件,比如:
1 | print 1234 |
而此时,如果再重新输入一个数字时,我们就需要打开文件,然后把1234改为新的数字,保存文件,然后运行。修改代码文件很繁琐,而且是很危险的,这还只是一行简单的代码,想想,如果一个代码文件有上百行,我们如何再找到这个数值的位置?会不会小手一抖,把其它行的数据也修改了而自己并没有意识到?繁琐的操作也必然会降低我们的效率。
这时,我们就自然有这么一个需求。
计算机:您好,我最亲爱的某某某,您的待收数量是多少,我来为您计算实际待收数量。
亲爱的某某某:嗯,1234。
计算机:您实际需要待收的数量是10000。
嗯,这就是人们所说的交互,其实就是让人跟计算机的沟通像人与人的沟通一样顺畅。
这时,我们需要知道这么一个东西:
1 | "请输入您的待收数量:")) int(raw_input( |
注意,里面的第一个1234是我们自己输入的,后面还跟了一个回车。
int(raw_input("请输入您的待收数量:"))
,这么大一坨?没关系,先死记下,在之后的文章我们会进一步了解。我们只需要知道,它让计算机询问我们待收数量。
如何计算取整后的值
我们现在用的python是个死脑筋。为什么这么说呢?如果你使用的python是之前文章中链接的python或是系统默认安装的,试试下面这个:
1 | 5 / 2 |
我们在学校学到的可是5 ÷ 2 = 2.5
,现在却得到个2。谁的问题?
语言的问题。语言是有局限的,而当我们输入5 / 2
时,python解释给计算机说:“请帮我们计算一下5 ÷ 2
的值,取整就可以了,小数点我们就不要了。
语言是在发展的,文章中用的python版本是python2,此时,默认情况下,整数除法的结果是整数,而且是下取整。
现在回到我们的问题:给一个数字,以10000为单位,上取整或下取整。我们假设是上取整,想想应该怎么做呢?
我们把单位缩小,先来看一下以3为单位上取整,我们来取一批数值观察一下。
我们来分别观察一下 待收数量
、实收数量
的值和它们之间的相互关系格式:
- 待收数量:
1
,实收数量:3
,待收数量 ÷ 3后下取整:0
- 待收数量:
2
,实收数量:3
,待收数量 ÷ 3后下取整:0
- 待收数量:
3
,实收数量:3
,待收数量 ÷ 3后下取整:1
- 待收数量:
4
,实收数量:6
,待收数量 ÷ 3后下取整:1
- 待收数量:
5
,实收数量:6
,待收数量 ÷ 3后下取整:1
- 待收数量:
6
,实收数量:6
,待收数量 ÷ 3后下取整:2
- 待收数量:
7
,实收数量:9
,待收数量 ÷ 3后下取整:2
- 待收数量:
8
,实收数量:9
,待收数量 ÷ 3后下取整:2
- 待收数量:
9
,实收数量:9
,待收数量 ÷ 3后下取整:3
- …
我们发现,待收数量 ÷ 3后下取整
的值会随着 实收数量
增长,但并不完全一致,而是相差 2
。如果在 ÷ 3
前先将 待收数量
+2,则结果刚好变成了3倍的关系。如下:
- 待收数量:
1
,实收数量:3
,(待收数量 + 2) ÷ 3后下取整:1
- 待收数量:
2
,实收数量:3
,(待收数量 + 2) ÷ 3后下取整:1
- 待收数量:
3
,实收数量:3
,(待收数量 + 2) ÷ 3后下取整:1
- 待收数量:
4
,实收数量:6
,(待收数量 + 2) ÷ 3后下取整:2
- 待收数量:
5
,实收数量:6
,(待收数量 + 2) ÷ 3后下取整:2
- 待收数量:
6
,实收数量:6
,(待收数量 + 2) ÷ 3后下取整:2
- 待收数量:
7
,实收数量:9
,(待收数量 + 2) ÷ 3后下取整:3
- 待收数量:
8
,实收数量:9
,(待收数量 + 2) ÷ 3后下取整:3
- 待收数量:
9
,实收数量:9
,(待收数量 + 2) ÷ 3后下取整:3
- …
也就是说,实收数量
刚好是 (待收数量 + 2) ÷ 3后下取整
的3倍,其中的 2
实际上就是由 3 - 1
得来的。我们用数学公式整理一下现在的这个想法:
1 | 实收数量 |
注意,其中的 /
符号即我们python语言里的除法,它是下取整的。
类比到10000时的情形,我们的公式会变成下面这个样子:
1 | 实收数量 = (待收数量 + (10000 - 1)) / 3 * 3 |
实际上,如果我们可以让计算机整数相除时上取整(而不是下取整),公式就会变得异常简单:n 上取整除以 10000 * 10000
,在我们学习了后面的更多知识后就可以做到。现在这个公式(n + (10000 - 1)) / 10000 * 10000
虽繁琐,但已是我们通过自己仅有的一点计算机知识所能学到的了。
马儿跑起来
下面,我们将之前的分析总结到一个文本文件里:
1 | print 10000 * (n + (10000 - 1)) / 10000 |
将n替换为提示用户输入待收数量:
1 | print (int(raw_input("Tell me the quantity: ")) + (10000 - 1)) / 10000 * 10000 |
将以上内容保存到a.py,然后输入python a.py
并回车,试试看:
1 | $ python a.py |
嗯,不错,可以。
一点遗憾
这是我们迈出的很重要的一点,如果你跟下来并且看懂了,这说明我们已经掌握了把自己的思想描述给计算机
的技巧。
但这还不完美,离我们的目标自动处理数据并保存结果
还是有一定距离的。多远的距离呢,嗯,可能是4-5篇文章吧。不算远,对吧,假定一篇文章40分钟,加起来也就是3-4个小时的样子。想想我们前面提到的,这些数据手工处理都要3-4个小时,太他妈赚了。
当然,3-4个小时是不够的,我们还需要一些时间消化和遗忘。不过,不管怎么算,这都是人生中很成功的一笔交易。
这篇文章中,还有很多有趣的东西被我一带而过了,留到下一篇我们再一起聊聊吧。
已经晚上23了,洗洗睡了。明天又是崭新的一天,啊哈哈。