不用判断语句,求两个数的最大值
来源:互联网 发布:linux新建文档命令 编辑:程序博客网 时间:2024/04/24 23:35
Hash 思想
1. 首先引进一种简单的思路:
假设我们对两个无符号整型进行求最大值的操作:#define BIT_SIZE (sizeof(int) * 8)int max_ui32(unsigned int a, unsigned int b){ int hash[2] = {a, b} ; int index = (a - b) >> (BIT_SIZE-1) & 1 ; // a <=> b : index // >= : 0 // < : 1 return hash[index] ;}
此方法的精髓在于, 两个无符号整型的减法会导致符号位的变化.
当 a >= b 时, 结果为正数, 因此符号位为 0.
当 a < b 时, 结果为负数, 因此符号位为 1.
显然, 此方法只是用于无符号整型, 更准确的说, 应该是只适用于同号的运算. 因为存在溢出问题(例如当一个很大的正数减去一个很小的负数, 如2000000000 和 -2123456789相减, 会导致结果向符号位进位, 从而溢出), 所以不能适应异号的运算.
2. 以下办法我个人认为也是应用了 hash 的思想, 只不过此程序通过枚举形成了一个映射, 从而处理了异号之间的问题. 适用于任何符号.// 引用他人程序
#define signed_bit(x) (( (x) & 0x80000000) >> 31)#define value_stuff(x) ( x & 0x7FFFFFFF)#define value_diff(x, y) signed_bit( value_stuff(x) - value_stuff(y) )int Max( int x, int y){ int nums[2][2][2] = { x, //000 y, //001 x, //010 x, //011 y, //100 y, //101 x, //110 y //111 }; int idx0 = signed_bit(x); int idx1 = signed_bit(y); int idx2 = value_diff(x, y); return nums[idx0][idx1][idx2];}
将一个int分成两部分,最高位为符号位,剩下的31位为负载部分。
符号位通过 signed_bit 得出,为 0(如果>=0) 或者为1(< 0)
负载部分通过 value_stuff 得出,介于0到0x7FFFFFFF之间,总为正数。
value_diff宏将两个数的负载部分进行减法运算(由于负载在0-0x7FFFFFFF之间且总为正数,因此不可能溢出),为0表示计算后的结果为正数,则x的负载大于或者等于y,否则x的负载小于y。
然后预先建立一个3维数组, 建立依据是逻辑运算:
对于的维分别代表:
signed_bit(x), signed_bit(y), value_diff(x, y)
然后给这个数组初始化:
x, //000 x为正数,y为正数,x负载>=y, 那么x为最大值
y, //001 x为正数,y为正数,x负载<y, 那么y为最大值
x, //010 x为正数,y为负数,那么x为最大值 (无需考虑负载部分)
x, //011 x为正数,y为负数,那么x为最大值(无需考虑负载部分)
y, //100 x为负数,y为正数,那么y为最大值(无需考虑负载部分)
y, //101 x为负数,y为正数,那么y为最大值(无需考虑负载部分)
x, //110 x为负数,y为负数,x负载>=y, 那么x为最大值
y //111 x为负数,y为负数,x负载<y, 那么y为最大值
剩下就是分别得到3个维的index,并返回数组中的值。
3. 还是一个应用 hash 的变种, 只不过这次变化在思路上有所不同, 因为有对于异号运算的处理, 所以能够适用于所有符号.对于函数 max_same 不做过多解释, 请看第一个简单的示例.#include "stdafx.h"#include <iostream>using namespace std ;#define BIT_SIZE (sizeof(int) * 8)int max_same(int a, int b){ int hash[2] = {a, b} ; int index = (a - b) >> (BIT_SIZE-1) & 1 ; // The 31-th bit is 0 if a >= b, // is 1 if a < b in 2'comlement. return hash[index] ;}int max_diff(int a, int b){ // One is non-gegative while other is negative. // Of course non-negative is bigger than negative one. int hash[2] = {a, b} ; int index = a >> (BIT_SIZE - 1) & 1 ; // sign bit of a : index // 0 : 0 // 1 : 1 return hash[index] ;}int max(int a, int b){ int hash[2] = {a, b} ; int (*fun[2])(int a, int b) = {max_same, max_diff} ; int index = (a ^ b) >> (BIT_SIZE - 1) & 1 ; // Is 1 if 'a' & 'b' are same sign, // otherwise is 0. return fun[index](a, b) ;}int _tmain(int argc, _TCHAR* argv[]){ cout <<"max_same: " <<max_same(2000000000, -2000000000) <<endl ; // 溢出 cout <<"max_diff: " <<max_diff(2000000000, -2000000000) <<endl ; cout <<"max: " <<max(2000000000, -2000000000) <<endl ; // 根据不同符号情况, 调用正确处理过程 cout <<"max: " <<max(2000000000, 0) <<endl ; // 根据相同符号情况, 调用正确处理过程 return 0;}
对于函数 max_diff, 在逻辑上也是十分简单的: 一个负数和一个非负数比大小, 结果显然是非负数一定大于负数. 因此, 只要两个变量异号, 则凡是符号位是 1 的变量, 必定小于另一个. 因为没有减法操作, 从而避免了溢出的问题.
对于最终的函数 max, 则是通过异或运算判断两个变量是同号还是异号. 根据不同的情况调用相对应的 max_XXXX 函数, 得到正确的结果.
数学插值
接下来看一个, 这个方法我目前没明白是如何实现的, 待高手指点.// 引用他人程序
new_max (int x, int y){ int xy, yx; xy = ((x - y) >> 31) & 1; yx = ((y - x) >> 31) & 1; return xy * y + yx * x + (1 - xy - yx) * x;}
这里还有一种更精妙的方法, 正所谓 "棋妙子无多".对于函数 max_same: 在不考虑溢出的情况下,unsigned int z=((x-y)>>31)&1的值有两种可能,x>=y时为0,x<y时为1,所以当x>=y时(1-z)*x+z*y的值为x,当x<y时其值为y,所以(1-z)*x+z*y就是x,y的最大值。函数如下:// 引用他人程序
int max_same(int x,int y){ unsigned int z; z=((x-y)>>31)&1; return (1-z)*x+z*y; // 各人认为, 这种数学方法与 hash 是殊途同归. 但是在使用方便程度上更胜一筹.}int max_diff(int x, int y){ unsigned int z; z=(x>>31)&1; return (1-z)*x + z*y;}int max(int x, int y){ unsigned int z; z=((x^y)>>31)&1; return (1-z)*max_same(x,y) + z*max_diff(x,y); // 这里极大的体现出了使用数学公式而非 hash 映射的简洁性. // 使用 hash 则必须要使用函数指针.}
对于函数 max_diff: 在考虑溢出的情况下,unsigned int z=((x^y)>>31)&1的值有两种可能,x、y同号时为0,x、y异号时为1。当x、y同号时x-y不会溢出,可参考1,得出最大值;当x、y异号时,取正的那个就是最大值。函数如下:
其它
此算法核心思想是通过异或运算, 找到最高位的 1 的位置, 利用此位置做除法, 将结果映射到 hash 中. 思想略显复杂, 此程序作者数学功底扎实.// 引用他人程序#define SHIFT (sizeof(int) * 8 - 1)int max(int x, int y){ unsigned int m, n; int ary[2] = {x, y}; m = x ^ y; m = m & ~(m / 2) & ~(m / 4); n = m | 0x01; return ary[ ((x & n) + n / 2) / n ^ !(m >> SHIFT)];}
说明一下吧:
这个算法是企图对x、y按位比较,简单地说,如果不考虑+、-号,则在第一次出现两者的位不相同时,该位是 1 的值比较大,如
00001111111100001111000011110000
00001110111100111111110011111010
左数第8位上,前者是1,后者是0,所以前者比后者大。
对于有符号的情况我们在后面再分析。
1、首先,x^y把x、y中不相同的位置1了,假设x^y=0...01X...X,后面的X表示任意的0或1,这表示在1所在的位置之前的位x、y是相等的,在这一位上或者x为1、y为0;或者x为0、y为1;我们主要是要确定哪一个为1。可惜的是我们无法得到0...010...0这个数以及1所在的位号(不然就要用循环去确定,这不符合题目的要求)
2、设m=x^y,则当x > y时,x在该位上为1,所以x & m = 0...01X...X,所以(x & m) / m 大约为1(注意“大约”,因为有等于0的可能存在,我们等会儿把它排除掉);当x < y时,x & m = 0...00X...X,所以(x & m) / m = 0,现在我们的程序好象可以工作了。。。
3、但是,有一个BUG,如果m == 0呢?(x & m) / m会溢出!m = 0说明x=y,现在的处理是m = m | 0x01,即把最后一位置1,使m >= 1!
这样当m == 0时,我们不管(x & m) / m算出来的是个啥结果,反正哪个都一样(x == y嘛!),而m > 0时,最后一位是不是1不影响我们的计算结果了!
4、前面说过这里还有个BUG,即当x在该位上为1时,我们不能确定(x & m) / m 一定 = 1,因为尽管x & m在该位上也为1,但x & m还是有可能 < m的(还是感叹如果我们能得到0..010...0这个数就好了!)。现在的处理是m = m & ~( m / 2 ) & ~( m / 4),把m这个数变成0...0100X...X,算式也修正为((x & m) + m / 2) / m,这个可以满足我们的要求了
5、最后再来考虑一下有符号的情况
设t=((x & m) + m / 2) / m,f = m的第一位=m >> SHIFT
则当m = 0时,表示x、y同号,这时按上面的方式确定最大值
当m = 1时,x、y一正一负,t表示x首位,为1则x < 0,为0则x >= 0
总的说来:
(1)当f=0,t=0时最大值为y -----1
(2)当f=0,t=1时最大值为x -----0
(3)当f=1,t=0时最大值为x -----0
(4)当f=1,t=1时最大值为y -----1
显然是个同或运算, 可以表示为: t ^ !f
如果不让用!那就是t ^ (~f )
关于 m = m & ~(m / 2) & ~(m / 4);是把m变成:0...0100X...X这样的形式,即1后面有两个0. 至于为什么有两个 0, 是为了让 (x & m) + m/2 的时候, 不产生进位而避免溢出. (如果溢出将导致 ((x & m) + m/2) / m 的结果为 0).
于是当x在该位上为1时:
(x & m) + m / 2 >=0...0100...0 + 0...0010...0 = 0...0110...0 >=m
(x & m) + m / 2) <= 0...01001...1 + 0...001001...1 <= 0...011...10 < 2m
所以((x & m) + m / 2) / m = 1
而当x在该位上为0时:
(x & m) + m / 2 <= 0...00001...1 + 0....001001...1 = 0...0011...1 < m
所以((x & m) + m / 2) / m = 0
一点提升:
a ^ b 的最高位 ---- 判断符号是否相等, 对于异或操作的理解应为: 将不同的位置为 1.
a - b 的最高位 ---- 在符号相同的情况下判断大小
a 的最高位 ---- a 的符号
b 的最高位 ---- b 的符号
原文地址:http://www.cnblogs.com/walfud/articles/2176238.html
- 不用判断语句,求两个数的最大值
- 不用判断语句,求两个数的最大值
- 不用判断语句,求两个数的最大值
- 不用判断语句,求两个数中最大的那个数,
- 不用判断性语句,实现求两个整数中较大数的函数
- 不用判断性语句,实现求两个整数中较大数的函数
- 不用判断性语句,实现求两个整数中较大数的函数
- 不用”if“,”?:“,”switch“或其他判断语句,求两个数中较大的数或较小的数
- 求两个整数的最大值,不使用判断语句
- 求两个数的最大值
- 求两个数的最大值
- 不用那if,switch,?:语句判断两个数的大小
- 不用判断语句比较两个数的大小值
- 08-14 比较两个数的大小,不用判断语句
- 不用判断语句比较两个数的大小值
- 不用if 、for等判断语句和乘除法求1+2..+n的值 &&不用加减乘除求两个数的和
- 不用判断求两个int数据中的最大值
- 另解,c/c++有趣的经典笔试题:不用判断语句求得两个整数的最大值
- GDB调试技巧
- 一日一点RakNet(59)--TCP Interface(TCP接口)
- debug容错 缺陷抓取
- Windows下安装Android 4.0
- eclipse调试开源代码方法
- 不用判断语句,求两个数的最大值
- 欧拉路和欧拉图
- PID参数调节总结
- log4net 在VS2010 NET4下的使用
- hdu 1277 全文检索
- 源码安装软件 pkgconfig ld.so.conf ldconfig
- 五、工厂模式
- eclipse安装ADT插件
- Completion