sysprog2020
homework
contributed by < JKChun
>
1
一個 char 是1個 byte,也就是8 bit,只要跟 1000 0000 (0x80)
做 AND
運算,就可以確定是不是有效的 ASCII 字元(因為128~255第8個 bit 一定是1),在64位元的系統中,可以一次處理8個字元,所以 mask 是 0x8080808080808080
,而while
迴圈中的 i
一次加8個,由於字元的總數不一定是8的倍數,最後的 while
迴圈用原本的方法一個字元慢慢檢查
- 解釋上述程式的運作原理,特別是為何用到 memcpy 呢?提示: 與 data alignment 有關,詳閱 C 語言:記憶體管理、對齊及硬體特性
- 將上述技巧應用於「給予一個已知長度的字串,檢測裡頭是否包含有效的英文大小寫字母」
- 承 (2),考慮常見的英文標點符號,判斷輸入字串是否為有效的字元集,避免逐一字元比對
提示: Intel SSE 4.2 加入 STTNI (String and Text New Instructions) 指令,可加速字串比對,詳見: Schema Validation with Intel® Streaming SIMD Extensions 4
A~Z: 0x41~0x5A
a~z: 0x61~0x7A
2
'0'
, '1'
, '2'
, …, '9'
對應到 0x30
, 0x31
, 0x32
, … 0x39
'a'
, 'b'
, 'c'
, …, 'f'
(小寫) 對應到 0x61
, 0x62
, 0x63
, …, 0x66
'A'
, 'B'
, 'C'
, …, 'F'
對應到 0x41
, 0x42
, 0x43
, …, 0x46
第一行是在判斷輸入是字母還是數字,由於數字0~9的 ASCII 的前4bit 都是0011
,而大寫和小寫的字母的 ASCII 的前4bit分別是0100
和0110
,所以把輸入 in
和0x40
(MASK)做 AND 運算就能辨別輸入是字母還是數字:
輸入 | letter 的值 |
---|---|
數字 | 0000 0000 |
字母 | 0100 0000 |
先看最後一行的 return
,裡面有 & 0xf
,這代表只取 in + shift
的後4bit,再看數字0~9的 ASCII 的後4bit 剛好就是對應的0~9,所以 shift
在輸入是字母的時候不是0,在輸入是數字的時候是某個數,接下來看大寫和小寫的字母的 ASCII 的後4bit,發現大寫和小寫的後4bit 都是一樣的,且只和對應的十進位數值差9,所以在輸入是字母時,shift的值為0x09
,因此在第二行將 letter 向右位移3(AAA)和6(BBB)並做 OR 運算得到0x09
字母 | ASCII | 對應的十進位 |
---|---|---|
a | 0110 0001 | 10 |
A | 0100 0001 | 10 |
將 hexchar2val 擴充為允許 "0xFF", "0xCAFEBABE", "0x8BADF00D" 等字串輸入,且輸出對應的十進位數值
Hexspeak
TODO !!
3
找餘數的想法:
目標:找出 , 的商
數學式
為什麼程式中的 ?
現在還不知道 TODO!!
參考了 sammer1107
的推導以及他對 jemalloc
的講解
只要今天 n 是被 d 整除且我們選擇的 k 夠大,我們就可以用 當作 magic number 。要做除法時就做 即可,對應到 c 語言中,即是乘法與位移兩個動作。
According to division rule, we can say that for some and . Then, we have
The first and second equality comes from the same reasoning in the jemalloc case. The third equality comes from the fact that . The final equality stands because is an integer.
The final line equals to only if .
因為在 不整除於 d 的時候,M 會得到 ,為了讓程式在計算 M 時都可以得到 這個結果,所以讓 。
TODO !!
4
no idea
TODO !!
5
#define PACKED_BYTE(b) (((uint64_t)(b) & (0xff)) * 0x0101010101010101u)
是用來取 b 的後4 bit 並複製8次,如:0x23,則結果為 0x2323232323232323*chunk ^= mask
可以得出如果字元在 A~Z 之間則 mask
一定是 0x20,不然就是 0x00,來轉小寫,所以 ((A ^ Z) & PACKED_BYTE(VV4))
的值在 A~Z 之間要 0x80AND
運算,所以 VV4
為 0x80A 和 Z
的第 8 bit 不一樣,所以選擇 VV2 = 0, VV3 = -1,讓在 A~Z 之間的字元經過加法後,變數 A
可以大於 128 而變數 Z
可以小於 128,不是在 A~Z 之間的字元經過加法後,變數 A 和 Z
不是都大於要不就都小於 128 。將前述測試程式 main 函式的 char str[] 更換為 char *str,會發生什麼事?請參閱 C 語言規格書,說明 string literal 的行為
TODO !!
6
lower
紀錄出現一次的 bit,higher
紀錄出現兩次的 bit,而且只有在lower
為0 higher
才能為1,只有在 higher
為0 lower
才能為1,假如已經出現過兩次了接著又出現了,代表出現第三次,那 lower
和 higher
的 bit 皆為0joey3639570
以5 (101) 為例:
第一次
000 ^ 101 = 101 (lower ^=
)
101 & 111 = 101 (lower &= ~higher
)
000 ^ 101 = 101 (higher ^=
)
101 & 010 = 000 (higher &= ~lower
)
結果而論lower
為 101 ,higher
為 000第二次
101 ^ 101 = 000 (lower ^=
)
000 & 111 = 000 (lower &= ~higher
)
000 ^ 101 = 101 (higher ^=
)
101 & 111 = 101 (higher &= ~lower
)
結果而論lower
為 000 ,higher
為 101第三次
000 ^ 101 = 101 (lower ^=
)
101 & 010 = 000 (lower &= ~higher
)
101 ^ 101 = 000 (higher ^=
)
000 & 111 = 000 (higher &= ~lower
)
結果而論lower
為 000 ,higher
為 000
- 請撰寫不同上述的程式碼,並確保在 LeetCode 137. Single Number II 執行結果正確且不超時;
- 延伸題意,將重複 3 次改為改為重複 4 次或 5 次,並找出可容易推廣到多次的程式碼;
TODO !!