離散化

概念

離散化(Discretization),把無限空間中有限的個體映射到有限的空間中去,以此提高算法的時空效率。通俗的說,離散化是在不改變數據相對大小的條件下,對數據進行相應的縮小。例如:

  • 原數據:1, 999, 100000, 15;處理後:1,3,4,2。
  • 原數據:{100, 200},{20, 50000},{1, 400};處理後:{3,4},{2,6},{1,5}。

有的時候,我們會發現對於一個序列,它的值域很大,對應算法的複雜度是 Θ(值域) 的。離散化是程序設計中一個常用的技巧,它可以有效的降低時間複雜度。其基本思想就是在衆多可能的情況中,只考慮需要用的值。離散化可以改進一個低效的算法,甚至實現根本不可能實現的算法。例如,在建造線段樹空間不夠的情況下,可以考慮離散化。

離散化的原理和實現都很簡單。爲了確保不出錯且儘可能地提高效率,我們希望離散化能實現以下幾種功能:

1、保證離散化後的數據非負且儘可能的小

2、離散化後各數據項之間的大小關係不變,原本相等的也要保持相等。

由此,找出數據項在原序列中從小到大排第幾就是離散化的關鍵。可以通過下面的方法以 O(n logn) 的時間複雜度完成離散化,n 爲序列長度。

離散化一共有兩種方法,方法一重複元素離散化後的數字相同,方法二重複元素離散化後的數字不相同。用的最多的是方法一。

方法一:重複元素離散化後的數字相同

例如:對於序列 [105,35,35,79,-7],排序並去重後變爲 [-7,35,79,105],由此就得到了對應關係 -7->1, 35->2, 79->3, 105->4。

基本的步驟可以分爲:

1、用一個輔助的數組把你要離散的所有數據存下來。

2、排序,排序是爲了後面的二分。

3、去重,因爲我們要保證相同的元素離散化後數字相同。

4、索引,再用二分把離散化後的數字放回原數組。

對應的代碼如下:

#include<algorithm> // 頭文件 

const int MAXN = 1e6+4;
//n 原數組大小 num 原數組中的元素 lsh 離散化的數組 cnt 離散化後的數組大小 
int lsh[MAXN], cnt, num[MAXN], n;

for (int i=1; i<=n; i++) {
    scanf("%d",&num[i]);
    lsh[i] = num[i];	
}

sort(lsh+1 , lsh+n+1);//排序
cnt = unique(lsh+1, lsh+n+1) - lsh - 1;//去重

//二分查找
for(int i=1; i<=n; i++) {
    num[i] = lower_bound(lsh+1 , lsh+cnt+1 , num[i]) - lsh;
}

在這段代碼中,num[] 經過離散,範圍就變成了 m。

數據解析

比如,這組數據:

1,23424,242,65466,242,0

排序後得到:

0,1,242,242,23424,65466

然後會去重,得到:

0,1,242,23424,65466

然後離散化的到:

1,3,2,4,2,0

注意事項

1、去重並不是把數組中的元素刪去,而是重複的部分元素在數組末尾,去重之後數組的大小要減一。

2、二分的時候,注意二分的區間範圍,一定是離散化後的區間。

3、如果需要多個數組同時離散化,那就把這些數組中的數都用數組存下來。

方法二:重複元素離散化後的數字不相同

例如:對於序列 [105,35,35,79,-7],排序後變爲 [-7,35,35,79,105],由此就得到了對應關係 -7->1,35->2,35->3,79->4,105->4。

基本的步驟可以分爲:

1、用一個輔助的數組把你要離散的所有數據存下來。

2、排序。

3、枚舉着放回原數組。

對應的代碼如下:

#include<algorithm>

struct Node {
    int data , id;
    bool operator < (const Node &a) const {
	return data < a.data;
    }
};

const int MAXN = 1e5+4;
Node num[MAXN];//原數組
int rank[MAXN];//離散化後數組
int n;

for (int i=1; i<=n; i++) {
    scanf("%d",&num[i].data);
    num[i].id = i;
}

sort(num+1 , num+n+1);

for (int i=1; i<=n; i++) {
    rank[num[i].id] = i;
}

這種方法複雜度比上面那一種要優,但不能處理重複元素。它直接用結構體存儲原本的數列的元素的位置,然後排序以後將他們再重新賦值。那麼 rank[] 就是結構體 num[] 離散化後的結果。

數據解析

原始數據:

data: 3 6 5 10 8
id : 1 2 3 4 5

排序以後:

data: 3 5 6 8 10
id: 1 3 2 5 4

離散化以後:

data: 3 5 6 8 10
id: 1 3 2 5 4
rank: 1 2 3 4 5

再按原來的順序排列:

data: 3 6 5 10 8
rank: 1 3 2 5 4

習題

模板題

洛谷 P1955,程序自動分析,https://www.luogu.com.cn/problem/P1955

洛谷 P1667,數列,https://www.luogu.com.cn/problem/P1667

VOJ 1056,圖形面積,https://vijos.org/p/1056

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章