這是M小白實驗室第一篇娛樂性科普文章
筆者博客:mwhitelab.com
筆者公衆號:技術雜學鋪
又到了過年的時候,每到這個時候,你總是能見到自己幾乎沒印象但父母就是很熟的親戚。
而且關係凌亂到你自己都說不清。
不信?那就來幾道測試題。
2019年親戚關係測試題正式開始。請完成如下填空。
- 爸爸的姐姐的女兒的女兒 = ________
- 媽媽的姐姐的弟弟的女兒 = ________
- 爺爺的弟弟的女兒的兒子 = ________
- 媽媽的媽媽的弟弟的兒子 = ________
參考答案如下:
- 表外甥女
- 舅表姐/舅表妹
- 堂姑表兄/堂姑表弟
- 舅表舅父
啥?
由於不知道該如何稱呼,面對陌生的親戚時,我們只能說一句“您好”,附帶一個尷尬又不失禮貌的微笑。
BUT! 作爲21世紀的資深網民(手機重度依賴患者),我們應該使用新時代的技術來解決這個尷尬的局面。那就是使用軟件——親戚關係計算器。
在微信小程序中搜索“親戚計算器”會顯示很多類似的程序。
操作界面和普通的計算器類似,不過數字和加減乘除鍵都變成了“父” “母” “兄” “姐” 這樣的關係。
以後遇見類似有不確定該如何稱呼的親戚,直接這樣用程序就可以了。
再BUT! 作爲21世紀的新型人才(這個詞是我現想的,我也不知道是啥意思),我們不能只能滿足於使用,更應該探求其本質,瞭解親戚關係計算器是如何實現的。
實現方法一:死記硬背法
關係 | 稱謂 |
父親的父親 | 爺爺 |
父親的母親 | 奶奶 |
父親的姐姐 | 姑媽 |
父親的妹妹 | 姑媽 |
…… | …… |
該方法嘗試記下所有親戚關係的結果,當用戶想要查詢某一種關係時,直接查表。
假設親戚之間的關係只有父、母、兄、弟、姐、妹、子、女八種。(先不考慮夫妻關係)
那麼像父親的父親,哥哥的女兒這樣簡單的 “XX的XX” 的關係有 8*8 = 64種可能,而 “XX的XX的XX” 有8*8*8 = 512種可能,四個關係的有4096種可能!如此大量的關係,光是把這些可能一個一個地列舉出來就會讓程序員精神崩潰。
然而這樣的死記硬背法是可行的,之前我們看到的微信小程序(其源代碼已公佈在github上)就是用這個方法來實現的:
我們先對關係進行簡化,比如在 ‘我’ 是男性的情況下,那麼‘我的兒子的父親的XX’在正常情況下就可以化簡成 ‘我的XX’。
再比如 母親的丈夫 = 我的父親,兄弟的父母=我的父母,姐姐的姐姐 = 姐姐等等。
接着,我們人爲給出六七百條最常用的關係和其對應的稱謂。任何一條用戶輸入的關係,其化簡後如果在已知的六七百條關係中,我們則返回其對應的稱謂,否則,返回不知道。
實現方法二:網狀圖
在親戚關係中,所有的名詞可以分爲兩類。
一類是稱謂,也就是我們如何稱呼其他人的,如父親、母親、爺爺、姑姑、舅舅等。
另一類是基礎關係,是稱謂的子集,由父親、母親、兒子、女兒、哥哥、弟弟、姐姐、妹妹、丈夫、妻子,這十個詞組成。任何一個稱謂都可以由基礎關係來表達。
如 爺爺 = 父親的父親;姥爺 = 母親的父親;太爺爺 = 父親的父親的父親。
圓角長方形框內爲稱謂,箭頭爲基礎關係(先不考慮丈夫與妻子這兩個關係)。我們可以用如下圖來表示“我”的親戚關係:
我們對圖中的每一個圓角長方形(稱謂)計算其八種關係(父母兒女兄弟姐妹)。
比如,對於上圖,我們接着計算父親和母親的八種關係對應的人,獲得如下圖:
之後對於得到的這張圖,接着計算每個圓角長方形(稱謂)對應的關係, 不斷地畫下去,可以得到一張巨大的網狀圖。
這張網狀圖可以無限延伸(如父親 的 父親 的 父親 的 父親……),我們根據實際情況,將網狀圖擴展到足夠用的地步就行了。
關係網
網狀圖的每一個稱謂和關係都是我們人爲確定了。不過其工作量遠遠小於“死記硬背法”。因爲對於一個經過四個關係的問題,如:父親的兒子的父親的父親=爺爺,母親的母親的女兒的父親=姥爺,一個很小的網狀圖就能確定這個問題的結果。而“死記硬背法”則需要記錄下所有 “XX的XX的XX的XX” 對應的結果才行。
網狀圖如何實現
不懂編程的朋友可跳過本節往下看。
C語言可以使用結構體來表示稱謂,用指針來表示關係(這裏沒有考慮父親的兒子可能是自己,哥哥,弟弟多種可能)。
struct node {
struct node *father;
struct node *mother;
struct node *big_bro;
struct node *small_bro;
struct node *big_sister;
struct node *small_sister;
struct node *son;
struct node *daughter;
};
python可以使用字典。這裏主要用python寫一個簡單的示意程序,具體代碼見github:
1.建立數據庫(該工作量十分龐大,這裏只展示幾個例子):
me = {'f':'父親','m':'母親','bb':'哥哥','sb':'弟弟','bs':'姐姐','ss':'妹妹','son':'兒子','dau':'女兒'}
father = {'f':'爺爺','m':'奶奶','bb':'伯父','sb':'叔叔','bs':'姑媽','ss':'姑媽','son':['我','哥哥','弟弟'],'dau':['我','姐姐','妹妹']}
mother = {'f':'姥爺','m':'姥姥','bb':'大舅','sb':'小舅','bs':'大姨','ss':'小姨','son':['我','哥哥','弟弟'],'dau':['我','姐姐','妹妹']}
…………
2.建立中文名與變量的對應關係:
name2var = {'我':me,'父親':father,'母親':mother,'哥哥':big_bro,\
'弟弟':small_bro,'姐姐':big_sister,'妹妹':small_sister,\
'兒子':son,'女兒':daughter}
relation2char = dict(zip(me.values(), me.keys()))
import numpy as np
# 考慮返回值可能不止一個 如父親的兒子可能爲[‘我’,‘哥哥’,‘弟弟’]
def returnNext(names,relation):
return_name = []
for name in names:
return_name.append(name2var[name][relation2char[relation]])
return list(set(np.array(return_name).flatten()))
3.使用一個函數,封裝所有操作:
def getName(relation_name):
relationships = relation_name.split('的')
name = [relationships[0]]
for relation in relationships[1:]:
name = returnNext(name,relation)
return name
4. 使用:
輸入 | 輸出 |
getName(‘我的父親的兒子’) | [‘哥哥’, ‘我’, ‘弟弟’] |
getName(‘我的母親的女兒’) | [‘姐姐’, ‘我’, ‘妹妹’] |
getName(‘我的父親的兒子的父親’) | [‘父親’] |
一些細節
首先,是性別:如果‘我’是女性,那麼‘我的父親的兒子’可以爲[‘哥哥’,‘弟弟’],而不可以包含‘我’。(上述代碼沒實現)
另外,關於夫妻關係:在正常情況下,男性稱謂只可以有‘妻子’,女性稱謂只可以有‘丈夫’。(上述代碼沒實現)
第三,多種可能:‘我的父親的兒子’ 可以是[‘我’,‘哥哥’,‘弟弟’],再若是再往後計算,如‘我的父親的兒子的兒子’ ,需要同時考慮‘我的兒子’,‘哥哥的兒子’,‘弟弟的兒子’這三種可能。(上述代碼已實現)
最後,給出一個線上的親戚關係計算器。該計算器爲開源代碼,由mumuy開發。