本文始發於個人公眾號:TechFlow
正文開始之前,我們先來講一個故事。
在很久很久以前,有一個萬人迷。
她從18歲開始就有數不完的追求者,追她的男生一個個在她的窗前排起了長隊。但是她挑來挑去,終究不覺得滿意。終於,這個萬人迷一天天長大,年老色衰,在她門口排隊的男生也越來越少。
她開始后悔拒絕男生時的輕率,懷念起了從前的榮光。她也不知道,最后她是會向現實妥協,選擇一個看起來遠不是那么好的男生共度一生,還是會就這么一直等下去。
這樣的故事其實並不罕見,知乎里關於剩男剩女以及婚嫁的問題屢見不鮮。選擇配偶也是我們人生當中的必經之路,蘇格拉底說過,人生就是一次無法重復的選擇,在婚姻這個問題上尤為明顯。
那么問題來了,如果我們是故事中的萬人迷,我們應該如何選擇配偶呢?
即使是真的萬人迷,她可以選擇的配偶也一定是有限的。我們可以做一個簡單的量化,假設她一年平均有30個追求者,她打算28歲結婚。那么從她18歲開始算起,假設她的魅力保持不變,她一共可以遇到300個潛在的配偶。
這個數字對於每個女生而言各有不同,但是它其實並不重要,並不會影響我們的計算過程。為了簡化計算,我們就假設它為n。接着,我們再進一步簡化模型,假設這n個男生排成一隊,一個一個地來發起追求。我們假設女生面臨每個追求者的時候只會有兩個選擇,一是直接拒絕,二是答應追求,從此牽手共度一生。
那么,我們來做一個好的決策呢?
和現實中一樣,一種比較聰明的做法是,先和前面的一些男生每個人都相處一段時間,做一個了解,摸清這些男生大概的水平底細之后再認真考慮。抽象成數學模型來,就是女生會直接拒絕掉前面k個男生,從第k+1個男生開始一一和前面k個男生比對。當一個比前面k個男生都要好的男生出現的時候,她果斷選擇接受,從此和他共度一生。
如此一來,這就成了一個數學問題,究竟這個k應該等於多少,才可以使得女生選中所有男生當中最好的那個的概率最大呢?
所以,我們應該怎么求出這個K呢?
對於某個固定的K,我們假設最佳配偶出現在了第i的位置。想讓他能被挑選中,必須要保證前面i-1個人中的最好的配偶出現在前K個人當中。也就是說如果真命天子前面沒有出現另一個優質的男生,會導致女生在遇見真命天子前就草草選擇。從這個問題上來說,真命天子也需要好的對手陪襯。
這個概率不難計算,是:\(\frac{K}{i-1}\)。
那么,我們對所有的i進行加權求和即可:
\(P(K)=\sum_{i=K+1}^n\frac{1}{n}\cdot \frac{K}{i-1}=\frac{K}{n}\sum_{i=K+1}^n\frac{1}{i-1}\)
我們假設n是一個很大的值,我們可以先算后面的部分。如果n足夠大,可以認為
我們令\(x=\frac{K}{n}\)
求積分,可以得到:
\(P(K)=x(ln(\frac{n}{K}))=x(ln(\frac{1}{x}))=-x\cdot ln(x)\)
我們對\(P(K)\)的求導,令它等於0,可以求出\(P(K)\)最大時\(x=\frac{1}{e}\)。這里的\(e\)就是數學當中經常出現的歐拉常數,也叫自然底數,\(\frac{1}{e}\)約等於37%. 那么,算到了這個結果,這個問題也就有了答案。
如果你是一個萬人迷,那么你應該拒絕掉前面37%的追求者,然后在剩下的63%的男士當中挑選一個比前面都強的作為配偶。那么你選到最佳配偶的概率達到最大值,它的概率為37%。
雖然有了答案,但是我們並不知道這個答案對不對,但是沒關系,我們是程序員,可以用代碼來模擬。
我們就按照萬人迷的配置來設定好了,假設她一生當中會面臨300個追求者。我們假設這三百個追求者的好壞層次不齊,按照分數排序,可以得到一個0到299的序號。排名越靠后,說明分數越大,男生越優質,然后我們再對這些男生進行亂序。
import random
def generateBoys():
boys = [i for i in range(300)]
random.shuffle(boys)
return boys
接着我們來編寫程序的主體,其實也很簡單,我們模擬進行許多次同樣的配偶選擇,模擬出我們通過這種策略能夠選中最佳配偶的概率,代碼並不難寫:
# iterations 是模擬擇偶的次數
def simulation(iterations=10000):
matched = 0
for i in range(iterations):
# 每次都創建新的追求者集合
boys = generateBoys()
# 最佳配偶的序號
best = max(boys)
maxi = 0
partner = 0
# 計算K, K=0.37 * 追求者總數
pickedNum = int(0.37 * len(boys))
for j in range(pickedNum):
maxi = max(maxi, boys[j])
# 一旦找到比前K個最好的都要好的,就結束
for j in range(pickedNum, len(boys)):
if boys[j] > maxi:
partner = boys[j]
break
# 判斷是否找到了最佳配偶
if partner == best:
matched += 1
return matched / float(iterations)
最后,我們運行代碼,得出的答案是0.3629。當然這也不是一個精確值,也是一個會波動的估算結果。迭代的次數越多,這個得到的結果越逼近真實值。用大量的實驗去測算某個事件發生的概率,這個也是統計學上常用的方法。
通過建模,我們把一個抽象的,無從下手的問題,簡化成了一個明確的數學問題。通過建立函數求最值的方法,求出了最優解。從結果上來看,如果真有一個姑娘能有這么多追求者,通過一種方法可以擁有37%的概率挑中她的真命天子,也算是非常棒了。
但是數學模型的是理想的,現實和理想總是有些差別。現實中,我們的時間精力是有限的,我們不一定有時間來一一衡量前面追求者的優劣。而且追求者的分布也不一定是隨機的,很有可能隨着我們自身的變化而變化。比如我們通過自己的努力,去往了更好的學校、公司,那么我們接觸到的異性也會更好。
不過盡管如此,這道算法問題對我們還是很有借鑒意義,希望能夠給大家帶來啟發。
今天的文章就到這里,希望大家有所收獲。如果喜歡本文,請順手點個關注吧。