HaoYu
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Versions and GitHub Sync Note Insights Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       owned this note    owned this note      
    Published Linked with GitHub
    Subscribed
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    Subscribe
    # 2020q3 Homework2 (quiz2) contributed by < `haoyu0970624763` > ## 1. ASCII 編碼判斷 ```cpp #include <stdio.h> #include <string.h> #include <stdbool.h> #include <stdint.h> bool is_ascii(const char str[], size_t size) { if (size == 0) return false; int i = 0; while ((i + 8) <= size) { uint64_t payload; memcpy(&payload, str + i, 8); if (payload & 0x8080808080808080) return false; i += 8; } while (i < size) { if (str[i] & 0x80) return false; i++; } return true; } ``` ***程式解說*** 對於單個字元 1 byte = 8 bits , ASCII碼為0-126 若此字元 和 0x80 做 & 不等於0 則表示他從左邊數過來第二個bit != 0 也就不為ASCII碼 同樣的邏輯推廣到 8 個字元即是答案 :::info memcpy 的使用不但是用來得到 64 bits 的 payload,memcpy會去檢查記憶體位址是否有 alignment ,可以避免因為對記憶體的 unaligned memory access 產生錯誤。 ::: ### 延伸問題二 ```cpp= #include <stdio.h> #include <string.h> #include <stdbool.h> #include <stdint.h> #define PACKED_BYTE(b) (((uint64_t)(b) & (0xff)) * 0x0101010101010101u) bool is_alphabet(const char str[], size_t size) { if (size == 0) return false; int i = 0; while ((i + 8) <= size) { uint64_t payload; memcpy(&payload, str + i, 8); uint64_t A = payload + PACKED_BYTE(128 - 'A' ); uint64_t Z = payload + PACKED_BYTE(128 - 'Z' - 1); uint64_t a = payload + PACKED_BYTE(128 - 'a' ); uint64_t z = payload + PACKED_BYTE(128 - 'z' - 1); uint64_t lowerMask = (a ^ z) & PACKED_BYTE(0x80); uint64_t upperMask = (A ^ Z) & PACKED_BYTE(0x80); if (( lowerMask | upperMask ) ^ PACKED_BYTE(0x80)){ return false; } i += 8; } while (i < size) { if ( (str[i] < 65 || (str[i] > 90 && str[i] < 97) || str[i] > 122)) return false; i++; } return true; } ``` ***程式解說*** 一開始想不到要怎麼寫 參考了別人一下也發現看不太懂 :::danger 看不懂就說看不懂,不要說模糊地「不太懂」。如果你認定其他同學的共筆寫不好,那就留訊息指出其不足之處。 再者,你的標點符號呢? :notes: jserv ::: 但是把全部測驗都解釋整理一次之後 就是把測驗 5 的思考迴路跟解法搬過來這邊解 ## 2. 16進位字元轉換 ```cpp= uint8_t hexchar2val(uint8_t in) { const uint8_t letter = in & 0x40; const uint8_t shift = (letter >> 3) | (letter >> 6); return (in + shift) & 0xf; } ``` ***程式解說*** 我們可以觀察回傳值 `(in + shift) & 0xf` 僅要後面四碼而已 而字元`'0'`到`'9'` 的僅取其後四個bit轉換過來即為答案 至於字元`'a'`到`'f'` 的其後四個bit轉換過來為 1-7 只要都再+9 即為對應之答案 我們先用 mask=0x40 如果你是字母(大小寫都可)letter都會等於 0x40 如果是 數字 letter=0x00 而shift則表示 如果你是字母 shift=0x09 數字的話則為0 接著就可求到答案了 ## 3.快速除法 ```cpp= const uint32_t D = 3; #define M ((uint64_t)(UINT64_C(0xFFFFFFFFFFFFFFFF) / (D) + 1)) /* compute (n mod d) given precomputed M */ uint32_t quickmod(uint32_t n) { uint64_t quotient = ((__uint128_t) M * n) >> 64; return n - quotient * D; } ``` ### 程式原理 $n\mod D = n - (n/D) \times D$ 也就是說 `n - quotient * D` 的 `quotient` 就是 `n / D` 的整數部份。 從題目裡的提示 $$ \dfrac{n}{d} = n \times (\dfrac{2^N / d}{2^N}) $$ `quotient = ((__uint128_t) M * n) >> 64` 右移 64 bits 即為 $\dfrac{1}{2^N}$,所以 N = 64。 但是 $M={(2^{64}/D)}$ 跟 ${(2^{64} - 1) / D} + 1$ 略為不同 參考`Rsysz` 發現兩者算出來的答案是一樣的 所以 `M=0xFFFFFFFFFFFFFFFF` ## 4.倍數判斷 ```cpp= bool divisible(uint32_t n) { return n * M <= M-1; } ``` ### 程式原理 延伸上一題的觀念 因為D=3 令 `n=3k,3k+1,3k+2` (k為非負整數) 其中 `3M=0x10000000000000002` 已經overflow 所以會把 3M視為`3M=0x0000000000000002` 所以 case1: $n * M = 3kM = 2k$ case2 : $n * M = 3kM +M = 2k +M$ case3 : $n * M = 3kM +2M = 2k +2M$ 所以答案 C,D皆符合 :::info 疑問:有點像是用答案硬推理 不知道為什麼當初會這樣構想程式 ::: :::danger 工程人員說話要精準,避免說「有點像是」,後者是文組^TM^用語 :notes: jserv ::: ## 5. 字串大寫轉換小寫 ```cpp #include <ctype.h> #include <stddef.h> #include <stdint.h> #include <stdio.h> #include <string.h> #define PACKED_BYTE(b) (((uint64_t)(b) & (0xff)) * 0x0101010101010101u) void strlower(char *s, size_t n); /* vectorized implementation for in-place strlower */ void strlower_vector(char *s, size_t n); int main() { /* quote from https://www.gutenberg.org/files/74/74-h/74-h.htm */ char *str = "This eBook is for the use of anyone anywhere at no cost and with \ almost no restrictions whatsoever. You may copy it, give it away or \ re-use it under the terms of the Project Gutenberg License included \ with this eBook or online at www.gutenberg.net"; int n = strlen(str); strlower_vector(str, n); puts(str); } void strlower(char *s, size_t n) { for (size_t j = 0; j < n; j++) { if (s[j] >= 'A' && s[j] <= 'Z') s[j] ^= 1 << 5; else if ((unsigned) s[j] >= '\x7f') /* extended ASCII */ s[j] = tolower(s[j]); } } void strlower_vector(char *s, size_t n) { size_t k = n / 8; for (size_t i = 0; i < k; i++, s += 8) { uint64_t *chunk = (uint64_t *) s; if ((*chunk & PACKED_BYTE(0x80)) == 0) { /* is ASCII? */ uint64_t A = *chunk + PACKED_BYTE(128 - 'A' + 0); uint64_t Z = *chunk + PACKED_BYTE(128 - 'Z' + (-1)); uint64_t mask = ((A ^ Z) & PACKED_BYTE(0x80)) >> 2; *chunk ^= mask; } else strlower(s, 8); } k = n % 8; if (k) strlower(s, k); } ``` ### 程式原理 `PACKED_BYTE(b)` 的作用是取出 `b` 的後 8 個位元(假設是 0x12),並且變成 0x1212121212121212 接著檢查這個字元是不是 ASCII 編碼 (運用到題一) 如果 `*chunk + PACKED_BYTE(128 - 'A' + 0) >= 128` 表示 chunk的ASCII 編碼大於等於`'A'` 如果 *chunk + PACKED_BYTE(128 - 'Z' -1) < 128表示 chunk的ASCII 編碼小於等於`'Z'` 把 A 跟 Z 做 Xor 如果等於 `10000000` 表示 chunk是介於 `'A'` 到 `'Z'` 之間 則 mask=`00100000`=`' '` 若大寫字母跟`' '` Xor 則會跑出小寫字母 如果chunk不介於`'A'`到 `'Z'` 之間 同時小於128 或是同時大於128 則 mask=`00000000` 任何東西跟`0` Xor 都等於自己 ## 6. 單次數尋找 ```cpp int singleNumber(int *nums, int numsSize) { int lower = 0, higher = 0; for (int i = 0; i < numsSize; i++) { lower ^= nums[i]; lower &= ~higher; higher ^= nums[i]; higher &= ~lower; } return lower; } ``` ### 程式原理 `lower` 是紀錄哪個位置的bit出現數為奇數次 出現奇數次的=1 `higher` 是紀錄哪個位置的bit出現數為偶數次 出現偶數次的=1 **實做邏輯** lower ^= nums[i] 會先把num[i]的bit 出現奇數次的bit紀錄住 lower &= ~higher 會把已經出現過偶數次的bit扣掉 (當有一個bit出現第三次 lower會紀錄住 但是這個其實是不需要的 會透過上一個迴圈的higher把第三次出現的的消掉 因為我們只要出現一次的bit) higher ^= nums[i] 會先把num[i]的bit 奇數次的bit的紀錄住 higher &= ~lower 會把已經出現過奇數次的bit扣掉 (當只出現一次 lower會記住 higher也會記住 但會被扣掉 而出現第二次時 lower會消掉 higher會記住 但不會被扣掉 也就是說higher會紀錄出現偶數次的bit) :::danger 注意筆記書寫規範,中英文字元用一個空白區隔,從小處做起 :notes: jserv ::: ## 新增: 第2題 16進位字串轉換 ```c=1 uint64_t HexToDigit(char* in) { char *tmp_ptr=in; char *reverse; uint64_t payload = 0, value = 0; if (in[0] == '0' && in[1] == 'x') tmp_ptr=&in[2]; #if __BYTE_ORDER == __BIG_ENDIAN reverse=tmp_ptr; #else memset(reverse,'\0',strlen(tmp_ptr)+1); for (int i = 0; i < strlen(tmp_ptr); i++) { reverse[i]=tmp_ptr[strlen(tmp_ptr)-1-i]; } #endif memcpy(&payload, reverse , sizeof(char)*strlen(tmp_ptr)); uint64_t letter = payload & 0x4040404040404040; uint64_t shift = (letter >> 3) | (letter >> 6); uint64_t tmp=(payload + shift) & 0x0F0F0F0F0F0F0F0F; int i=strlen(tmp_ptr); for( int i=strlen(tmp_ptr); i!=0 ; i--){ value= value << 4; value |= ((tmp >> (i-1)*8) & 0x0F); } return value; } ``` 根據電腦的endian架構去決定input的字串要不要反過來 把 input 的字串反過來是因為 電腦架構還有 uint64_t 的 endian 是不一樣的,會發現這個 是因為我在測試 `0x12` 的時候,答案一直都不是我預期的,反而是 `0x21` 的答案,所以就上 stackoverflow 尋找才發現是 endian 的問題 邏輯: `strlen(tmp_ptr)` 表示需要轉換的長度 用 `tmp` 存著所有轉換完的 input ,每 8bits 表示一個數字 從轉換完的輸入最左邊 8bits 開始依序到最右邊 8bits ,將 8bits 右移到最右邊8 個 bits 並且跟 value 做 AND 接著再把 value 左移4個bits(因為是16進位) 把第8-15bits 右移到最右邊8 個 bits 並且跟 value 做 AND ....依此類推 `tmp >> (i-1)*8` 控制是哪一組 8bits 右移到最右邊 ` & 0x0F` 只需要最右邊 4bits ## 新增: 第3題 快速除法(jemelloc原理) $n$ 為**被除數**, $q$ 為**商**, $d$ 為**除數** 數學式子從`Rsysz`參考,但是他的 $r=(-2)^k mod \space d$ 應為筆誤或是錯誤 r 表示 $2^k mod \space d$ (也就是餘數) 假設$n = q \times d$,已知 n , d 求 q $q = n / d$ $=\left \lfloor \left \lceil \dfrac{2^k}{d} \right \rceil \times \dfrac{n}{2^k} \right \rfloor = \left \lfloor \dfrac{2^k + r}{d} \times \dfrac{n}{2^k} \right \rfloor,k為任意整數,而r = -(2)^k mod \space d$ $將\left \lfloor \dfrac{2^k + r}{d} \times \dfrac{n}{2^k} \right \rfloor展開為\left \lfloor \dfrac{2^k}{d} \times \dfrac{n}{2^k} + \dfrac{r}{d} \times \dfrac{n}{2^k}\right \rfloor = \left \lfloor \dfrac{n}{d} + \dfrac{r}{d} \times \dfrac{n}{2^k} \right \rfloor$ $因假設n /d為整除,所以n/d的小數部分為0,因此式子可以化簡為\dfrac{n}{d} + \left \lfloor \dfrac{r}{d} \times \dfrac{n}{2^k} \right \rfloor$ 也就是當$\dfrac{r}{d} \times \dfrac{n}{2^k} < 1$,$n/d = \left \lfloor \left \lceil \dfrac{2^k}{d} \right \rceil \times \dfrac{n}{2^k} \right \rfloor$就會成立 因 $r$ 是 $\dfrac{2^k}{d}$ 的餘數,因此 r < d 且 $r/d<1$總是成立,所以只需滿足$\dfrac{n}{2^k} < 1$ 又 $n$ 為32bits的數,最大值為 $2^{32}-1$,因此可以令 k = 32(k之最大值)來確保$\dfrac{n}{2^k} < 1$,而這也是為什麼jemalloc bound 數值範圍在 $2^{32}$ 之間。 [code 連結](https://github.com/haoyu0970624763/sysprog21-quiz2/blob/master/quickDiv.c) div_info->magic = $\left \lceil \dfrac{2^k}{d} \right \rceil$ 為何要取上高斯?? 用例子說明 : $12 \times \dfrac{2^3}{6} >> 3$ 若是 $\dfrac{2^3}{6}$ 四捨五入或取下高斯 , 答案皆為1,取上高斯答案則為 6(所以要取上高斯) 理論上最優的算法當然是 $12 \times 2^3 /6 >> 3$ 但程式語言無法做到這件事。 取下高斯或是捨去會有部份值被捨去,會造成相乘出來的結果變小,進而影響結果。 取上高斯或是進位,會造成相乘出來的結果變大,進而影響結果,但後來有右移所以把答案調回來。 ## 新增: 快速除法跟一般除法之間的效能比較: **最一開始的實驗:** 將快速除法跟除法分成兩個執行檔,除數為亂數設定,被除數也是亂數設定。 兩個檔案的亂數種子是設定為一樣的,所以兩個檔案的除數跟被除數都是一樣的,皆進行一億次運算 想像中的結果,quickdiv 應該快於 div 的運算,然而結果卻不一樣。 [div code 連結](https://github.com/haoyu0970624763/sysprog21-quiz2/blob/master/div_test.c) [quickdiv code 連結](https://github.com/haoyu0970624763/sysprog21-quiz2/blob/master/quickdiv_test.c) ![](https://i.imgur.com/7PvxtuN.png) **從上圖可以發現,一般的除法會略快於快速除法** **為什麼會導致這樣的結果呢?** * **1. 實驗設計錯誤** * **2. 在實做上 quickdiv真的慢於div** * **3. 實驗測資導致的結果** ### 針對實驗設計錯誤的問題,我仿照`RinHizakura`的實驗方式進行實驗 底下的 code 為 `RinHizakura` 的實驗基礎設計 ```cpp static uint64_t MAX = ((uint64_t)(1) << 32) - 1; int main() { struct timespec tt1, tt2; clock_gettime(CLOCK_MONOTONIC, &tt1); for (uint64_t i = 2; i < 5000; i++) { div_info_t DIV; div_init(&DIV, i); uint64_t num = rand() % MAX; clock_gettime(CLOCK_MONOTONIC, &tt1); size_t ans1 = div_compute(&DIV, num); clock_gettime(CLOCK_MONOTONIC, &tt2); long long time1 = (long long) (tt2.tv_sec * 1e9 + tt2.tv_nsec) - (long long) (tt1.tv_sec * 1e9 + tt1.tv_nsec); clock_gettime(CLOCK_MONOTONIC, &tt1); size_t ans2 = num / i; clock_gettime(CLOCK_MONOTONIC, &tt2); long long time2 = (long long) (tt2.tv_sec * 1e9 + tt2.tv_nsec) - (long long) (tt1.tv_sec * 1e9 + tt1.tv_nsec); printf("%ld, %lld, %lld\n", i, time1, time2); } } ``` ## 改良的第一版測試,測試 多個被除數 除以 單一除數 [test1 code 連結](https://github.com/haoyu0970624763/sysprog21-quiz2/blob/master/TestDiv/test1.c) 下圖為測試結果 ![](https://i.imgur.com/pZLWvGB.png) 可以發現在編譯器沒優化的情況下, quickdiv 明顯快於 div 而經過優化之後發現兩者速度皆減低不少,而根據 `RinHizakura`的實驗發現這是因為編譯器發現 除法的結果是不需要用到的,所以自動把它略過了。 ## 改良的第二版測試,測試 多個被除數 除以 多個除數 [test2 code 連結](https://github.com/haoyu0970624763/sysprog21-quiz2/blob/master/TestDiv/test2.c) 因為知道存在編譯器的優化,所以在程式過程中我們把編譯器的優化關掉。 同時為了避免測資的偏頗,設置數個不同的 divisor ,並分別計算兩種不同的除法的時間並取平均值, quickDiv 暫時不包含 div_init 的執行時間(也就是僅計算除法的部份)。 ![](https://i.imgur.com/ah7pZjC.png) 由上圖可發現 quickdiv 明顯快於 div ## 改良的第三版測試,測試2的小變形 [test3 code 連結](https://github.com/haoyu0970624763/sysprog21-quiz2/blob/master/TestDiv/test3.c) test2的變形,因為在程式執行過程中,其實不能忽略 div_init 的執行時間 因為他也算在 quickDiv 的部份環節,若是以 test2 為基準,每一層 loop 都找一個新的divisor 也就是每一個quickDiv都包含 div init的時間。 ![](https://i.imgur.com/57dZeZQ.png) 由上圖就可以發現, quickdiv 的時間明顯慢於 div 關於 `RinHizakura` 的實驗筆記的這部份修正,我覺得是不必要的。 ```cpp int main() { struct timespec tt1, tt2; clock_gettime(CLOCK_MONOTONIC, &tt1); for (uint64_t i = 2; i < 5000; i++) { div_info_t DIV; div_init(&DIV, i); uint64_t num = rand() % 10000; num *= i; clock_gettime(CLOCK_MONOTONIC, &tt1); size_t ans1 = div_compute(&DIV, num); clock_gettime(CLOCK_MONOTONIC, &tt2); long long time1 = (long long) (tt2.tv_sec * 1e9 + tt2.tv_nsec) - (long long) (tt1.tv_sec * 1e9 + tt1.tv_nsec); clock_gettime(CLOCK_MONOTONIC, &tt1); size_t ans2 = num / i; clock_gettime(CLOCK_MONOTONIC, &tt2); long long time2 = (long long) (tt2.tv_sec * 1e9 + tt2.tv_nsec) - (long long) (tt1.tv_sec * 1e9 + tt1.tv_nsec); printf("%ld, %lld, %lld, %ld, %ld\n", i, time1, time2, ans1, ans2); } } ``` 為了符合 div 的假設,因此需讓被除數 n 可以整除 d,才可以得到正確的計算結果 **因為 jemalloc 其實也可以處理非整除的問題,只是要將 debug的那一行註解掉,而且我覺得 div_init 的時間是需要被考慮進去的,所以我在自己的測驗code3有加入 init , 還有一個地方是他的 divisor 是從 2 加到 4999 ,我覺得要用亂數才比較能符合真實狀況,所以有進行修改。** :::info 小結論:我們從改良的實驗code可以發現,當遇到一個新的除數,因為要包含div_init的時間,在這個時候,quickdiv會較慢,在除同一個除數的時候 quickdiv 會較快,但這個結論跟我的最開始的實驗有產生矛盾。 ::: ### 矛盾解法1. 仔細看 `RinHizakura` 跟我最開始設計的實驗有一個**巨大的差異**,就是他的變數宣告都是 `uint64_t` 而我的是 `int` ,但此題的除法實做應該只須用到 `int` , quickdiv 是用乘法跟右移 取代掉 32bit除法 ,在過程中的確需要 64bit ,但卻不是全部都需要。 [test4 code 連結](https://github.com/haoyu0970624763/sysprog21-quiz2/blob/master/TestDiv/test4.c) 將test2進行微調,並把變數宣告成int 並且不包含 div_init ![](https://i.imgur.com/9gj0J5U.png) 可以發現,用 int 型別的確略快於 uint64_t , 然而卻不足以超越 quickdiv的時間 比對最初的資料跟 test1 , 1E 筆資料 int 型別大概要0.9秒 而 uint64_t 要1.5秒 ### 矛盾解法2. **重新 review 自己最開始的測試結果,我發現我測試的是整段 code ,而 code 當中還包含rand() 而 rand() 次數高達一億次,也因為包含這個非除法的部份,會導致自己偵測的時間變得不只包含除法,所以不準確。** ### hint **在測驗過程中應盡量避免極端值得出現,EX:數字太小 , 數字太小在 fastdiv 測試 會有異常高的測驗時間值,但我因為測試的資料量較大,所以我認為平均下來應該還好,然而最好還是要撇去極端值存在才能更好得比較效能** ## 新增: 第5題 延伸問題 char str[] 更換為 char *str 結果: `Segmentation fault` > char *p = “abc”; > >defines p with type ‘‘pointer to char’’ and initializes it to point to an object with type ‘‘array of char’’ with length 4 whose elements are initialized with a character string literal >If an attempt is made to use p to modify the contents of the array, the behavior is undefined. string literal 是靜態配置的,也就是 const char *str 嘗試去更動其內容是 undefined behavior ## 新增 4.倍數判斷 解說 ```cpp= bool divisible(uint32_t n) { return n * M <= M-1; } ``` 延伸自上一題的觀念 若以 D=3 此題為例子來看: 其中 `3*M=0x10000000000000002` 會產生overflow ,也就是所以大於 3 的數字乘上 M 都會發生overflow , 3*M 在這裡被視為 `0x0000000000000002` , 4*M 則代表 2+M...依此類推 而 6*M 則視為 4 ,也就是說 當 3的倍數乘上 M , 最後的數值 會小於或小於 M (不確定是哪個) 。 但有一點很重要的是, M 是 $\left \lceil \dfrac{2^k}{d} \right \rceil$​ (上高斯值),舉個例子說明 EX: 當我們取 k=4 (bit數為4的意思) 而 d=4 , 則 M = 4 = (0xF/4)+1 取 n=4 , n*M = 16 (overflow ,修正為 0 < M) 取 n=5 , n*M = 20 (overflow ,修正為 4 = M) 取 n=6 , n*M = 24 (overflow ,修正為 8 > M ) 取 n=7 , n*M = 28 (overflow ,修正為 C > M ) 取 n=8 , n*M = 32 (overflow ,修正為 0 < M ) (0xF/4) 實際上只有 3.x 的值 , 但取上高斯則為 4 ,會導致一個情況是 n * M 造成的overflow 進行修正後的值 = 取上高斯的值,導致 n*M=M ,但是這情況是不整除的。 所以要 -1 修正,把上高斯的值去掉。 ## 新增: 6.改寫 singleNumber 這題舉例子說明的話 例1: 1,1,1,3 bit 0 =1 出現 4 次 , 而 bit1=1 出現 1 次 , 其餘 bit X=1 出現 0次 ,最後要回一個數字 bit 0,1 = 1 例2 : 1,1,1,3,5,5,5 bit 0 =1 出現 7 次 , 而 bit1=1 出現 1 次 , bit2=1 出現 3 次其餘 bit X=1 出現 0次 ,最後要回一個數字 bit 0,1 = 1 從這裡我們可以發現,要去紀錄 bit X 出現 3k+1 次的時候 ( k >= 0 ) , 出現 3 次的則是不重要 因此我們可以做簡化,紀錄出現 3k+1次的,紀錄出現 3k+2 次的,出現 3k+3 次的 ```c= int singleNumber(int *nums, int numsSize) { int apprear1 = 0, apprear2 = 0, apprear3 = 0; int tmp1=0,tmp2=0,tmp3=0; for(int i=0;i<numsSize;i++) { apprear1 = (~(tmp2 | tmp1) & nums[i]) | tmp1 & ~nums[i] | tmp3 & nums[i]; apprear2 = tmp1 & nums[i] | tmp2 & ~nums[i]; apprear3 = tmp2 & nums[i] | apprear3 & ~nums[i]; tmp1 = apprear1; tmp2 = apprear2; tmp3 =apprear3; } return apprear1; } ``` apprear 1 紀錄出現 3k+1 次的 , apprear2 紀錄出現 3k+2 次的 , apprear 1 紀錄出現 3k+1 次的。 apprea1 要紀錄的有 * 1.沒有出現在 3K+1 次 也沒有出現在 3K+2 次 但是 nums[i] 有的 (因為這些應該被紀錄在 apprear2 跟 apprear3 裡面) * 2.出現 3K+1次 但在 nums[i] 沒出現的, * 3.出現在 3K+3 次也有出現在 nums[i]裡面的(因為 3k+4 = 3(k+1) +1 ) apprea2 要紀錄的有 * 2.出現 3K+1次 在 nums[i] 也有出現的, * 3.出現 3K+2 次 但在 nums[i] 沒出現的 apprea3 要紀錄的有 * 2.出現 3K+2次 在 nums[i] 也有出現的, * 3.出現 3K+3 次 但在 nums[i] 沒出現的 最後回傳 出現 3K+1 次的即可。 [改寫版 code 連結](https://github.com/haoyu0970624763/sysprog21-quiz2/blob/master/singleNumber.c) [改寫擴充版 code 連結](https://github.com/haoyu0970624763/sysprog21-quiz2/blob/master/singleNumber_expansion.c)

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully