你真的了解模运算吗?
来源:互联网 发布:免费x网站最新域名 编辑:程序博客网 时间:2024/06/05 03:47
问题
假设我们需要编写一个字母表右移映射的程序(可能用于实现某种加密算法),说起来似乎有些抽象,举个例子便清晰了:
譬如字母表为 { ‘a’, ‘b’, ‘c’, ‘d’, ‘e’ }, 右移3位的话, 字母表便被映射为 { ‘d’, ‘e’, ‘a’, ‘b’, ‘c’ }
使用Lua,我们简单编写了以下代码
local code_table = { "a", "b", "c", "d", "e" }local function get_map_code(code_index, shift_count) local code_table_count = #code_table local map_index = (code_index + shift_count - 1) % code_table_count + 1 return code_table[map_index]endfor i = 1, #code_table do print(get_map_code(i, 3))end
现在我们需要扩展程序以支持字母表左移映射的需求,考虑到左移仅是右移的逆操作,我们只要改变shift_count的符号即可~
for i = 1, #code_table do print(get_map_code(i, -3))end
运行测试目测没有问题,nice~
现在我们为了某些考虑(譬如性能),需要将代码移植至C/C++,移植完成后的代码如下:
const char codeTable[] = { 'a', 'b', 'c', 'd', 'e' };const int codeTableCount = sizeof(codeTable) / sizeof(char);auto getMapCode =[&codeTable, codeTableCount](int codeIndex, int shiftCount){ auto mapIndex = (codeIndex + shiftCount) % codeTableCount; return codeTable[mapIndex];};for (auto i = 0; i < codeTableCount; ++i){ cout << getMapCode(i, 3) << "\n";}
程序的运行结果与Lua是一致的,但是当我们简单的移植左移的时候,程序的结果却出错了……
for (auto i = 0; i < codeTableCount; ++i){ // error !!! cout << getMapCode(i, -3) << "\n";}
问题其实就出在模运算(%)上:
左移操作由于使用了负数的偏移,导致了负数取模运算,而对于负数取模,Lua和C/C++的结果是不一致的,进而导致了结果的偏差……
那么到底Lua和C/C++中的负数取模有什么不一样呢?我们先从模运算的定义说起~
r = a - I(a / b) * b
其中a为除数,b为被除数,r即为模运算的结果,即余数,而I(…)代表的是取整函数,取整函数不同,取模结果自然也就不同
对于Lua,I(…)使用的是向下取整(Floor)的方式,所以如果我们在Lua中计算-1 % 5 的话, 有:
r = -1 - Floor(-1 / 5) * 5 = -1 - (-1) * 5 = 4
而对于C/C++而言,I(…)使用的是截断(Truncate)的方式,所以如果我们在C/C++中计算-1 % 5 的话, 有:
r = -1 - Truncate(-1 / 5) * 5 = -1 - (0) * 5 = -1
由于模运算的结果为负导致索引出界,自然程序的结果也就不会正常了~
知道了程序出错的原因,“修复”起来也就有了对策,方法很简单,自己实现一个使用Floor的取模运算即可~
const char codeTable[] = { 'a', 'b', 'c', 'd', 'e' };const int codeTableCount = sizeof(codeTable) / sizeof(char);auto module =[](int dividend, int divisor) -> int{ return dividend - floor((float)dividend / (float)divisor) * divisor;};auto getMapCode =[&codeTable, codeTableCount, &module](int codeIndex, int shiftCount){ auto mapIndex = module(codeIndex + shiftCount, codeTableCount); return codeTable[mapIndex];};for (auto i = 0; i < codeTableCount; ++i){ cout << getMapCode(i, -3) << "\n";}
值得一提的是如果你使用Lua中math.fmod来计算 -1 % 5 的话,结果和C/C++中是一致的,为 -1
总结
模运算看似简单,但其实大家不一定真正了解,这里有一段Python中关于模运算怎么实现(同Lua一样,也使用了Floor取整)的讨论,有兴趣的朋友可以看下~
OK,下次再见吧~
- 你真的了解模运算吗?
- 你真的了解telnet吗?
- 你真的了解telnet吗?
- 你真的了解HTML吗
- 你真的了解while(n--)吗?
- 你真的了解HTML吗
- 你真的了解计算机除法吗
- 你真的了解xargs命令吗
- 你真的了解null吗?
- 你真的了解find命令吗
- 你真的了解HTML吗
- 你真的了解互联网吗?
- 你真的了解 console 吗
- 你真的了解 console 吗
- 二维码,你真的了解吗?
- 你真的了解Hybrid APP吗
- 你真的了解visible属性吗
- 你真的了解nosql世界吗?
- CSDN Markdown使用教程
- Srping MVC中Controller的void方法声明误区
- 我踩到微信小程序map组件的一些神坑
- 面试经历6
- 华为机试 四则运算
- 你真的了解模运算吗?
- org.hibernate.MappingException: Could not get constructor for org.hibernate.persister.entity.SingleT
- 算法 第八章习题 8.12
- pyspark的pickle.PicklingError
- 剑指offer--面试题19:正则表达式匹配
- 众人寻AI千百度
- Android Studio 更改预览风格
- iOS网络HTTP、TCP、UDP、Socket 知识总结
- linux-2.6.11-socket(1)