# 2023q1 Homework2 (quiz2) contributed by <`kart81604`> ## 測驗`1` :::success AAAA = `8` BBBB = `32` CCCC = `x + 1` ::: ### 程式碼原理 首先要將輸入值從左到右第一個出現 `1` 之後的位元都改成`1` ,如輸入 `x = 0100 0100 0100 0010` 應轉換為 `x = 0111 1111 1111 1111` 程式碼部分前七行皆為 ```c x |= x >> 1; ``` 這個連做 7 次後,會得到第一個 `1` 之後的 7 個位元也接著會是 `1` ,因此得到了至少連續 8 個`1` 在一起的數列,那之後就也不用慢慢的只位移一個位元了,我們可以一次位移 8 個位元了 ```c x |= x >> 8; ``` 之後就會得到從第一個 `1` 開始數,共連續 16 個 `1` 連在一起的數列,以此類推,等等可以直接位移 16 個位元,再來就是 32 個位元了。 做完上述的行為後,我們得到了第一個 `1` 開始之後,後面皆為 `1` 的數列,之後最後再加上 1 ,就可以得到大於輸入值,且為 2 的冪次的輸出值。 ```c return x + 1 ``` ### 用 `__builtin_clzl` 改寫 ::: success `int __builtin_clz (unsigned int x)` Returns the number of leading 0-bits in x, starting at the most significant bit position. If x is 0, the result is undefined. `int __builtin_clzl (unsigned long)` Similar to __builtin_clz, except the argument type is unsigned long. ::: 由上述描述可知,`__builtin_clz` 會回傳 `input` 最左邊有幾個連續 `0` ,那麼只要在 `unit64_t` 型別下,全部都是 `1` 的值,往右位移 `__builtin_clzl(x)` 個位元就好了。 ```c uint64_t next_pow2(uint64_t x) { return (0xFFFFFFFFFFFFFFFF >> __builtin_clzl(x)) + 1 ; } ``` ## 測驗`2` :::success DDDD = `i & (i - 1)` EEEE = `ans << len` ::: ### 程式碼原理 設定輸入: n = 4 ,藉由題目描述,已知 n = 3,串接後為 `11011` ,接著要在此數列後新增 `100` ,因此要將原來的數列空出 3 格來放進去。 ```c vvv 11011 ``` 用來判斷要移出多少空格的參數為 `len` ,每經過2的冪次之後,要將 `len` 加 1 。 之後將 `ans` 往左位移 `len` 長度,再用 `|` 將要放入的值放進去,再 mod $10^9+7$ 。 ## 測驗`3` :::success AAAA = 3 BBBB = 7 CCCC = 7 DDDD = 0 ::: ### 程式碼原理 由於 `*end = qword + len >> 3` ,因此 `len` 要是不為 8 的倍數,會有一部分的 byte 沒有被計算到,因此for迴圈不一定有把 `*buf` 全部都檢查到。 由於我們每 8 個 byte 記錄一次,因此我們減掉 `count` 也應該用以計算的部分去減。 ```c count = (1 << 3) * (len / 8) - count; ``` 這部分就是檢查是否有剩餘的 byte 沒有被檢查到,如果有的話,那麼除以 8 就會有非零的餘數,就利用測驗 `3 `前面提到的 `count_utf8` 來計算剩餘的部分,如果 `len` 是 8 的倍數,則代表 for 迴圈就檢查完所有的 byte 了,就也不用再計算了。 ```c count += (len & 7) ? count_utf8((const char *) end, len & 7) : 0; ``` ## 測驗`4` :::success EEEE = `-x` FFFF = `x` ::: ### 程式碼原理 `is_pattern` 是要檢查輸入值以二進位表示,是否能分成左右兩部分,左邊皆為 `1` ,右邊皆為 `0`。 ```v 1110 0000 0000 0000 //true 1111 1111 1000 0000 //true 1000 0000 0000 0000 //true 1000 1000 0100 0000 //false ^ ^ 0000 0000 0000 0000 //false 1111 1111 1111 1111 //true ``` 假設輸入值是滿足這個條件,也就是左邊部分為連續 `1` ,右邊部分為連續 `0`。 ```c x = 1111 1100 0000 0000 //input n = 0000 0100 0000 0000 //n = -x ^ n ^ x = 1111 1000 0000 0000 ^ ``` 可以發現,如果輸入值滿足條件,則它的二補數的形式是他的值最右邊的 `1` 的位子為 `1` ,其餘為 `0`,再與輸入值進行 xor ,會得到原本的輸入值,少了最右邊的 `1` ,其餘與原本一樣,因此會比原輸入值小。 再來觀察不滿足條件的狀況 : ```c x = 1111 1100 1000 0000 //input n = 0000 0011 1000 0000 //n= -x n ^ x = 1111 1111 0000 0000 ``` ```c x = 1111 1100 1000 1000 //input n = 0000 0011 0111 1000 //n = -x n ^ x = 1111 1111 1111 0000 ``` 可以觀察到,`n ^ x` 會得到除了原本為 `1` 位置,還多了好幾個 `1` ,因此會比原輸入值大。 原理如下,找出輸入值轉成二進位後,其中所有 `1` 中最右邊的位子。下例為 10 。 ``` 1234 5678 9012 3456 //position xxxx xxxx x100 0000 //input ``` 計算其二補數 ``` 1234 5678 9012 3456 //position xxxx xxxx x100 0000 //input yyyy yyyy y011 1111 //~x yyyy yyyy y100 0000 //-x = ~x + 1 ^^^ ^^^^ //same as input ``` 可以觀察到,以 `-x` 最右邊的 `1` 開始往右,他的二補數部分都與原輸入值相同,之後進行 xor 運算後得出 : ``` 1234 5678 9012 3456 //position xxxx xxxx x100 0000 //input yyyy yyyy y100 0000 //-x 1111 1111 1000 0000 //-x ^ x ``` 如果 input 前面那串 xxx... ,皆為 `1`,那麼就會比 `-x ^ x` 大,如果其中有包含 `0` ,則會小於 `-x ^ x` 。