1. 程式人生 > 其它 >不使用比較和條件判斷實現min函式的一種方法

不使用比較和條件判斷實現min函式的一種方法

不使用比較和條件判斷實現min函式,引數為兩個32位無符號int。

面試的時候遇到的題目,感覺很有意思。

搜了一下多數現有的解法都是僅有兩種限制之一,即要麼僅要求不能使用比較,要麼僅要求不能使用條件判斷,於是打算寫一下一種能兼顧兩種限制的實現方法。

需要注意的是,條件判斷當然也包含三目表示式、switch-case語句甚至abs等隱含條件分支的語法糖或標準庫函式,除非能夠不借助條件分支實現(例如沒有條件分支的abs:參考連結)。

Solution

基本思想很簡單,在二進位制表示下從高位開始逐位比較,相同的位置可以直接忽略,直到遇到第一個不相同的位置,大小關係就決定了。譬如比較

a=011010
b=010110

時,從高位至低位比較到第三位時兩數不同。此時必定是較大者 a 此位為 1,較小者 b 此位為 0,記此位為符號位 sign_a, sign_b

實際上此時我們已經分辨出兩數的大小,再想辦法將較大者的資訊抹掉即可。方法是分別將 a, b 剩餘的每一位都與符號位相或,此時較大者 a 後半部分變為全 1,而較小者 b 不變,將兩者相與,其結果等於 b,求得min。

在實際實現時可以使用位運算消除條件分支,具體可以參考程式碼。

"""
myMin(0b011010, 010110)

(1)
011010  A
010110  B
^ same 0 vs. 0

(2)
011010  A
010110  B
 ^ same 1 vs. 1

(3)
011010  A
010110  B
  ^ diff, sign_A=1, sign_B=0

(4)
011110  A'
010110  B'
   ^ A_i |= sign_A, B_i |= sign B

(5)(6)
011111  A''
010110  B''
     ^ A_i |= sign_A, B_i |= sign B

A'' & B'' = B
"""

def myMin(a, b):
    found = 0
    sign_a, sign_b = 0, 0
    for i in range(32, -1, -1):
        bit = 1 << i
        xa, xb = (a & bit) >> i, (b & bit) >> i

        # if not found:
        #   d = xa ^ xb
        # else:
        #   d = 0
        d = (not found) & (xa ^ xb)

        # if xa ^ xb == 1:
        #   found = 1
        found |= xa ^ xb

        # if d:
        #   sign_a, sign_b = xa, xb
        sign_a |= d & xa
        sign_b |= d & xb

        a |= sign_a * bit
        b |= sign_b * bit
    return a&b

# 用於生成隨機測試用例測試正確性
import random, time
loop = 0
MAX = 1<<32
while True:
    a, b = random.randint(0, MAX), random.randint(0, MAX)
    if myMin(a, b) != min(a, b):
        print(f"min({a}, {b}) = {min(a, b)} != {myMin(a, b)}")
        break
    loop += 1
    print(loop, end='\r')
    time.sleep(0.001)