contributed by < DokiDokiPB
>
重新梳理 next_pow
數值
next_pow2(7) = 8
next_pow2(8) = 16
next_pow2(42) = 64
這裡有個疑問是:輸入數值只能小於 0x8000000000000000,最高位元必須為 0,沒有定義大於此數值應該輸出什麼?
直覺想到思路可以寫為:
假設變數 的設定為 1 的最高位元為 ,每個被設定 1 的位元為 , , … , ,next_pow(x)
的最高有效位元為
每次 x |= x >> 1;
相當於每個 向右傳播一次,最終,從 到最低位元皆為 1,再加上 1,即獲得
另外,可以把程式碼改成以下形式
__builtin_clzl
改寫在原本的函式中
於是改寫成:
在 C99 規格書中
6.5.7 Bitwise shift operators
The integer promotions are performed on each of the operands. The type of the result is that of the promoted left operand. If the value of the right operand is negative or is greater than or equal to the width of the promoted left operand, the behavior is undefined.如果 shift count 為負數,會有未定義行為
考量了 right shift 有未定義行為,改寫成:
cc -O2 -std=c99 -S next_pow2.c
擷取部份組合語言
取自 LeetCode 1680. Concatenation of Consecutive Binary Numbers
將變數 i
與變數 len
之間的關係列出來,會發現每次 len++
發生在 i
為 2 的冪:
i & (i - 1)
可用來移除最低位元的 1。
__builtin_{clz,ctz,ffs}
改寫,並改進取餘數的運算第一個想法是可以將分支 (branch) 移除
有些線索可以去思考
const int M = 1e9 + 7;
是質數(i | (ans << len)) % M;
可化為 ((i % M) + (ans << len) % M) % M
很好,你發現質數這個重要屬性,接著要搭配數論去思考,參見 Modulo a Prime Number
jserv
想了幾種可能性:
n
足夠大令與 n
的差距大的數字可忽略;不過比對 n = 1023
的輸出數值是錯的
__builtin__clz(n) = 22; // 512 <= n <= 1023
,考量輸出為 32 位元的整數可以想成,只要考量附近的數字((i % M) + (ans << len) % M) % M
,其中的 ans << len
,可以經由手算出 2 的乘法反元素,可算出 ,不過還是牽涉到除法0 ~ 10e9 + 6
對應的乘法反元素;不過輸入甚麼輸出固定,也可以很乾脆建立表格。以正規表達式顯示符合的二進制表示字串: ,例如:, , ,
目前想到的是,先將套用數值進去,查看數值間的關係:
~x
變為 ~x + 1
會變為 2 的冪,只有一個位元被設定為 1,且此位元位置與 的最低有效位元位置相同n ^ x
會設定該相同位置的位元為 0n < x
成立~x + 1
不會是 2 的冪n ^ x
產生大於 x
的數值