儿子小学三年级,经常要做24分计算,于是动手用Arduino nano 做了一个24分计算器,把开发过程记录下来,备忘。
准备物料:我的抽屉里就有现货,都是以前淘宝上卖的:
就三个东西,十分简单。
开始设计了,因为Arduino开发用的是C语言,所以网上找了一个计算24分的C语言源代码,比较简单,稍微看了一下,使用穷举法把4个数字、加减乘除运算符交换位置计算一番,等于24分就返回。网上代码有些小错,稍微修改了一下编译、测试都通过了,代码在Centos6.5下测试通过:
https://yunpan.cn/cSEdbXVcsshr2 访问密码 0125
然而在百度的时候,发现了一个计算24分的网站:http://www.4shu.net/
里面列出了1~13(K)计算24分的全部1362个题目,列出了每个题目的全部独立解法。找到这个以后,我想可以把这些信息都存在Arduino里,然后通过题目就可以查找的该题目的24分解法,虽然这个方法比上面的穷举法计算更复杂,我却对这个方法很感兴趣,尝试实现它。
思路:做一个题目结构数组,一个解法字符串。
题目结构数组按题目小到大的顺序排列。
题目结构
struct tm {
unsigned char tm[2]; // 题目4个数字,1个字节放2个数字
unsigned int offset; // 一个24分题目解法中的偏移位置 (半个字节长度1)
unsigned char cd; // 一个24分题目解法的长度(半个字节长度1)
} ;
题目因为是顺序排列的,所以可以通过折中查找法来快速查找。
全部1362个题目的所有解法,我保存到一个文本文件里,我发现它有33,450个字节,而Arduino的程序空间只有30,720 字节,就是不写程序,都给它存,也存不下这些解法的信息,还不包括题目的信息呢!
经过分析:解法的文件的字符就只有17个(所有回车的0x0D0x0A都删除):“0 - 9”,“+”,“-”,“*”,“/”,“,”,“(”,“)”
一个字节8位二进制数据,原来只放1个字符,如果放2个字符,就可以压缩一半。
然而半个字节4位二进制数,只有16种编码,解法的字符一共有17个,怎么办呢?
“,”是一个24分题目的不同解法的分隔符,也是不能省的。统计发现运算符中的“/”相对较少,而且一个计算式里不可能同时出现2个“/”,所有就用2个除号“//”表示“,”
最后编码如下(半个字节):
“0” :0x0H
“1” :0x1H
“2” :0x2H
“3” :0x3H
“4” :0x4H
“5” :0x5H
“6” :0x6H
“7” :0x7H
“8” :0x8H
“9” :0x9H
“(” :0xAH
“)” :0xBH
“+” :0xCH
“-” :0xDH
“*” :0xEH
“/” :0xFH
连续的2个“/”表示“,”,用来分隔一个24分题目的不同解法。
这些压缩了查不到一半空间,可以放下了,题目信息本来用一个结构表示(5个字节):
struct tm {
unsigned char tm[2]; // 题目4个数字,1个字节放2个数字
unsigned int offset; // 一个24分题目解法中的偏移位置 (半个字节长度1)
unsigned char cd; // 一个24分题目解法的长度(半个字节长度1)
} ;
然而,1362个题目需要1362个这样的结构一共需要6810个字节,加上前面的解法信息,和本身程序代码
编译发现超出了Arduino nano的30,720 字节的程序存储空间。
Arduino nano的单片机是ATMEGA 328芯片,是哈佛结构的,程序空间和数据空间是分离的,
程序空间32KByte,数据空间2KByte。
把题目的6810个字节放在数据空间里也太大,而且题目的的4个数字已经压缩放到2个字节了。
经过考虑,发现题目结构中的unsigned int offset;可以省略,用牺牲速度来换空间!
列如:我要第100题在解法字符串里的偏移,就它之前99题的解法长度加起来,不就是第100题的偏移位置。
所以题目信息的结构表示(3个字节)::
struct tm {
unsigned char tm[2]; // 题目4个数字,1个字节放2个数字
unsigned char cd; // 一个24分题目解法的长度(半个字节长度1)
} ;
这样编译一下发现可以装入到Arduino nano的30,720 字节的程序存储空间里。
好在显示用的是串口屏,使用代码十分简单,花不了多少程序存储空间。
最后写好了所有代码,一共用了29,866 bytes (已经使用了程序空间的97%!)
如果不能计算出24分,显示: