2015年5月26日 星期二

[86duino] 86Duino EduCake 101

2014年3月18日 星期二

[gae]用程式登入 google app engine 建立的服務

這裡用到的是 google 已說要廢棄的 ClientLogin。

這是我很久以前使用的方法,雖然 google 說已經廢棄,但現在還是可以使用。

 

要能夠被 GAE 認為是登入過的使用者,主要就是在 cookie 裡面,看到 auth 這個 token。
而要拿到 auth 這個 token 的值,就得從 google account authorization 拿到。
因此,第一步是知道 google account authorization 的網址:

https://www.google.com/accounts/ClientLogin

post request 裡面要用 application/x-www-form-urlencoded 包含幾個必要內容:

  • Email:登入者的帳號。
  • Passwd:登入者的密碼。
  • service:值為 ah。我忘記為什麼是 ah 了。對於登入 GAE ,填 ah 是可以的。
  • source:自己的 GAE 服務的名字。
  • accountType:"HOSTED_OR_GOOGLE",原因請參照文末的參考。

在填入以上必要資訊之後,傳送 post request 給 clientlogin 的 service,可以在 response 裡面拿到 Auth 的值。這就是參考裡面流程的第7步。

接下來,是跟 GAE 的事。要讓 GAE 知道你拿到 auth 的 token 並且幫忙導至想要的位置,要將 request 送去以下的網址:

http://你的GAE服務網址/_ah/login?continue=你想導去的網址&auth=拿到的token值

這樣,GAE 服務那裡的程式,從 users.get_current_user() 就會拿到值,便可以當做 user 已經 login 了。

在這兩個 request 裡,會用到 cookie。在 python 中,要使用有 cookie 功能的 http client,要使用 urllib2 與 cookielib 配合使用。範例如下:

cookiejar = cookielib.LWPCookieJar()
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookiejar))
urllib2.install_opener(opener)

如此,urllib2.urlopen 就會有 cookie 功能,才能夠以上兩個 post request 正常工作。完整的 python 範例如下:

import urllib
import urllib2
import cookielib

def passauth(login_address, login_pass, login_app, login_app_address):

  url = login_app_address
  users_email_address=login_address
  users_password=login_pass
  target_authenticated_google_app_engine_uri= 'http://%s' % login_app_address
  my_app_name = login_app
  cookiejar = cookielib.LWPCookieJar()
  opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookiejar))
  urllib2.install_opener(opener)

  auth_uri = 'https://www.google.com/accounts/ClientLogin'
  authreq_data = urllib.urlencode({ "Email":   users_email_address,
                                    "Passwd":  users_password,
                                    "service": "ah",
                                    "source":  my_app_name,
                                    "accountType": "HOSTED_OR_GOOGLE" })
  auth_req = urllib2.Request(auth_uri, data=authreq_data)
  auth_resp = urllib2.urlopen(auth_req)
  auth_resp_body = auth_resp.read()

  print 'auth_resp_body', auth_resp_body
  auth_resp_dict = dict(x.split("=")
                        for x in auth_resp_body.split("\n") if x)
  authtoken = auth_resp_dict["Auth"]
  serv_uri = target_authenticated_google_app_engine_uri
  serv_args = {}
  serv_args['continue'] = serv_uri
  serv_args['auth']     = authtoken


  full_serv_uri = "http://%s/_ah/login?%s" % (url,urllib.urlencode(serv_args))

  return full_serv_uri

full_serv_uri = passauth('xxxxx@gmail.com','yyyyy','zzzzz','zzzzz.appspot.com')
print 'full_serv_uri',full_serv_uri
serv_req = urllib2.Request(full_serv_uri)
serv_resp = urllib2.urlopen(serv_req)
serv_resp_body = serv_resp.read()
print 'serv_resp_body', serv_resp_body

 

參考:

https://developers.google.com/accounts/docs/AuthForInstalledApps?hl=zh-tw

2013年12月8日 星期日

[mosquitto]在 windows command 下,要傳送雙引號。

這應該算是 windows command prompt 的小技巧,只是我到現在要用 mosquitto 的 mosquitto_pub.exe 才遇到。要把雙引號送進批次檔或是執行檔送出雙引號,其跳脫符號就是雙引號。

因為每次執行 mosquitto_pub.exe 都要到安裝的目錄下,而且,topic 跟 host 每次都要手打,很想偷懶。

於是在順手的地方,例如 C:\User\user,放了一個叫做 send.bat 的批次檔。內容如下:

"C:\Program Files (x86)\mosquitto\mosquitto_pub.exe" -t test -h 192.168.1.111 -m %1

然後,只要我在 C:\User\user 底下執行

send test

就會得到

test

若是執行

send test test

卻只會得到

send test

若要得到 test test,就要執行

send "test test"

接下來,我想要送一個 json 物件,{"id":"ricky","msg":"hello world!"},若直接執行

send {"id":"ricky","msg":"hello world!"}

,會變成

{id:ricky

我們看一下實際執行的指令是:

C:\User\user>"C:\Program Files (x86)\mosquitto\mosquitto_pub.exe" -t test -h 192.168.1.111 -m {"id":"ricky"

原因是,參數有先傳進批次檔,雙引號算是控制符號之一,真的送出去的字串裡,雙引號都被拿掉了。

所以不論是執行檔或是批次檔,要傳送 json 這種東西,雙引號的部份要用雙引號跳脫,所以就長成這樣:

send.bat "{""id"":""ricky"",""msg"":""this is test""}"

跟 Visual Basic (VB) 一樣,是用雙引號來跳脫。我還試了很久的反斜線…,兩邊沒有要統一一下嗎?

[sublime] 在 mac 開啟 .hgrc

在開啟檔案的時候,按 cmd + shift + .

隱藏檔就會跑出來。

嗯,很搞笑。

2013年12月2日 星期一

[python] 基因演算法探討數字遮罩的最佳解

前言

在 facebook 上,python taiwan 出現一個討論

 

楊文誠
這是我學校作業,但是我真的看不懂,可以幫我翻譯一下嗎?拜託~~
我連我要幹嘛都不知道了......

1391532_552537671495061_279533194_n

圖1:一個學生來問作業的解答,引起廣泛的討論。[1]

題目是找出合乎一個範圍內的數字,以它跟某一特定的數字作 or 運算可得到的值越大越好,而其本身越小越好。這可以當成是一個尋找遮罩的問題。

雖然有留言說已經有各種解法出現在 ptt 上(可見不只一個人來討解答),我就愛自己試一試。題目有附註說,用蠻力法要扣30分,但是我很難界定蠻力法是什麼樣子?難道不能把 i in range(L, U+1) 都算過一遍嗎?

遇到這種問題,當我沒辦法直覺想到演算法,就會交給上天/上帝,也就是基因演算法。誰叫這題目正好命中基因演算法的解形式呢!

人工智慧演算法,通常應用在正規演算法找不到或是正規演算法要算很久的情況。如果把所有解都算過一遍以得到最佳解都來得及,為什麼要用人工智慧得到可能最佳解呢?所以現在人工智慧演算法已經不流行了。在運算力如此可怕的時代,想用人工智慧演算法的人,準確的說會使用基因演算法的人,大概就像我這樣把演算法丟給上帝的懶墮蟲吧。我猜 ptt 的各種解法應該不會有用基因演算法的,因為不保證答案會對喔!可以說根本是來搞笑的!(若說是做先期研究的開路先鋒,就好聽很多。)

在這次的文章裡,可以好好體會了一下基因演算法的眉眉角角,在實際應用上會增加不少經驗值。同時,我也在這次的研究中,學到用基因演算法的方法論來研究問題、優化演算法。

第一章 基因演算法的基礎

基因演算法是模仿生物演化的理論,留下好基因,求得最佳解的一種演算方法。它從基因的互換、突變,來得到新的基因,透過適應性的評估哪些是決定比較好的基因並留下,再進行基因的互換、突變以得到新的基因,不斷重復這過程,直到滿意為止。

以上是理論,那實務上真的要解決問題,要面對的是先回答另一些問題:

  1. 基因的形式為何?
  2. 基因的互換方式為何?
  3. 基因的突變方式為何?
  4. 適應性如何評估?
  5. 要留下多少基因?
  6. 要看幾代?

這些問題,也就是問題之前的問題,是演算法之外的事,一般是不會在教科書上教的。多數的教科書,多是只給了一兩個範例,但是沒有說為什麼要這麼問這些問題。多把文章重點放在解釋基因演算法的原理或實作的程式碼。反而上述的幾個問題該「如何回答這些問題」成為了實務上最難解的問題。有些時候上面的六個問題沒有答案,就不能用基因演算法了。而這些答案都是 case by case,領域不同、題目不同,則答案就不同。甚至答案可以有很多,得看實際執行後的結果及效率來決定答案好不好,是不是最佳。經驗也會影響答案的內容,而且答案的內容也會影響實作,在這麼交互影響之下,答案討論起來是很繁瑣又不具通用性,或許是教科書不寫的原因吧?但是重申一次在實務上,這很重要!

我這裡按順序說明我對於各個問題的初步答案,第一個問題,基因的形式,在這裡剛好就是 mask 的樣子,由一串0與1組合而成的字串,不過為了計算方便,把它轉成 32-bit 的正整數數字,也剛好合題目的需求。剩下的問題,答案先暫時決定如下:

  • 基因的互換方式:暫定為「最佳基因的前半部加本次最佳基因的後半部」。
  • 基因的突變方式:暫定為「隨機挑一個bit,0、1互換」
  • 適應性評估:正好為題目需求,與 mask 運算完的數字越大越好,而 mask 本身則是越小越好。那就是按運算完的數字排列,挑最大的,若運算完數字同樣大小,則按 mask 本身數字挑比較小的。
  • 留下多少基因:跟基因互換有關聯,暫定為「已知最佳基因,與本次獲得最佳基因」,因此只有兩個。
  • 暫定1000代

第二章 完全亂數與暴力法的差別

在決定前一章的六個問題後,我開始實作我的演算法。程式中有一些基礎函式要先做:

import random
string_length = 32

def _bin_string(num):
    # bin(63) => '0b111111'
    return bin(num)[2:].zfill(string_length)

def _invert_at(s, index):
    return bin(int(s, 2) ^ 2 ** (index))[2:].zfill(string_length)

def _int_from_bin(s):
    return int(s, 2)

def score(num):
    '''分數函數'''
    return num | N

def f1(num1, num2):
    '''交配'''
    s1 = _bin_string(num1)
    s2 = _bin_string(num2)
    return _int_from_bin(s1[0:16] + s2[16:32])

def f2(num):
    '''突變'''
    i = random.randint(0, 31)
    s = _bin_string(num)
    ss = _invert_at(s, i)
    return _int_from_bin(ss)

def is_acceptable(num):
    '''存活函數'''
    if num > U:
        return False
    elif num < L:
        return False
    else:
        return True

接下來才是依照問題寫好的基因演算法:

L = 201310
U = 3567891
N = 30951344

p1 = L
p2 = U

i = 0
for j in range(1000):

    m1 = f1(p1, p2))
    m2 = f2(m1)

    if not is_acceptable(m1):
        print 'skip m1', j, m1
        continue
    if not is_acceptable(m2):
        print 'skip m2', j, m2
        continue

    score0 = score(p1)
    score1 = score(m1)
    score2 = score(m2)
    if score1 > score2:
        d = m1
    elif score1 < score2:
        d = m2
    else:
        d = min(m1, m2)
    score3 = score(d)
    if score0 > score3:
        p2 = d
    elif score0 < score3:
        p2 = p1
        p1 = d
    else:
        p1 = min(p1, d)
        p2 = max(p1, d)

    i += 1
    print j, p1, p2

print i, p1, p2

 

第一次執行,咦!沒有輸出。經過檢查,m1 不在 LU 的範圍內,所以完全得不到存活的子代基因,遇到這麼不順利的情況,非改不可。把 m1 的產生方式改成 f2(f1(p1, p2)),經過 10 次(每次 1000 代)測試,發現平均有存活的子代基因約 430 次左右,當然答案都不一樣,10 次得到最好的解是 mask 2603087,分數是 33554431。

那麼暴力法呢?程式如下:

p1 = L

for j in range(L, U + 1):

    score0 = score(p1)
    score1 = score(j)
    if score0 > score1:
        pass
    elif score0 < score1:
        p1 = j
    else:
        p1 = min(p1, j)

    print p1, score(p1), j

超級短的程式,得到的結果是 mask 2603087,分數是 33554431。

在觀察基因演算法給出的 10 次答案的時候發現,在求適應性分數來說,幾乎 10 次有 9 次的分數是正解。但是求 mask 的部份,卻不一定會是正確答案。拿這個答案交出去可能不會得分。或許,我再多跑幾次,可能得到更好的 mask,可能得到更差的 mask,但實務上來說,若我不跑一次暴力法,我永遠不會知道 mask 是不是最小,score 是不是最大。能不能接受基因演算法的答案,真的是看情況。

另一方面,兩者的執行環境都在 windows 的 console 下跑,並且取消所有迴圈中的 print,若不取消,暴力法的執行時間被 print 拖累太多太多,我認為不公平。最後結果是暴力法的時間是0:00:01.95,而基因演算法的時間是 0:00:00.14,約為暴力法的 7% 的執行時間。

第三章 從基因演算法的角度思考優化

在第二章中雖然基因演算法有得到答案,但其實是不確定是否為最佳答案。假設答案是不能接受這種不確定性,那如何思考更好的演算法,可以在可忍容的資源下把所有可能的答案都算過一遍?從基因演算法的角度出發就是要問如何更有效率的演化?提高演化效率的方向有,(1)基因的存活率、(2)基因交配的方法、(3)基因突變的方法。這三個方向不完全是獨立的,它們之前會隨問題而有不同程度的某種關聯性。

隨機挑選子代

首先從提升存活率來看,之前的子代產生是經由交配、突變產生,有時子代會跑出規定的上下限而直接淘汰。是否能直接從解集合裡產生子代,以提升存活率,進而能更廣泛提取驗證子代呢?首先試試不經交配、突變,直接亂數選取子代的效果。

f4_i = range(L, U + 1)
def f4(num, restart=False):
    '''不經交配、突變,直接亂數選取子代'''
    global f4_i
    if len(f4_i) == 0:
        raise Exception('len(f4_i) == 0')
    if restart:
        f4_i = range(L, U + 1)
    i = random.randint(0, len(f4_i) - 1)
    si = f4_i[i]
    del f4_i[i]
    return si

改寫原先程式,把存活函數拿掉,避免產生重覆的子代:

from datetime import datetime
fnstart = datetime.now()
'''不經交配、突變,直接亂數選取子代'''
result = []
p1 = L
p2 = U
for k in range(10):
    i = 0
    for j in range(1000):

        m1 = f4(p1)
        m2 = f4(p1)

        score0 = score(p1)
        score1 = score(m1)
        score2 = score(m2)
        if score1 > score2:
            d = m1
        elif score1 < score2:
            d = m2
        else:
            d = min(m1, m2)
        score3 = score(d)
        if score0 > score3:
            p2 = d
        elif score0 < score3:
            p2 = p1
            p1 = d
        else:
            p1 = min(p1, d)
            p2 = max(p1, d)

        i += 1
        #print j, p1, p2

    #print i, p1, p2
    result.append([p1, score(p1)])

print 'execute time', datetime.now() - fnstart

def cmp_result(x, y):
    if x[1] != y[1]:
        return y[1] - x[1]
    else:
        return x[0] - y[0]
print sorted(result, cmp_result)

結果,執行時間變長了,都是 10 * 1000 的迴圈,用掉約 37 秒。得到的答案更不正確。

execute time 0:00:37.332000
[[2603343, 33554431], [2603343, 33554431], [2603343, 33554431], [2603343, 33554431], [2603343, 3554431], [2603343, 33554431], [2603343, 33554431], [2603343, 33554431], [3128447, 33554431], 2620006, 33554422]]

首先,維護解集合的代價太高,在這例子中,高過低存活率的計算代價。另外得到的答案變異數變大,暗示前次基因的交配策略,在選擇子代中有顯著的助益。這是個很重要的暗示。

找出基因固定段

由前段知道交配策略有顯著助益,因此朝向基因段交互關係研究。

L、U、N 三個數的2進位表示為:

L = 00000000000000110001001001011110
U = 00000000001101100111000100010011
N = 00000001110110000100011110110000

從問題的本質來看,N 與 mask 作用後要最大,最好是 mask 都為 1,但是 mask 又要最小,策略就是 mask 把 N 的 0 的地方換成 1 的是最有效率。

U = 00000000001101100111000100010011
M = 11111110001001111011100001001111 (第一步)

然而,因為有 L、U 作為存活函數的參數,是解集合的上下限,在比 U 最前面的 1 更前面的 0 是不可以變動。所以 M 要把比 U 最高位 1 之前的 1 全變成 0:

U = 00000000001101100111000100010011 (有點對不齊)
M = 00000000001001111011100001001111 (第二步)

因此,基因交配以最佳解的前半部為子代的前半部,是把比較小的 M 留下來,正好是提高演化效率的方法,算是誤打誤撞。不過,在了解 N 、 M 及 U 的關係之後,可以把 M 的基因分成兩段:

M[0:U_first_1_pos+1] 這一段是固定不動,會維持較小的 M,並且有合理的天然上限,交配、突變時會增加存活率。
M[U_first_1_pos+1:] 這一段就是可以改變的基因段。

在第二步這裡得到的 M 是可以得到最高分數的 M,如果它滿足 L <= M <= U,那麼答案就得到了。這裡,又不小心地得到了跟暴力演算法一樣的答案 M = 00000000001001111011100001001111 = 2603087。

如果情況不是呢?那就要在 M[U_first_1_pos+1:] 這一段基因裡變化。

在 M[U_first_1_pos+1:] 的優化

在變動段的基因段裡,依然照著最佳演化的目標前進,繼續觀察 N、M 的關係。

00000001110110000100011110110000 (N)
00000000001001111011100001001111 (M)

第二步得到的 M,在它不滿足 L <= M <= U 的條件下,必須要改變某些基因,讓它符合 L <= M <= U。在所有的基因的變化會有幾種可能,它的影響如下:

  1. N:0, M:0,把 M 的 0 變成 1 ==> 分數變大,mask 變大。
  2. N:1, M:1,把 M 的 1 變成 0 ==> 分數不變,mask 變小。
  3. N:0, M:1,把 M 的 1 變成 0 ==> 分數變小,mask 變小。
  4. N:1, M:0,把 M 的 0 變成 1 ==> 分數不變,mask 變大。

也因此,上面的情況裡,優先測試的是 2 (當 M > U) 或 4 (當 M < L)。若都無法找到滿足 L <= M <= U 的解,則再嘗試 1 (當 M < L) 或 3 (當 M > U) 。但是在第二步得到的 M,不會有第 1 種及第 2 種情況。也就是只剩下第 3 種(分數變小,mask 變小)、第 4 種(分數不變,mask 變大)。所以到了這裡可以明確的說:

當 M > U 的時候,採取「尋找 N:0, M:1 的 bit,把 M 那邊的 bit 反轉」的策略進行演化。

當 M < L 的時候,採取「尋找 N:1, M:0 的 bit,把 M 那邊的 bit 反轉」的策略進行演化。

這樣的基因組應該全部列舉出來了吧?仍然以例題為例,有13個 bit 是 N:0, M:1,所以有 2^13 = 8192 種變化。而 N:1, M:0 有 9 個 bit,所以有 2^9 = 512 種變化。如果仔細想想,又可以再使用 L, U 去掉不合適的基因。可以更準確。不論 L, U, N 怎麼變化,依照步驟到這裡的全列舉,已經會是很小的範圍了。

結尾

在這個題目中,用了基因演算法,一不小心就在短時間內得到不錯的答案。以找到答案為前提之下,基因演算法可以提供一個初步的探索。而以尋找問題解法為目的的時候,也可以從基因演算法的角度出發,思考基因的最佳演進方向及演進方法。在探求的過程中,把問題的解決方法思考出來,也算是一個很好的訓練解析問題的方法。人工智慧演算法不是沒用,只是式微。跟老兵一樣?

參考:

[1] [https://www.facebook.com/photo.php?fbid=552537671495061&set=gm.10152422989873438&type=1&relevant_count=1&ref=nf]

2013年11月16日 星期六

[osx] 軟體 raid 簡單操作 SOP 及軟體 raid 不支援 3T 硬碟?

最近興高彩烈買了 3T 硬碟一顆,準備要跟另外一顆 3T 硬碟合體變成 raid1,但每每在製作過程中會出現無法將硬碟加入 raid 這種錯誤。實在讓人想不通。2T 的硬碟都可以正常工作的,如果真的是軟體限制,希望新版的 osx 可以支援。

順便記下軟體 raid 的簡單操作。

組合成 raid:

  插入兩顆硬碟。

  打開磁碟工具程式

  先隨便點一顆硬碟,再點右邊 RAID 頁籤

  在 「RAID 設置名稱」那一欄填入一個名字,這會是之後掛載上來的磁碟名字。

  「卷宗格式」選 Mac OS擴充格式 (日誌式)

  「RAID 類型」選 映射的 RAID 設置

  按下該頁籤左下方的 + 號,會出現一個灰色的硬碟圖示。

  從左方的硬碟中,把你要的兩顆硬碟拖到灰色的硬碟圖示下方。按下製作按鈕。

  接下來等工具程式,它會把剩下工作做完,然後掛載一個新的磁碟到系統裡,就完成了。

遇到硬碟有問題:

  打開磁碟工具程式,當硬碟有問題的時候,會看到紅色的字。

  點到那紅色字,畫面右邊會顯示狀況,它會告訴你哪一顆硬碟有問題,整個 RAID 設置會進到已降級的狀態。

  在已降級的狀態下,無法新增硬碟。可以做的動作有幾個,結果非常不同,一、對有問題的硬碟做「重建」,重建若完成,RAID 會回到已連線狀態;二、對有問題的硬碟按下 - 號,再按「降級」。RAID 設置會少掉一個硬碟,但還是 RAID 設置,狀態會回到已連線,就可以新加硬碟;三、對有問題的硬碟按「刪除」,這會使用 RAID 設置被取消,會跑出兩個硬碟都是同個名字,但資料都還在。

  所以,最好是先試重建,若硬碟問題不大,就可以再撐一陣子。重建時間很久,2T要等3~5天,若途中還有對該硬碟做動作,會等更久。

  若硬碟真的該換,則是對有問題的硬碟按下 - 號,再按降級,讓 RAID 回到已連線,然後再加新的硬碟,維持 RAID 的設置。

  刪除的動作我以後應該不會再去按了。除非是要把硬碟轉成一般用途。

[python] bit 操作與易經

從沒有機會需要操作 bit,沒想到寫易經的程式非得用到不可。

第一個最需要的是,把數字轉成 0101 的字串。這個內建的 bin() 函數可用,只是它會在字串前多加0b。依實際需要,得把 0b 去掉。

>>> bin(13)
'0b1100'
>>> bin(13)[2:]
'1101'

第二個就是把 0101 的字串轉回數字,這可以用 eval,或是 int('0b1100', 2) 來達成。

>>> int('0b1101',2)
13
>>> int('1101',2)
13

第三個,易經是 6-bits 系統,而且非常需要補 0 在高位數。

>>> bin(13)[2:].rjust(6,'0')
'001101'

第四個,因為我手上的易經參考書,低位寫在前面,高位寫在後面,我得把字串反過來。從字串轉回數字也得一樣照辦。

>>> bin(13)[2:].rjust(6,'0')[::-1]
'101100'
>>> int('101100'[::-1], 2)
13

第五個,對某個 bit 做反轉運算,因為對 6-bits 的操作沒有把握再加上高低位已經反轉,已經夠亂了,所以採取直接對字串操作,但因為 python 的字串沒法單獨對第某個字元操作,所以寫了個函數代替。主要是拆開字串為 list,操作完再組回去。index 從 0 開始。

def _invert_at(s, index):
    ss = list(s)
    if ss[index] == '0':
        ss[index] = '1'
    else:
        ss[index] = '0'
    return ''.join(ss)

>>> _invert_at('101100', 1)
'111100'

更新:經過研究測試之後,相同的功能可以用 bin(int(s, 2) ^ 2 ** (5 - index))[2:].rjust(6, '0') 來解決。

基礎工程結束,接下來應用開始。因為用 bit 來表達了,原本常用的 1~8,都得用 0~7 這種以 0 開始的 index 系統。

一般卜卦是在得到一個卦(叫做「本卦」)之後,都會再求一個「變卦」及「互卦」。在一般簡易數字型卜卦中,本卦、變卦、互卦的求法是:

  • 本卦是得到兩個 0~7 之間的數字,一個代表上卦,一個代表下卦。例如,上卦 1、下卦 7,就是 101100,豐卦。(下卦在前面,低位在前面)
  • 變卦是從 0~5 之間得到一個數字,代表動爻的位置,然後把本卦的對應的 bit 反轉。例如,101100,我們得到 2 爻動,就是得到 111100,大壯卦。
  • 互卦的求法是把本卦234爻做為下卦,把345爻做為上卦。011110,大過卦。

互卦用程式來表示很清楚:

>>> s = '101100'
>>> s[1:4] + s[2:5]
'011110'

接下來,用程式表示八宮卦象變化大概就是這樣:

def get_8_change(s, step):
    if step == 0:
        return s
    elif step == 1:
        return _invert_at(s, 0)
    elif step == 2:
        return _invert_at(s, 1)
    elif step == 3:
        return _invert_at(s, 2)
    elif step == 4:
        return _invert_at(s, 3)
    elif step == 5:
        return _invert_at(s, 4)
    elif step == 6:
        return _invert_at(s, 3)
    elif step == 7:
        ss = _invert_at(s, 2)
        ss = _invert_at(ss, 1)
        ss = _invert_at(ss, 0)
        return ss

拿坎宮的八個卦的程式是

s = '010010'
for i in range(8):
    s = get_8_change(s, i)
    print s, _int_from_bin_r(s), money64[s]

執行結果是:

010010 18 坎
110010 19 節
100010 17 屯
101010 21 既濟
101110 29 革
101100 13 豐
101000 5 明夷
010000 2 師

八宮卦象變化的函式 get_8_change 的 step 0~6 跟京房 16 卦一樣,可以做為基礎繼續改造。