Leetcode【470兴喂、478蔼囊、497、519衣迷、528】

題目描述:【Math】470. Implement Rand10() Using Rand7()
解題思路:

這道題是用等概率的 Rand7()([1, 7])產(chǎn)生等概率的 Rand10()([1, 10])畏鼓。

因?yàn)槭且a(chǎn)生等概率的 Rand10(), 因此諸如 Rand7() + Rand7() // 2 的做法就不用想了(不是等概率)壶谒。

但是有一條重要的數(shù)學(xué)性質(zhì):調(diào)用兩次等概率的 RandN() 可以產(chǎn)生等概率的 Rand(N^2)()云矫,如調(diào)用兩次等概率的 Rand7() 可以產(chǎn)生等概率的 Rand49()。

這就好辦了汗菜,我們可以用 Rand7() 先產(chǎn)生 Rand49()让禀,得到等概率的 [1, 49]挑社,然后隨機(jī)產(chǎn)生一個(gè)數(shù)字,如果是 [41, 49]巡揍,丟棄了也無(wú)妨痛阻。如果是 [1, 40],對(duì) 10 取模腮敌,就可以得到等概率的 [1, 10]阱当。

那么,問(wèn)題的關(guān)鍵在于糜工,如何 Rand7() 兩次來(lái)產(chǎn)生 Rand49() 呢弊添?公式如下:

  • Rand49() = Rand7() + 7 * (Rand7() - 1)

即 Rand7() 得到 [1,2,3,4,5,6,7],7 * (Rand7() - 1) 得到 [0,7,14,21,28,35,42]啤斗,然后對(duì)應(yīng)元素相加即可得到等概率的 [1,49] 了(畫(huà)一個(gè)方陣對(duì)應(yīng)著加一下)表箭。

此解決方案可以推廣到所有 RandN 生成 RandM (N < M) 的場(chǎng)景(如果 N >= M,直接丟棄超過(guò) M 的數(shù)字即可)钮莲。

Python3 實(shí)現(xiàn):
# The rand7() API is already defined for you.
# def rand7():
# @return a random integer in the range 1 to 7

class Solution:
    def rand10(self):
        """
        :rtype: int
        """
        while True:
            tem = rand7() + (7 * (rand7() - 1))  # 構(gòu)造均勻分布 1~49
            if tem <= 40:  # 丟棄大于40的數(shù)字免钻,保證等概率
                return (tem - 1) % 10 + 1  # mod10 可以得到均勻分布的[1,10]

問(wèn)題描述:【Math】478. Generate Random Point in a Circle
解題思路:

這道題給出圓的半徑 r 及圓心坐標(biāo),隨機(jī)生成一個(gè)在圓內(nèi)或圓上的坐標(biāo)崔拥。

很簡(jiǎn)單极舔,只需要隨機(jī)生成兩個(gè)正負(fù)半徑范圍內(nèi)的浮點(diǎn)數(shù) x、y链瓦,然后判斷是否滿(mǎn)足 x^2 + y^2 <= r^2(= 表示可以在圓上)拆魏,如果不滿(mǎn)足,重新生成兩個(gè)浮點(diǎn)數(shù)慈俯;滿(mǎn)足的話(huà)渤刃,各自加上圓心坐標(biāo)就是最后的結(jié)果。

Python3 實(shí)現(xiàn):
class Solution:

    def __init__(self, radius: float, x_center: float, y_center: float):
        self.radius = radius
        self.x_center = x_center
        self.y_center = y_center

    def randPoint(self) -> List[float]:
        while True:
            x = random.uniform(-self.radius, self.radius)
            y = random.uniform(-self.radius, self.radius)
            if x * x + y * y <= self.radius * self.radius:
                return [self.x_center + x, self.y_center + y]


# Your Solution object will be instantiated and called as such:
# obj = Solution(radius, x_center, y_center)
# param_1 = obj.randPoint()

問(wèn)題描述:【Binary Search】497. Random Point in Non-overlapping Rectangles
解題思路:

這道題是給一些不重疊的矩陣贴膘,從這些不重疊的矩陣中隨機(jī)采樣一個(gè)整數(shù)點(diǎn)(采樣點(diǎn)包括矩陣邊界)卖子。

因?yàn)榭赡苡泻芏嗑仃嚕覀冇忠WC等概率的選取一個(gè)點(diǎn)刑峡,因此我們可以先計(jì)算出每個(gè)矩陣能采樣多少個(gè)點(diǎn)洋闽,并計(jì)算總采樣點(diǎn),然后 num = random.randint(1, 總采樣點(diǎn)) 采樣一個(gè)數(shù) num突梦。接下來(lái)诫舅,我們要計(jì)算這個(gè) num 落在了哪一個(gè)矩陣中。這時(shí)我們可以像下面的 Leetcode 528 題一樣宫患,按順序求出矩陣的前綴和刊懈,然后使用二分查找的方法計(jì)算 num 落在了哪一個(gè)矩陣中。最后,我們?cè)傧裣旅娴?Leetcode 519 題一樣俏讹,將 num 映射到二維坐標(biāo)上当宴,返回結(jié)果畜吊。

舉個(gè)例子泽疆,假設(shè)有三個(gè)矩陣 rects = [[2,1,5,3], [1,1,2,2], [1,2,3,4]],我們?nèi)菀椎挠?jì)算出每個(gè)矩陣可以采樣的點(diǎn)數(shù)分別為 [12,4,9]玲献,總采樣點(diǎn)數(shù)為 12+4+9 = 25殉疼,同時(shí)計(jì)算前綴和 pre_sum = [12,16,25]。我們假設(shè)隨機(jī)了一個(gè) [1, 25] 區(qū)間的數(shù) num = 15捌年,那么根據(jù) pre_sum瓢娜,就可以使用二分查找的方法,得到 15 應(yīng)該位于 rects[1] 這個(gè)矩陣中礼预,并且可以知道 15 是 rects[1] 中的第 3 個(gè)樣本點(diǎn)(15-12=3)眠砾。最后,我們把這第 3 個(gè)樣本點(diǎn)映射到二維坐標(biāo)中托酸,假設(shè)按照從左到右褒颈,從下到上映射,我們將會(huì)得到坐標(biāo) [1, 2]励堡。

Python3 實(shí)現(xiàn):
class Solution:

    def __init__(self, rects: List[List[int]]):
        self.rects = rects
        self.pre_sum = [0] * len(rects)  # 前綴和
        self.pre_sum[0] = (rects[0][2] - rects[0][0] + 1) * (rects[0][3] - rects[0][1] + 1)
        for i in range(1, len(rects)):
            self.pre_sum[i] = self.pre_sum[i-1] + (rects[i][2] - rects[i][0] + 1) * (rects[i][3] - rects[i][1] + 1)

    def pick(self) -> List[int]:
        rand = random.randint(1, self.pre_sum[-1])
        low, high = 0, len(self.pre_sum) - 1
        # 根據(jù)前綴和進(jìn)行二分查找谷丸,找到隨機(jī)數(shù)落在哪個(gè)矩陣中(low即為位置)
        while low <= high:
            mid = (low + high) // 2
            if self.pre_sum[mid] < rand:
                low = mid + 1
            elif self.pre_sum[mid] >= rand:
                high = mid - 1
        # 找到對(duì)應(yīng)矩陣
        x1, y1, x2, y2 = self.rects[low][0], self.rects[low][1], self.rects[low][2], self.rects[low][3]
        rows = x2 - x1 + 1
        cols = y2 - y1 + 1
        if low != 0:  # 在該矩陣中的rand值,取值為[1, rows*cols]
            rand -= self.pre_sum[low-1]
        rand -= 1  # 將rand值范圍變?yōu)閇0, rows*cols-1]应结,便于映射到二維坐標(biāo)上
        # 左下角對(duì)應(yīng)0刨疼,右上角對(duì)應(yīng)rows*cols-1,從左到右鹅龄,從下到上   
        i = x1 + rand % rows
        j = y1 + rand // rows
        return [i, j]


# Your Solution object will be instantiated and called as such:
# obj = Solution(rects)
# param_1 = obj.pick()

問(wèn)題描述:【Array】519. Random Flip Matrix
解題思路:

這道題是說(shuō)給一個(gè) n_rows 行 n_clos 的矩陣揩慕,初始化為 0。如果調(diào)用 flip 函數(shù)時(shí)扮休,隨機(jī)將 0 的位置置為 1迎卤,返回矩陣坐標(biāo)。如果調(diào)用 reset 函數(shù)肛炮,重置矩陣為全 0止吐。

因?yàn)闀r(shí)間消耗比較多的肯定是 random() 的調(diào)用次數(shù),但是矩陣大小可以為 10000*10000侨糟,且 flip 函數(shù)最多調(diào)用 1000 次碍扔,因此我們可以隨機(jī)多次 random() 函數(shù),找到非 0 的位置秕重,把它置為 1不同。

因?yàn)橐笳{(diào)用的 random() 次數(shù)最少,因此我們可以通過(guò)調(diào)用一次 random() 來(lái)計(jì)算出矩陣坐標(biāo)(而不用兩次)。例如 n_row = 2二拐,n_cols = 3服鹅,我們隨機(jī)一個(gè) [0, n_rows*n_cols-1] 區(qū)間的數(shù) num,然后使用 i = num // n_cols百新、j = num % n_cols 就可以得到對(duì)應(yīng)矩陣的行列坐標(biāo)(i企软,j)。

因?yàn)檫€要考慮到空間饭望,所以直接開(kāi)一個(gè) n_rows 行 n_clos 的矩陣空間肯定是浪費(fèi)的仗哨,而且在調(diào)用 reset 函數(shù)也是 O(n^2) 的復(fù)雜度,這樣肯定不行铅辞。實(shí)際上厌漂,我們只需要保存那些置為 1 的坐標(biāo)到一個(gè)集合中。在 flip 函數(shù)中斟珊,每次 random() 一個(gè)坐標(biāo)苇倡,判斷其是否在集合中(O(1) 復(fù)雜度),如果在囤踩,說(shuō)明這個(gè)坐標(biāo)之前已經(jīng)被置為 1 了旨椒,那就重新 random() 一個(gè)坐標(biāo);如果不在高职,說(shuō)明這個(gè)坐標(biāo)之前沒(méi)有被置為 1钩乍,就把當(dāng)前坐標(biāo)加入到集合中,同時(shí)返回該坐標(biāo)怔锌。在 reset 函數(shù)中寥粹,我們也只需要 .clear() 清空這個(gè)集合即可。時(shí)間復(fù)雜度和空間復(fù)雜度都極大地降低埃元。

Python3 實(shí)現(xiàn):
class Solution:

    def __init__(self, n_rows: int, n_cols: int):
        self.n_rows = n_rows
        self.n_cols = n_cols
        self.one = set()  # 保存置為1的那些坐標(biāo)

    def flip(self) -> List[int]:
        while True:
            rand = random.randint(1, self.n_rows*self.n_cols) - 1
            row = rand // self.n_cols  # 根據(jù)隨機(jī)的數(shù)字計(jì)算矩陣行列坐標(biāo)
            col = rand % self.n_cols
            if (row, col) not in self.one:  # 之前沒(méi)有被置為1的坐標(biāo)
                self.one.add((row, col))
                return [row, col]

    def reset(self) -> None:
        self.one.clear()  # 清空置為1的那些坐標(biāo)


# Your Solution object will be instantiated and called as such:
# obj = Solution(n_rows, n_cols)
# param_1 = obj.flip()
# obj.reset()

題目描述:【Binary Search】528. Random Pick with Weight
解題思路:

這道題實(shí)際上是給一個(gè)數(shù)組 w涝涤,其中 w[i] 代表位置 i 的權(quán)重。寫(xiě)一個(gè)函數(shù)岛杀,可以隨機(jī)地獲取位置 i阔拳,選取位置 i 的概率與 w[i] 成正比。

舉個(gè)例子类嗤,w = [1,3,2,2]糊肠,我們需要隨機(jī)的獲取位置 i (0、1遗锣、2货裹、3),選取位置 0 的概率是 1/(1+3+2+2) = 1/8精偿,選取位置 1 的概率是 3/(1+3+2+2) = 3/8弧圆,選取位置 2 的概率是 2/(1+3+2+2) = 2/8赋兵,選取位置 3 的概率是 1/(1+3+2+2) = 2/8。

弄懂題意后搔预,我們可以想到先隨機(jī)一個(gè) [1, 8] 中間的數(shù)霹期,比如 7,那么 7 是哪個(gè)位置的呢拯田?很明顯屬于位置 3历造;如果隨機(jī)的數(shù)是 3,則屬于位置 1勿锅。

因此帕膜,我們可以先求出 w = [1,3,2,2] 的前綴和,得到 pre_sum = [1,4,6,8]溢十,分別對(duì)應(yīng)位置 0 到 3,然后隨機(jī)產(chǎn)生一個(gè) num = random.randint(1, 8)(1 <= num <= 8)达吞,使用二分查找的方法確定落在哪個(gè)對(duì)應(yīng)的位置即可张弛。時(shí)間復(fù)雜度為 O(logn)。

Python3 實(shí)現(xiàn):
class Solution:

    def __init__(self, w: List[int]):
        self.tot = sum(w)  # 總和
        self.pre_sum = [0] * len(w)  # 前綴和
        self.pre_sum[0] = w[0]
        for i in range(1, len(w)):
            self.pre_sum[i] = self.pre_sum[i-1] + w[i]
        

    def pickIndex(self) -> int:
        tot = self.tot
        pre_sum = self.pre_sum
        rand = random.randint(1, tot)  # 隨機(jī)產(chǎn)生一個(gè)數(shù)1<=rand<=tot
        low, high = 0, len(pre_sum) - 1
        while low <= high:  # 二分查找
            mid = (low + high) // 2
            if pre_sum[mid] < rand:
                low = mid + 1
            elif pre_sum[mid] >= rand:
                high = mid - 1
        return low  # 最終的對(duì)應(yīng)位置
        

# Your Solution object will be instantiated and called as such:
# obj = Solution(w)
# param_1 = obj.pickIndex()
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末酪劫,一起剝皮案震驚了整個(gè)濱河市吞鸭,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌覆糟,老刑警劉巖刻剥,帶你破解...
    沈念sama閱讀 211,561評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異滩字,居然都是意外死亡造虏,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,218評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)麦箍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)漓藕,“玉大人,你說(shuō)我怎么就攤上這事挟裂∠沓” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,162評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵诀蓉,是天一觀的道長(zhǎng)栗竖。 經(jīng)常有香客問(wèn)我,道長(zhǎng)渠啤,這世上最難降的妖魔是什么狐肢? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,470評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮埃篓,結(jié)果婚禮上处坪,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好同窘,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,550評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布玄帕。 她就那樣靜靜地躺著,像睡著了一般想邦。 火紅的嫁衣襯著肌膚如雪裤纹。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,806評(píng)論 1 290
  • 那天丧没,我揣著相機(jī)與錄音鹰椒,去河邊找鬼。 笑死呕童,一個(gè)胖子當(dāng)著我的面吹牛漆际,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播夺饲,決...
    沈念sama閱讀 38,951評(píng)論 3 407
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼奸汇,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了往声?” 一聲冷哼從身側(cè)響起擂找,我...
    開(kāi)封第一講書(shū)人閱讀 37,712評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎浩销,沒(méi)想到半個(gè)月后贯涎,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,166評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡慢洋,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,510評(píng)論 2 327
  • 正文 我和宋清朗相戀三年塘雳,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片且警。...
    茶點(diǎn)故事閱讀 38,643評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡粉捻,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出斑芜,到底是詐尸還是另有隱情肩刃,我是刑警寧澤,帶...
    沈念sama閱讀 34,306評(píng)論 4 330
  • 正文 年R本政府宣布杏头,位于F島的核電站盈包,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏醇王。R本人自食惡果不足惜呢燥,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,930評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望寓娩。 院中可真熱鬧叛氨,春花似錦呼渣、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,745評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至仁连,卻和暖如春蓝角,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背饭冬。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,983評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工使鹅, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人昌抠。 一個(gè)月前我還...
    沈念sama閱讀 46,351評(píng)論 2 360
  • 正文 我出身青樓患朱,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親扰魂。 傳聞我的和親對(duì)象是個(gè)殘疾皇子麦乞,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,509評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容