要介紹代數系統之前要先了解二元運算
二元運算
為一個函數
唯一存在 使得 , 則稱為二元運算(binary operation)
舉幾個例子
, 則為上的一個二元運算
, 因為無定義所以不是在上的二元運算
而代數系統的正式定義如下
, 則和數個上的二元運算就稱為代數系統
代數系統上具有許多性質
以下介紹最常見的幾個
名稱 | 定義 | 備註 |
---|---|---|
封閉性(closed) | 滿足 | |
結合性(associative) | ||
交換性(commutative) | ||
單位元素(identity) | 元素和單位元素做二元運算後依然等於自己 | 分為左單位元素與右單位元素(同時存在會相等) |
反元素(inverse element) | 元素和反元素做二元運算後等於單位元素 | 分為左右反元素(不一定相等) |
反元素性質 | 的反元素存在 | |
消去性 | 左消去性: , 右消去性: |
而群(group)又分為以下幾種
名稱 | 定義 | 備註 |
---|---|---|
半群(semi group) | 滿足封閉性, 結合性的代數系統 | |
單群(monoid) | 滿足封閉性, 結合性, 具有單位元素的代數系統 | 有單位元素的半群, 單位元素唯一 |
群(group) | 滿足封閉性, 結合性, 具有單位元素, 反元素性質的代數系統 | 具有反元素性質的單群, 且必定具有消去性 |
交換群(abelian group) | 滿足封閉性, 結合性, 交換性, 且具有單位元素, 反元素性質的代數系統 | 具有交換性的群 |
定義 , 為n個等價類所成的集合
定義二元運算 , 滿足
為一個代數系統且具有
阿貝爾群就是一個代數系統並滿足以下特性
首先要了解什麼是complement, 補數
在做減法的時候, 例如111 - 79, 這個看似單純的算式卻因為要做到借位而變複雜
如果考慮以下設計111 - 79 = 111 + (100 - 79) + (1 - 100)
利用兩個減法取代原本的算式從而避免借位的操作
(100 - 79) 可以稱為-79的nine's complement
過往我們學到什麼是2補數呢?就是把某個二進位表示法(binary string)的數字取1補數(每個位元反轉), 之後加上1
例如以4個bits表示的數字當中, 0010的二補數就是1101 + 0001 = 1110
並且可以觀察到任何數和自己的2補數相加之後都會得到10000, 變成5 bits, 因為溢位處理會是0000, 這難道是巧合?
這時候要考慮到阿貝爾群, 計算機這樣設計編碼是有意義的
因為群的特性的關係, 任何兩數做了binary operation之後依舊會在原本數的集合當中, 這可以避免溢位帶來的錯誤
並且不管怎麼設計, 0 (identity element)的表示法是唯一的
以8位元表示法為例, 0是00000000, 而1的表示法是00000001, 而1的反元素(-1)呢?
在阿貝爾群中的元素都滿足, 我們知道(-1)存在並且唯一, 和(1)進行binary operation後會是identity element, 0
所以(-1)必然是11111111
但是如果從0開始不斷加1, 加了次後同樣也會得到11111111
那11111111到底是正數還負數?
此處不該以正數, 負數的觀點來思考電腦中的數字, 也就是他不是一個直線並且向兩側無限延伸的座標軸, 而是一個環形
(-x)並非負方向的x, 而是x的反元素
因此我們知道在阿貝爾群這樣的代數系統當中, 我們應當捨棄過往使用正數, 負數來看數字和符號的觀點, 從而用群的概念來思考數字
正負號只是端看電腦怎麼處理這些數字而已
以上討論的範圍還只涵蓋了整數還有加法形成的代數結構呢!
關於實數和乘法所形成的代數結構也有待探討
允許溢位的加減法和整數構成一個群
允許溢位的乘法構成一個環
注意到從以上阿貝爾群再推論到電腦設計編碼的方式可以知道
看到這裡可能有人會想, 程式語言通常會區分signed, unsigned
等不同資料型態, 那是否就要對正負號做額外處理?
實際上完全不需要, 因為電腦中的數字完全遵守阿貝爾群的定義, 因此不管你是用什麼正數負數丟進去, 對電腦來說他不會知道正數負數的概念, 他只會利用遵守阿貝爾群的二元運算來操作這些數字, 因此不需要區分指令集
然而這裡也牽涉到一個問題, 由於'溢位'的操作是把第i個bit後面的數字全部忽略, 這就牽涉到我們的電腦到底用幾個bits表示一個數字
然而現代電腦有8位元, 16位元,32甚至64位元表示法
每一種表示法就是一個阿貝爾群, 因為identity element 0 表示法不一樣
如果我們想要把資料搬遷到另一種表示法的環上, 例如8位元的1丟到16位元的1
則是把00000001, 左邊填充8個0即可
不過如果是1的反元素(-1)呢?在unsigned的情況, 一樣可以直接填充8個0
但若是signed的情況11111111丟到16位元的表示法依然應該要是(-1), 也就是1111111111111111, 這種情況就需要考慮符號
Operator | Function | Example |
---|---|---|
& |
bitwise AND | |
| |
bitwise OR | |
^ |
bitwise XOR | |
~ |
NOT | |
<< |
bitwise left shift | |
>> |
bitwise right shift |
在開始了解bitwise實用的地方前
應該先完成以下練習以熟悉基本的bitwise算數
C Bitwise Operators
Practice with bit operations
Stanford Bitwise Practice
利用man ascii
來觀察ascii code編碼
不需要硬背, 但需要瞭解規則
-1
: 全為1 (-1
和~1
不同)INT_MAX
: msb為0, 剩下全為1INT_MIN
: msb為1, 剩下全為0UINT_MAX
: 全為1bitvector
bitvector有以下幾種經典操作
通常要針對某個bit做操作, 要搭配bitmask
而bitmask的功能就是使得操作只對某個bit有效, 把剩下的bit給遮住
假設a, b
是bitvector
a
least significant bit : (a & 1)
a
: (a | 1)
i
th bit for a
(counting from right most) : (a | (1 << i))
a
: (a & ~1)
i
th bit for a
(counting from right most) : (a & ~(1 << i))
a
: (a ^ 1)
i
th bit in a
: (a ^ (1 << i))
a
with b
: a | b
a
with b
: a & b
b
from a
: a & ~b
除了針對單一個bit做操作, 也可以對數個bit操作
只要掌握mask的技巧
a
has either of two lowest bits on : a & 0x3
a
has both of two lowest bits on : (a & 0x3) == 0x3
a
: a &= 0xff
Constant mask應該要用16進位來表示, 因為人很難直覺地將10進位數字轉為2進位, 但是16進位轉2進位只需要一瞥
並且與其每次都要hard-coding 一個神奇的數字
不如利用C語言的前處理器#define CONSTANT VALUE
這樣的形式來定義mask
-1
or ~0
n
, all others off : 1 << n
n
least significant bits on, all others off : (1 << n) - 1
1 << 32
, MIN_INT
k
most significant bits on, all others off : ~(-1 >> k)
, ~(~0U >> k)
Bitwise Manipulation
假設x, y
都是32-bit有號數
1 << x
: 2的x
次方~x + 1
: x
的2補數, 也就是-x
x >> 31
: x
的正負號x &= (x-1)
: clear lowest "on" bit in x
(清除x
當中最低位的1)(x ^ y) < 0
: 如果x, y
不為同號數則為true