使用深度搜索分析心理調查問卷的研究
背景
昨天我女朋友發了一個心理調查問卷要我做。我向來對這種形似心理調查問卷嗤之以鼻: 一個人的性格怎麼可能由幾個簡單的問題決定。但作爲技術人員,我決定用技術的手段分析這份調查問卷,向女朋友證明其缺乏科學性。
調查問卷的原版如下圖
有興趣的朋友可以玩玩~
我想了解如下幾個問題:
1. 問卷是不是有環路?如果有環路,則可證明這份問卷不正規。
2. A 答案是不是不可能達到?因爲4個答案中,只有A答案沒有同的傾向。
3. 如果 A 答案能到達,那麼有多少種可能?佔比是多少?
分析
這種問卷調查有特點:每一個題目都對應多個選項;每一個選項,都對應一個題目或者答案。從一個人答題的路徑來看,其實這就是一個典型的圖結構。
結合我想了解的問題,有如下幾個方面需要考慮:
1. 證明圖沒有環路
2. 使用DFS證明A是可以達到的
3. 統計所有的可能的結果,算出A的佔比
建模
我打算使用 列表模式 構建圖
@list = [
[1],
[2,3],
[3,4],
[4,5,6],
[5,6,7],
[6,7],
[7,8,9],
[8,9],
[9,10,11],
[10,11,12],
[12,13],
[13,14],
[13,15],
[15,18,14],
[15,18],
[16,18],
[17,18,19,20],
[20,21,'B'],
[19,'C'],
[22,'A'],
[21, 'D'],
[22, 'B'],
['A', 'C', 'D']
]
圖的節點有 0 到 22, 再加上 A, B, C, D。
1. 因爲我不想處理下標,所以圖的起始位置,我設置成0;
2. 圖的節點,我簡單的用數組的下標表示;
3. 答案直接輸出,不需要放在節點的集合中;
DFS
圖有沒有環路
代碼如下:
def has_cycle?
recursion_stack = [false] * 23
check_cycle(0, recursion_stack)
end
def check_cycle(vertex, recursion_stack)
recursion_stack[vertex] = true
@list[vertex].each do |node|
next if over?(node)
return true if recursion_stack[node]
check_cycle(node, recursion_stack)
end
recursion_stack[vertex] = false
return false
end
其中 over?
方法是用來判斷遞歸是否結束。
def over?(point)
['A', 'B', 'C', 'D'].include?(point.to_s)
end
has_cycle
來判斷是否有迴路,我將一次完全迭代的節點是否訪問到了的信息保存在recursion_stack
- 它調用 深度搜索 算法實現的
check_cycle
運行得出的結果是 false
A 可以到達嗎?A如果可以到達,那麼A的可能性有多大?
@result = []
def dfs(node)
@list[node].each do |vertex|
if over?(vertex)
@result << vertex
next
end
dfs(vertex)
end
end
使用深度有限搜索, 將最後的結果保存在 @result
中,將結果打印出來。
def count_charactor(result, charactor)
result.count { |item| item == charactor }
end
dfs(0)
puts "一共有 %d 可能的答案" % @result.size
('A'..'D').to_a.each do |charactor|
puts "#{charactor}: %d 種可能, 佔比爲 %f" % [ count_charactor(@result, charactor), count_charactor(@result, charactor).to_f / @result.length]
end
結果如下:
一共有 27860 可能的答案
A: 8960 種可能, 佔比爲 0.321608
B: 3360 種可能, 佔比爲 0.120603
C: 8120 種可能, 佔比爲 0.291457
D: 7420 種可能, 佔比爲 0.266332
輸出能到答案A的100路徑
@i = 0
def dfs(node, stack)
stack.push node
@list[node].each do |vertex|
if over?(vertex)
@result << vertex
if vertex == 'A'
@i += 1
puts "到A的路徑有:"
puts stack.join(" -> ")
end
break if @i == 100
next
end
dfs(vertex, stack)
end
stack.pop
end
用一個棧stack
來保存路徑, 到A點將所有的結果輸出
總結
圖的算法應用真的很廣泛,值得深入研究。有時候將實時中的問題抽象成圖的問題,能讓我們從另外的一個角度看問題。再則,用ruby
實現圖的算法也是很簡單的。dfs
比bfs
簡單,但收到程序棧的限制。但比較適合本程序。