背景描述
我有2块磁盘,并要求设计一个函数 cal() 用于计算一块磁盘中文件的数量
我们很容易写出
#define DISK1 1
#define DISK2 2
int cal(int disk)
{
int size = 0;
if (disk == DISK1) {
size = ...; // 计算磁盘1大小
return size;
}
if (disk == DISK2) {
size = ...; // 计算磁盘2大小
return size;
}
}
这样用户如果需要统计两块磁盘总文件数量,就可以这样写
int size1 = cal(DISK1); // 计算 磁盘1的文件数量
int size2 = cal(DISK2); // 计算 磁盘2的文件数量
int size_sum = size1 + size2;
简化一点就是
int size_sum = cal(DISK1) + cal(DISK2);
如果有三块磁盘呢?
int size_sum = cal(DISK1) + cal(DISK2) + cal(DISK3);
四块呢???
多合一
有什么办法能将多次调用改成一次即可呢?答案是提供一个DISK_ALL参数
#define DISK1 1
#define DISK2 2
#define DISK3 3
#define DISK_ALL 4
int cal(int disk)
{
int size = 0;
if (disk == DISK1 || disk == DISK_ALL) {
size += ...; // 计算磁盘1大小
}
if (disk == DISK2 || disk == DISK_ALL) {
size += ...; // 计算磁盘2大小
}
if (disk == DISK3 || disk == DISK_ALL) {
size += ...; // 计算磁盘3大小
}
return size;
}
这样,用户如果想统计所有磁盘的文件数量,就可以写出
int size_sum = cal(DISK_ALL);
如何选择性的统计呢?
加入了DISK_ALL参数后,我们可以很方便的统计所有磁盘文件数量的和了
一个新的问题是,我们如何只统计其中几块磁盘的文件数量呢?
例如,我想求1盘、3盘的数量,那么我只能写成
int size = cal(DISK1) + cal(DISK3);
代码如何优化才能让调用再变成一次呢?
答案是引入bit map的思想
#define DISK1 1
#define DISK2 2
#define DISK3 4
#define DISK_ALL 7
int cal(int disk)
{
int size = 0;
if (disk | DISK1) {
size += ...; // 计算磁盘1大小
}
if (disk | DISK2) {
size += ...; // 计算磁盘2大小
}
if (disk | DISK3) {
size += ...; // 计算磁盘3大小
}
return size;
}
这时用户就可以写成
int size = cal(DISK1 | DISK3); // 统计1、3号磁盘文件数量
int size = cal(DISK1 | DISK2 | DISK3) // 统计所有磁盘文件数量
int size = cal(DISK_ALL) // 统计所有磁盘文件数量的等价写法
这里利用了二进制位运算的性质
我们预设了
DISK1 = 1 = 001
DISK2 = 2 = 010
DISK3 = 4 = 100
DISK_ALL = 7 = 111
当我们传入disk = DISK1 | DISK3时,我们实际传入了disk = 001 | 100 == 101
所以代码中三个if判断的条件结果分别是:
disk | DISK1 = 101 | 001 = 001 = ‘true’
disk | DISK2 = 101 | 010 = 000 = ‘false’
disk | DISK3 = 101 | 100 = 100 = ‘true’
就会走入计算1、3盘文件数量的分支
But Why
为什么这样可行呢
这其实利用了正交性:我们设定的几个不同的 DISKx 的值,在二进制表示上,是彼此正交的
所以可以通过简单的或或者加操作来对DISKx进行组合
所以可以返回看下上面的代码。将
|操作换成+操作一样等价
一个面试题
如果我非要定义
#define DISK1 1
#define DISK2 2
#define DISK3 3
能否仍然满足之前的需求呢?
显然。1,2,3 在二进制表达层级上,已经不是彼此正交的了
所以cal(DISK1 | DISK2) 并不等价于cal(DISK1) + cal(DISK2)
如何恢复他们的正交性呢?
编码
1、2、3 不具备二进制表示的正交性
但是2<sup>1</sup>、2<sup>2</sup>、2<sup>3</sup>,在二进制表示层面上是正交的
定义编码函数f(x) = 2<sup>x</sup>
则f(DISK1 + DISK2) == f(DISK1) + f(DISK2)
定义可以改写成
#define DISK1 f(1)
#define DISK2 f(2)
#define DISK3 f(3)
#define DISK_ALL f(4)
其实这与之前是等价的,只不过换了一种思考角度
正交性在其他领域的作用
测试
在测试某一对象(以接口为例),我们会对每一个入参进行等价类划分,对划分出来的测试点,进行正价分析。通常被用于构造测试输入数据。
例如一个接口f(int a, int b),接受两个参数a和b,设计说明a的合法取值范围为0~100,b的合法取值范围为0~10
则可以得到以下正交表
| ab的取值 | -1 | 0 | 1 | 99 | 100 | 101 |
|---|---|---|---|---|---|---|
| -1 | f(-1, -1) | f(0,-1) | f(1,-1) | f(99,-1) | f(100,-1) | f(101,-1) |
| 0 | f(-1,0) | … | … | … | … | … |
| 1 | f(-1,1) | … | … | … | … | … |
| 9 | f(-1,9) | … | … | … | … | … |
| 10 | f(-1,10) | … | … | … | … | … |
| 11 | f(-1,11) | … | … | … | … | … |
则可以完成对接口f的基本覆盖
本文探讨了如何通过位运算优化计算多块磁盘文件数量的函数,引入了DISK_ALL参数和位运算的正交性概念。当磁盘数量增加时,利用二进制编码和位运算,可以实现一次性计算多个磁盘的文件总数。同时,还介绍了如何通过编码函数恢复不同数值之间的正交性,以应对特定的数值冲突问题。这种方法在软件设计和测试中也有广泛应用,如接口测试的正交性分析。
1138

被折叠的 条评论
为什么被折叠?



