1. 程式人生 > >聊聊左閉右開區間

聊聊左閉右開區間

《程式設計珠璣》裡說過:大約10%的專業程式設計師,才能夠正確地寫出二分查詢。儘管第一個二分查詢程式於1946年就公佈了,但是第一個沒有bug的程式在1962年才出現。

二分查詢易錯的原因之一,是比較範圍寫的不對,對於一些情況是錯誤的。記住比較範圍是左閉右開區間,就很難寫錯了。

左閉右開區間在STL資料結構的構造引數,一些api的返回值範圍等場景,也都廣泛應用。仔細思考下,還是有些規律在裡面的。

先來看三個使用的場景。

一、javascript隨機數返回值

在js中,產生隨機數函式 Math.random() 返回值是[0,1)間的實數。

通過偏移和縮放返回值,能對映到任意實數區間。再通過取下界,可以隨機產生任意整數,從而滿足各種隨機數的場景。

如果返回範圍是全封閉,全開,或者左開右閉可以嗎?
答案是不可以。

全封閉:包含了上界的1,會造成產生整數的隨機數概率不均勻。

例如要隨機產生0-9十個整數,做法是Math.floor(Math.random()*10),產生的數是均勻隨機,滿足需求。如果包含了上界的1,那麼產生10是超出範圍的。只有隨機出1時才產生10,概率要比其他整數小很多,隨機的概率就不均勻了。

全開區間:沒有了0點,對映生成整數的時候,產生0的概率要比其他整數的概率要低,不滿足隨機均勻。

左開右閉:左開右閉和左閉右開是對稱的,理論上是可以的,但是和左閉右開相比,有些不習慣。

二、迭代器遍歷

for(int i = 0
; i < count; ++i) { ... }

在C語言中,陣列的個數是count,遍歷的迴圈下標的區間也是左閉右開[0,count)。因為陣列是從0開始計數,所以不能包含上界。

A、要寫為左右都是閉區間,[0,count-1]也是可以的。但是有個減一比較彆扭。

B、有些迭代器,例如連結串列或數的子節點指標,就要設計個哨兵節點表示結束,判斷結束時直接判斷迭代器是否不等於end,不用右開區間,要判斷next是否是null,會多出一些額外的判斷,對一些邊界要用特殊邏輯。

for(iterator it=a.start();it!=a.end();++it)
{
    ...
}

三、在演算法和api中的左閉右開區間

在C語言標準庫的二分查詢bsearch,快速排序qsort的函式裡,無論是函式內部實現,還是引數,都傳的左閉右開區間;

在STL容器的建構函式,演算法操作,也都是左閉右開區間。

一方面是為了容易迭代,更通用。

另一方面是像二分、快排,都是分治演算法,一個左閉右開區間[x,y),子區間可以分解為[x,y0),[y0,y1),[y1,y2)...[yn,y),父子同構,天然適合分治實現。

在整數範圍內,如果非要寫為左閉右閉區間,也是可以的。[x,y]分解的子區間為[x,y0-1],[y0,y1-1],[y1,y2-1]...[yn,y]。但是無論怎麼分,總有一個區間和其他不同,劃分偏左或偏右一個元素,劃分是不整齊的。要打各種邊界處理補丁來彌補。

在實數範圍來進行計算,就用左畢右閉區間就不能實現了。

例如利用二分在單調遞增的函式上逼近計算,每次都是分為[x,(x+y)/2),和[(x+y)/2,y)兩個區間,如果左右封閉,要麼進行一次重複判斷計算,要麼表示不出來。在實屬範圍內,給定一個實數x0,是給不出和他距離最小的x1的確切值的,更別說用計算機表示了。

總結

左閉右開區間劃分的子區間,也符合左閉右開的性質,同構的;

按比例劃分子區間後,對映到邊界節點上的概率也是成比例的;

全閉區間要處理邊界情況,有特殊點,對程式設計和演算法理解造成障礙。

綜上,在應用數字範圍的場景中,抽象成左閉右開區間,是一個好方法。

相關推薦

聊聊區間

《程式設計珠璣》裡說過:大約10%的專業程式設計師,才能夠正確地寫出二分查詢。儘管第一個二分查詢程式於1946年就公佈了,但是第一個沒有bug的程式在1962年才出現。 二分查詢易錯的原因之一,是比較範圍寫的不對,對於一些情況是錯誤的。記住比較範圍是左

二分查詢演算法(區間

二分查詢演算法是一個基本但用處十分廣泛的演算法,但要寫出一個沒有bug的二分查詢演算法也不容易,《程式設計珠璣》一書中提到僅有百分之十的人可以第一次就寫出沒有bug的二分查詢演算法,主要原因在於尋找中間區間時資料有可能溢位,以及區間的選擇不正確導致死迴圈,陣列越

C++區間的理解

C++的區間是左閉右開的,關於這樣做的優勢,做了一個筆記整理,也處理下之前一直比較模糊的區間二分的問題。 左閉右開的區間第一個優勢是,當需要取中間元素的時候,mid=begin+end/2的定位問題。如果區間元素的個數是奇數個,那麼mid永遠是指向中間的元素;如

程式設計中左區間的廣泛應用

(以下僅為個人主觀看法,還請辯證看待!)     見過很多程式碼後不難發現,許多的程式中對於“區間”的設定,總是左閉右開[left,right)。(當然,對於索引什麼的是離散的數值)。 甚至在c,java,python等主流語言中,左閉右開區間也是非常普遍的存在(並不絕對)

區間區間——從初等數學中吸取一點程式設計養分

開區間和閉區間,學過這個很多年了吧,估計是在初中時候的數學裡。今天特意查了下,終於又知道了“[”是閉的,“(”是開的(詳細請看下面的花色字型)。 查這個好笑否?其實是有緣由的,當然也有些用處,我慢慢道來 1.程式設計中,總是很頻繁的要實現對資料進行搜尋,查詢的程式碼。以搜尋舉例,要給定開關條件

【Codeforces Round 331 (Div 2)D】【DP 記憶化搜尋 期望DP區間性質 好題】Wilbur and Trees 砍樹隨機從概率倒的期望底面覆蓋長度

Wilbur and Trees time limit per test 2 seconds memory limit per test 256 megabytes input standard input output standard ou

C++ STL區間的好處

C++STL中規定的區間為前閉後開,這樣定義的好處主要有兩點: 1. 為“遍歷元素時,迴圈的結束時機”提供一個簡單的判斷依據。只要尚未到達end(),迴圈就可以繼續下去。 2. 不必對空區間採取特殊處理手段。空區間的begin()就等於end()。

leetcode+從每一個區間裡的最大數,雙向單調佇列

//雙向單調佇列 class Solution { public: vector<int> maxSlidingWindow(vector<int>& num

JS獲取鼠標)滑事件

col function 點擊 doc 一個 cti 輸出 解釋 log 鼠標左(右)滑也是網站開發中常見的效果之一,這裏對鼠標左(右)滑做出一些解釋。 首先要獲取需要左右滑事件的節點: eg: var div=document.getElementByI

鼠標移到某個地方時,從劃出一條線的動畫

spl near webkit forward line for solid linear clas .line { border-bottom: 2px solid #E40012; width:0px; display: inline-bloc

表達式從求值(所有運算符的優先級都一樣,僅包括+ - * /)

col void spa enter 所有 char tchar putchar express #include <stdio.h> int main(void){ float a, b; char ch; printf(

移置位

int 裏的 通過 超過 二進制 方法 原碼 可能 字長 int i = 1;i = i << 2; //把i裏的值左移2位 也就是說,1的2進制是000...0001(這裏1前面0的個數和int的位數有關,32位機器,gcc裏有31個0),左移2位之後變成

從上到下按層打印二叉樹,每層打印順序從

public val empty queue ron root div rom top 1 class Solution{ 2 public : 3 vector<int> PrintFromTopToBottom(TreeNode* root){ 4

css3基礎 transition 配合position,實現從彈出的效果

資源 oct auth tca round 公開 gpo absolute ide 禮悟:   公恒學思合行悟,尊師重道存感恩。葉見尋根三返一,江河湖海同一體。 虛懷若谷良心主,願行無悔給最苦。讀書鍛煉養身心,誠勸且行且珍惜。   

界面出,出特效

ini rri star nbsp sta 分享圖片 version override @override left_in <?xml version="1.0" encoding="utf-8"?> <translate xmlns:

題目描述 在一個二維數組中(每個一維數組的長度相同),每一行都按照從遞增的順序排序,每一列都按照從上到下遞增的順序排序。請完成一個函數,輸入這樣的一個二維數組和一個整數,判斷數組中是否含有該整數。

這樣的 -i 一個 整數 描述 輸入 遞增 lse i+1 題目描述 在一個二維數組中(每個一維數組的長度相同),每一行都按照從左到右遞增的順序排序,每一列都按照從上到下遞增的順序排序。請完成一個函數,輸入這樣的一個二維數組和一個整數,判斷數組中是否含有該整數。 1

python 代碼題06 回數是指從讀和從讀都是一樣的數,例如12321,909。請利用filter()篩選出回數

list turn 例如 lte else 代碼 rom 都是 python def is_palindrome(n): return str(n) == str(n)[::-1] output = filter(is_palindrome, range(1, 10

如何將一個數的二進位制位模式從翻轉

一個數的二進位制是由位組成,我們需要對它的每一位進行操作。 第一位翻轉:20 —> 2(31-0) 第二位翻轉:21 —> 2(31-1) 第三位翻轉:22 —> 2(31-2) 第 i 位翻轉: 2i-1 —> 2(31-i) 數字value第i位翻轉之後的數值

編寫函式可求得返回值value的二進位制位模式從翻轉後的值

編寫函式:  unsigned int ReverseBit(unsigned int value);  這個函式的返回值value的二進位制位模式從左到右翻轉後的值。  如:  在32位機器上25這個值包含下列各位:  00000000000000

iOS實戰之從自動填充佈局頁面

這裡主要講的就是控制元件寬度可變時的計算: 方法一: 這裡使用一個寬度可變的Button,寬度不變的兩個Label從左往右依次排布來測試。這裡使用三組組合,每一組都用一個UIView包裹。 首先為了測試,Button的標題,依次為: NSArray *btnTitle =