面試向算法 - 二分法專題(二)

2021-01-18 百學原理
前言

在我看來,大部分面試的算法題從來都不是難在思維,而是缺乏系統的教學。它不像數學屬於普及的基礎教育,算法題目的大部分知識、技巧往往都局限於 competitive programming 當中 (比如各種 OI 競賽、 ACM 競賽等),這些都是大部分計算機行業從業者接觸不到的。它就像一個大群體中一個半封閉的小群體一樣,系統的知識就在那裡,只是我們很少會主動走進去。因此,我期望將這些知識給帶出來,就引申出了本系列文章和視頻。

在 《二分法(一)》當中我們已經分別講解了:

那麼,本篇的主要內容即是對剩下的四種應用進行深入分析。

最大/最小平均值(max/min average)

注意:搭配 B 站視頻更香奧!

正文 類型二:對結果進行二分

這類問題仍然屬於最優化問題,該問題的所有方案組成問題的 值域,需要求所有方案中的最優方案。

而上述通過二分法能夠處理的最優化問題需要滿足:值域 滿足某種特殊的單調性。用更加詳細的話講,就是能夠通過題目特性將值域分成兩個區間,一個區間滿足特性,另外一個區間不滿足特性

我們來看具體的例子:leetcode: 1011. 在 D 天內送達包裹的能力[1]

(圖 5.1)

題目需要求 D 天內能夠傳送所有包裹的最低運載能力,假設這個 最低運載能力 為 x。那麼很顯然 x+1 也能夠在 D 天內運完所有包裹。因此本題的 check 特性 就是 能在 D 天內運完所有包裹。我們可以根據該特性將值域分為兩個區間:

(圖 5.2)

那麼值域的範圍是多少呢?左邊界肯定不能

這裡值得注意的是,有些題目不會給出右邊界,因此我們需要根據經驗寫出右邊界,比如

分析到這裡,算法思路就結束了。最後結果 右區間的左端點,可以通過模板一來寫。而 check() 方法可以很容易根據定義用貪心寫出來。

def check(x):
    cnt = 0 # 統計傳輸天數
    curr = 0 # 當前的重量,不能超過 x

    for t in weights:
        if t > x: return False # 如果單個物品超過 x, 很明顯表示該運載能力不夠
        
        curr += t  # 加上當前物品,看看是否會超重,超重的部分下一次再運
        if curr == x:
            cnt += 1
            curr = 0
        elif curr > x:
            cnt += 1
            curr = t
    
    if curr > 0: cnt += 1 # 最後還剩餘 curr(不超過運載能力),只能放在下一次再運 
    return cnt <= D # 根據判斷總運輸天數是否超過 D,來判斷該 x 能夠滿足要求

本算法的時間複雜度為

有同學可能會問,該題的暴力樸素解法是什麼?

其實也很簡單,組合數學。將數組切分成 D 個區間,不同的切分方式為不同的方案。最優方案是

而我們根據題目中隱含的單調性將複雜度極大地降低,下面我們來看看同類型的其他題目,首先給出個定義。

在值域上二分的這類題目,其代碼區別主要在 check 方法,不同的題目會結合其他算法來進行考察。比如 leetcode: 778. 水位上升的泳池中遊泳[2]

(圖 5-3)

本題看似是個 hard 級別難度,如果你理解上上述的思路後,本題就變成了一個 easy 級別的。

同樣來分析題目,假設最少耗時為 t, 很明顯 t+1 也能夠到達右下角,而 t-1 不能。所以本題 值域是滿足單調性的。

所以我們可以二分值域,答案就是右區間的左端點。而本題不一樣的是 check() 方法,我們需要通過 dfs 來判斷值域中的值是否滿足題目要求。

l = 0 # 二分部分
r = 2500
while l < r:
    mid = (l+r)//2

    if check(mid): r = mid
    else: l = mid+1

return l

def check(t):
    if t < grid[0][0]: return False # 特判 (0, 0) 點

    vis = [[False]*n for i in range(n)] # 保存當前節點是否被訪問過
    dx = [1, -1, 0, 0] # 常見 dfs 寫法
    dy = [0, 0, 1, -1]

    vis[0][0] = True

    def dfs(x, y): # dfs 過程
        if x == n-1 and y == n-1: # 到達右下角
            return True
        
        for i in range(4):
            x1 = dx[i] + x
            y1 = dy[i] + y

            if x1 >= 0 and x1 < n and y1 >= 0 and y1 < n and not vis[x1][y1] and grid[x1][y1] <= t:
                vis[x1][y1] = True
                if(dfs(x1, y1)): return True

        return False
    
    if dfs(0, 0): return True # 調用 dfs
    else: return False

由於到目前為止,dfs 我們還沒有講過,所以這裡就不詳解介紹了,後續課程再說。

理解了本類題型,下面 18 道題目就很簡單了,作為課後習題留給大家。

leetcode: 1482. 製作 m 束花所需的最少天數[3]leetcode: 1292. 元素和小於等於閾值的正方形的最大邊長[4]leetcode: 875. 愛吃香蕉的珂珂[5]acwing(頭條 2019 筆試):680. 剪繩子[7]acwing(第八屆藍橋杯): 1227. 分巧克力[8]acwing(頭條 2019 筆試): 730. 機器人跳躍問題[9]leetcode: 793. 階乘函數後K個零[12]leetcode: 719. 找出第 k 小的距離對[13]類型三:

另一類可以被單獨拿出來的問題被稱為求 。這類問題的出現頻率很高且可以通過二分法來解決。

我們先通過一道簡單的題目來看看為什麼被稱為 -  leetcode: 410. 分割數組的最大值[14]。

圖 5-4

(圖 5-4)

本題雖然標為 hard 級別,但仍然屬於入門的難度。根據題意,需要設計一個算法,使 m 個子數組各自和的最大值最小,轉換為更簡單的公式就是:,這裡假設最終結果為 t,可以直接得出下面三個推論:

一定存在一種切割方式使得

所以,值域範圍是存在單調性的。

而我們求的 右區間的左端點,用整數域二分模板一就在合適不過了。

而具體的值域為 。

代碼的主要區別仍然是在 check() 方法的實現上。前面我們有講過,可以通過貪心來實現。具體為什麼呢?儘管貪心的數學證明一直是個不太好處理的事(一般通過反證法或者構造法來證明)。我們仍然可以構造出貪心策略來 - 每次切割時使切割開的區間和儘可能靠近傳入的 。如下代碼所示:

def check(x: int) -> bool:
    total, cnt = 0, 1 # cnt 為 1 是由於剩下最後的部分沒有處理
    for num in nums:
        if total + num > x:
            cnt += 1
            total = num
        else:
            total += num
    return cnt <= m

下面我們看一道 NOIP 2015 提高組 的一道題 - acwing: 519. 跳石頭[15]。

(圖 5-5)

題目要求給出 最多移走 M 塊石頭之後,最短跳躍距離的最大值,轉換成公式就是 。

假設該值為 ,同樣可以得出 3 個推論:

值域滿足單調性,可以根據是否存在移動方法將 值域範圍分為兩個區間。

很明顯,這是求左區間的右端點,用模板二的代碼即可。稍微變化的仍然是

def check(x):
    
    cnt = 0 # 統計需要移動的石頭數量
    last = 0 # 上一個未移動的石頭位置
    for i in range(1, n+1):
        if d[i] - last < x: # 如果當前石頭到上一個未移動石頭距離不足 x,則當前石頭需要被移走
            cnt += 1
        else:
            last = d[i]
    
    return cnt <= m

是不是很簡單就解決了看似 hard 級別的題目,如果題目想出的更加複雜一點,可以將二分和圖論結合,比如課後習題中的一道,這裡就不講了(如果不會,可以在答疑群裡面提出)

課後習題(6 道)

leetcode plus: 774. minimize-max-distance-to-gas-station[16]類型四 - 求最大平均值 / 最小平均值

第四類可以通過二分法可以解決的有趣問題就是 最大平均值/最小平均值。舉個抽象的例子,假設存在一個數組

(圖 5-6)

題意告訴我們每個可能方案需要滿足 區間大小 ,那麼暴力樸素的思路是什麼?

枚舉所有可能的方案,而方案數為 , 而判斷單個方案的時間複雜度是多少呢?

樸素做法是 前綴和技巧 達到

我們將上述題意用公式表示出來就是:,假設結果為

很顯然,值域 具有單調性,可以劃分為兩個區間:(這裡使用

由於 值域 是實數域,所以我們可以直接套用 實數二分模板即可

那麼該如何判斷某個值 - 也就是

對於所有 最大平均值/最小平均值 的思路是類似的(大家多手推幾遍就有感覺了,算是通用思路),需要進行如下圖 5-7 所示的變形即可:

(圖 5-7)

在這裡,我們運用了簡單的 一維前綴和貪心 的優化(具體技巧在後續課程會詳細講解),將

def check(x):
    s = [0] * (n+1)
    minV = [0] * (n+1)
    for i in range(1, n+1):
        s[i] = s[i-1] + a[i] - x
        minV[i] = min(minV[i-1], s[i])
    
    for i in range(m, n+1):
        if s[i] - minV[i-m] >= -1e-6: # 值得注意,由於是浮點數,所以在一定範圍保證精確即可
            return True
    
    return False

最後由於我們要取下底取整,需要 math.floor(r*1000)。

題目 acwing: 02. 最佳牛圍欄[20] 就是直接對上題進行了描述的替換,思路和分析模型完全一模一樣,這裡就不再重複了。

本題除了二分,還有通過斜率求解的

同樣,如果我們想要稍微增加本類題目的難度,可以將其與其他基礎算法結合,比如圖論,同樣的課後習題中 codeforces edu 中有一題即使如此。

課後習題(共 6 題)

acwing: 361. 觀光奶牛(圖+二分)[23]類型四 - 求某種特殊的第 k 大 / 小

最後一類要介紹的是在一個或多個有序數組中尋找合併後的第 快速選擇算法 或者還未講到的 來實現,但是下面要講的題目無法非常方便的通過這兩種算法來求解(要麼時間複雜度過大、要麼代碼實現過於複雜)。

我們先來看 leetcode: 668. 乘法表中第k小的數[25]:

(圖 5-8)

樸素方式是先將所有序列合併然後直接得出 第 k 小數,如果合併方式是 的方式,那麼時間複雜度為

根據題意,第

假設第

對於任意的

所以,上面兩個結論對於整個值域是具有單調性的,比如我們可以構造:右區間的左端點(構造左區間右端點也同樣的思路)。

那麼如何快速求得

本類題目的第二個特性就是有序序列符合某種連續性。

比如乘法表:

因此,我們可以在 那麼算法總體的時間複雜度就是

def check(x):
    cnt = 0

    for i in range(1, m+1):
        cnt += min(x // i, n) 
    
    return cnt >= k

非常容易的就通過二分思路解決看似 hard 級別的題目。

下面我們來看一道稍微變化的題目,leetcode: 878. 第 N 個神奇數字[26],雖然是 hard 級別,但是難度仍然不高:

(圖 5-9)

樸素做法是在 神奇的 特性,統計的策略是:

但是該時間複雜度達到

那麼題意是否有什麼特性來降低複雜度呢?假設第

那麼根據上面的特性將 右區間的左端點,使用模板一代碼即可。

def gcd(a, b):
    return gcd(b, a % b) if b else a 

def check(x):
    cnt = x // A + x // B  - x // LCM

    return cnt >= N

l = 1
r = N * min(A, B)

LCM = A // gcd(A,B) * B 

最小公倍數,我們就直接通過歐幾裡得算法求出最大公約數來得到了。

課後習題(共 5 題)

leetcode: 786. 第 K 個最小的素數分數[27]其他涉及題目

在算法題目中結合多種技巧解決一道題目是很常見的事,下面就給大家列舉一些二分法結合其他算法的題目,這些題目就不講解了,有問題可以在答疑群裡面提出來。

數學+二分

628. 美麗的數(google kickstart 2016 round E problem B)[31]

二分+dp: acwing: 472. 跳房子[32]

二分+差分:acwing: 503. 借教室[33]

貪心+二分:  LIS 問題(動態規劃專題講解)

總結

二分法專題到這裡就基本結束了,本專題總共涉及到 60 道 題目,以 LeetCode 難度劃分基本屬於 hardmedium 偏上的題目,類似的題目還有很多,但是基本都大同小異。掌握了本篇的這幾種主要題型,相信大家以後再面對二分類題目時就可以信手拈來了。

最後我們再回顧下我們的內容:通常我們將二分法分為整數域上的二分和實數域上的二分。

整數二分的細節在於終止邊界、左右區間取捨時的開閉情況,來避免漏掉答案或者造成死循環模板一模板二

通用 5 種二分類型題目為:

最大/最小平均值(max/min average)參考資料[1]

leetcode: 1011. 在 D 天內送達包裹的能力: https://leetcode-cn.com/problems/capacity-to-ship-packages-within-d-days/

[2]

leetcode: 778. 水位上升的泳池中遊泳: https://leetcode-cn.com/problems/swim-in-rising-water/

[3]

leetcode: 1482. 製作 m 束花所需的最少天數: https://leetcode-cn.com/problems/minimum-number-of-days-to-make-m-bouquets/

[4]

leetcode: 1292. 元素和小於等於閾值的正方形的最大邊長: https://leetcode-cn.com/problems/maximum-side-length-of-a-square-with-sum-less-than-or-equal-to-threshold/

[5]

leetcode: 875. 愛吃香蕉的珂珂: https://leetcode-cn.com/problems/koko-eating-bananas/

[6]

leetcode: 287. 尋找重複數: https://leetcode-cn.com/problems/find-the-duplicate-number/

[7]

acwing(頭條 2019 筆試):680. 剪繩子: https://www.acwing.com/problem/content/682/

[8]

acwing(第八屆藍橋杯): 1227. 分巧克力: https://www.acwing.com/problem/content/description/1229/

[9]

acwing(頭條 2019 筆試): 730. 機器人跳躍問題: https://www.acwing.com/problem/content/description/732/

[10]

acwing: 1028. 複製書稿: https://www.acwing.com/problem/content/description/1030/

[11]

codeforces edu 8 道題目: https://codeforces.com/edu/course/2/lesson/6/2/practice

[12]

leetcode: 793. 階乘函數後K個零: https://leetcode-cn.com/problems/preimage-size-of-factorial-zeroes-function/

[13]

leetcode: 719. 找出第 k 小的距離對: https://leetcode-cn.com/problems/find-k-th-smallest-pair-distance/

[14]

leetcode: 410. 分割數組的最大值: https://leetcode-cn.com/problems/split-array-largest-sum/

[15]

acwing: 519. 跳石頭: https://www.acwing.com/problem/content/521/

[16]

leetcode plus: 774. minimize-max-distance-to-gas-station: https://leetcode-cn.com/problems/minimize-max-distance-to-gas-station/

[17]

acwing: 2436. 串分割: https://www.acwing.com/problem/content/description/2438/

[18]

codeforces 4 道經典題目: https://codeforces.com/edu/course/2/lesson/6/3/practice

[19]

a_0, a_1, ... , a_{n-1}]

[20]

acwing: 02. 最佳牛圍欄: https://www.acwing.com/problem/content/description/104/

[21]

acwing: 2430. 送禮物: https://www.acwing.com/problem/content/description/2432/

[22]

某公司筆試題: https://www.acwing.com/community/content/537/

[23]

acwing: 361. 觀光奶牛(圖+二分): https://www.acwing.com/problem/content/description/363/

[24]

codeforces 3 道經典題目: https://codeforces.com/edu/course/2/lesson/6/4/practice

[25]

leetcode: 668. 乘法表中第k小的數: https://leetcode-cn.com/problems/kth-smallest-number-in-multiplication-table/

[26]

leetcode: 878. 第 N 個神奇數字: https://leetcode-cn.com/problems/nth-magical-number/

[27]

leetcode: 786. 第 K 個最小的素數分數: https://leetcode-cn.com/problems/k-th-smallest-prime-fraction/

[28]

acwing: 1236. 遞增三元組: https://www.acwing.com/problem/content/description/1238/

[29]

codeforces 3 道經典題目: https://codeforces.com/edu/course/2/lesson/6/5/practice

[30]

leetcode: 483. 最小好進位: https://leetcode-cn.com/problems/smallest-good-base/

[31]

628. 美麗的數(google kickstart 2016 round E problem B): https://www.acwing.com/problem/content/description/630/

[32]

acwing: 472. 跳房子: https://www.acwing.com/problem/content/description/474/

[33]

acwing: 503. 借教室: https://www.acwing.com/problem/content/description/505/

相關焦點

  • 面試手撕算法系列:二分法
    最近春招開始了,面試面著面著一言不合就開始手撕代碼手撕就手撕,接下來我打算寫幾個專題講講面試中手撕的常見題目 這些都是LeetCode上有的題目 手撕無非就是 樹、鍊表、二分、字符串這些常用的數據結構二分法查找,也稱為折半法,是一種在有序數組中查找特定元素的搜索算法。
  • 算法競賽小專題系列(1):二分法、三分法
    清華大學出版社二分法和三分法是算法競賽中常見的算法思路,本文介紹了它們的理論背景、模板代碼、典型題目。1. 二分法的理論背景在《計算方法》教材中,關於非線性方程的求根問題,有一種是二分法。所以,二分法的複雜度是O(logn)的。例如,如果函數在區間[0, 100000]內單調變化,要求根的精度是10-8,那麼二分次數是44次。  二分非常高效。所以,如果問題是單調性的,且求解精確解的難度很高,可以考慮用二分法。  在算法競賽題目中,有兩種題型:整數二分、實數二分。
  • 程式設計師面試題,200億個數字找中位數,不給分桶算法,怎麼辦?
    海量數據裡面如何尋找一堆數字的中位數,是一道非常常見的面試題。據稱,這個題目是騰訊前首席技術執行官Tony推崇的一個面試題,被加入的騰訊的面試題題庫。今天我們就來討論下這個題目,有一個存放著200億個數字的文件,存放著32位整型,可能會有重複的數字,現在想從這堆數字中找到他們的中位數。
  • 程式設計師挑戰:如何讓一個技術小白搞懂二分法檢索?
    圖源:unsplash在向萌新介紹計算機科學時,教授基本算法可能是最具挑戰性的部分之一。它介於假設性問題和抽象性思維之間,十分棘手。但或許可以不必這麼困難。有一個小眾的觀點:只要能清晰地構造問題,任何一位善於思考的人都可以對一個基本算法問題提出優秀的解決方案。不相信嗎?可以找一個朋友試驗一下。我敢打賭,即使他們沒有任何技術背景,最後他們也能開發出一個二分法檢索的算法,並且理解這個概念。
  • 二分法
    二分法是什麼呢?百度上的定義是:「對於區間[a,b]上連續不斷且f(a)·f(b)<0的函數y=f(x),通過不斷地把函數f(x)的零點所在的區間一分為二,使區間的兩個端點逐步逼近零點,進而得到零點近似值的方法叫二分法。」        這個定義比較複雜,我們把它分成幾部分來看。
  • 教學研討|用二分法求方程的近似解·教案·課件
    本節課是以上節課的「連續函數的零點存在性定理」為確定方程解所在區間為依據,從求方程近似解這個側面來體現「方程與函數的關係」;而且在「用二分法求函數零點的步驟」中滲透了算法的思想,為學生後續學習算法的內容埋下伏筆。
  • 漫畫:二分法深度剖析(第二講)
    今天是小浩算法「365刷題計劃」第67天。繼續為大家分享二分法系列篇的內容,看一道比較簡單的題目。這道題目是比較簡單,但我認為同時也是非常經典,建議大家掌握!根據之前說過的二分法模板,要使用二分法,我們當然要找到Left,Right,Mid,那在這裡,Mid 自然被作為最終我們要找的平方根的值(不像上一道題,Mid是作為速度,不太容易被想到),而 Left 和 Right,我們採用 1 和 x/2。
  • 算法分析——二分法查詢
    題目 假設在一個已經排好序的有序序列(N個元素,升序排列),使用二分法進行查詢(使用遞歸實現) 原文收錄在 公眾號 慕陽源碼 感興趣或者有問題的小夥伴 聯繫我哦
  • 數據結構與算法-2
    舉例:大文件在有限的資源下進行操作、二分法、快速排序、合併排序。回溯定義:回溯算法實際是一個類似枚舉的搜索過程,在搜索中發現原先選擇並不優或達不到目標,就返回一步嘗試別的選擇。走不通(剪枝)就退回再走的技術稱為回溯法。藉助遞歸去解決,類似DFS。
  • 武術'二分法'
    隨著武術運動自身的發展和人們對武術文化的研究不斷向縱深推進,武術文化的內容和內涵都在發生變化,對武術的定義和分類也在不斷發生變化。為了區別表達不同形式和內容的武術在流傳過程中的不同文化特點,並進行優化項目管理,目前武術界流行一種把武術劃分為傳統武術和競技武術的分類方法。也就是本文所指的武術「二分法」。  武術「二分法」具有一定的「隱蔽性」。
  • 使用Python和C語言實現二分法查找(折半查找)
    二分查找 叫折半查找,它對要查找的序列有兩個要求,一是該序列必須是有序的,二是該序列必須是順序存儲的。二分法查找算法的原理如下:1、 如果待查對象為空返回-1,表示查找不到目標元素2、 如果待查序列不為空,則將中間元素與要查找的目標元素進行匹配看是否相等,如果相等表示 查找成功,返回該中間元素的索引如果不相等再比較這兩個元素的大小
  • JAVA編程——二分法查找
    一、二分法檢索過程二分法檢索(binary search)又稱折半檢索,二分法檢索的基本思想是設數組中的元素從小到大有序地存放在數組(array)中(註:二分法查找的關鍵,首先數組元素必須從小到大有序排列),(1)首先將給定值 key 與數組中間位置上元素的關鍵碼(key)比較,如果相等,則檢索成功
  • 網際網路公司最常見的面試算法題大集合
    很多求職者都會在LeetCode刷上一遍,面試官也喜歡在上面挑選各類題目。LeetCode是一個美國的在線編程網站,收集了各個大廠的筆試面試題,對找工作的畢業生和開發者來說,非常有價值。不過LeetCode上面的題目很多都是考察應聘者對基礎知識的應用,適合進行練習編程基礎或者準備面試。
  • 二分法思維
    二分法思維是一種哲學思維,是指把一樣東西看作兩個部分來看待。比如一個人做一件事情,不是做對了,就是做錯了。買一個包包,不是買的假貨,就是買的真貨。        答案:  1.有必要  2.沒必要        類似上面的例題,每個提問都只有兩個答案的這種方式,就叫二分法思維。
  • 2020 圖算法工程師面試基礎、要點
    為你總結面試必備的知識要點,助你面試成功。 這段時間面試連連,幾輪下來的感受就是,好點兒的公司對細節摳的很細,希望求職者能夠對使用的算法以及這個算法的其它觸類旁通的領域都能夠有系統的理解。
  • 算法中的微積分:5大函數求導公式讓你在面試中脫穎而出
    事實上,所有機器學習算法的本質都是數學問題,無論是支持向量機、主成分分析還是神經網絡最終都歸結為對偶優化、譜分解篩選和連續非線性函數組合等數學問題。只有徹底理解數學,才能正真掌握這些機器學習算法。Python中的各種資料庫能幫助人們利用高級算法來完成一些簡單步驟。
  • 《Python程式設計師面試算法寶典》PDF超清版開源了文末附下載方式
    、分類歸納,提煉出算法面試的各種應對技巧,是一本Python程式設計師算法面試的圖書寶典。√ 採用抽絲剝繭式分析,深入解釋計算機科學的底層邏輯——算法及原理。√ 包括60多個算法題目,針對性強,拿來就用。通過實戰學習解題思路。《Python程式設計師面試寶典》是一本介紹Python程式設計師面試的圖書寶典。
  • 五分鐘學編程:怎樣才能學好筆試面試最愛考察的算法
    第二次認識算法,還是在研究生期間找實習工作的時候,面試的時候總有一些關於算法知識的考察,這些算法題比之前自己學數據結構的時候要更難一些,比如要讓你描述一下快排的過程,或者是二分查找的過程,由於是電話面試居多,一般都不會考察太複雜的問題。第三次認識算法,是在面試了頭條這類對算法要求極高的公司之後。
  • ——從思想與表達「二分法」談起
    在著作權法中,思想與表達二分法是一項重要原則,並對著作權的保護意義重大。一、思想與表達二分法原則的含義與規定思想與表達二分法原則將作品分為思想與表達兩方面,著作權法只保護對於思想觀念的獨創性表達,而不保護思想觀念本身。
  • 武林至尊,寶刀屠龍——略談易經中的陰陽二分法
    陰陽二分法的基本原理為:任何事物的產生與發展都是陰陽兩種力量相互作用的結果,陰陽兩種力量的此消彼長構成了事物千變萬化的發展狀態,或者反過來說,在事物紛繁複雜的表面下都隱藏著陰陽兩種相互作用力量。也正因為如此,任何事物都可以一分為二,也都可以二合為一。