成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

數(shù)據(jù)庫中用一個值來保存多種情況:二進制和按位異或

qieangel2013 / 2536人閱讀

摘要:通過這個方法,我們可以非常順利的在一個數(shù)據(jù)表中用一個值保存多種情況。了解了這個原理之后,我們只需要在數(shù)據(jù)庫中保存二進制轉(zhuǎn)換而來的十進制值,在查詢時,用對比值二進制轉(zhuǎn)換而來的十進制值去按位異或一下,即可得到我們想要的結(jié)果。

例如,某個房間可從[燈,床,桌,椅,杯子,飲水機……]這些器具中挑選,從而組成這個房間的裝潢。我們可能會設(shè)計一個房間表,再設(shè)計一個器具表,再設(shè)計一個關(guān)系表,通過這個關(guān)系表來保存它們之間的對應(yīng)關(guān)系。但是這樣的效率明顯是比較差的,需要同時查詢?nèi)龔埍聿拍芡瓿伞?/p>

為了不適用關(guān)系表,我們還可以在房間表中設(shè)計一個字段,通過一個有規(guī)律的字符串來保存器具表的器具ID,例如:

1,2,3,7

下面,我們提供一種通過一個值來計算即可獲得這一器具組合的結(jié)果,方法如下:

array(
  "1" => "燈"
  "2" => "床",
  "4" => "桌",
  "8" => "椅子",
  "16" => "飲水機",
  ……
);

如果我們將5保存到數(shù)據(jù)庫中,我們可以立馬知道,這個房間有“燈”和“桌”,而如果保存的是23,則一定有“燈”“床”“桌”和“飲水機”。

給每一個器具一個給定的值,這個值一定是2的n次方(n>=0),這樣就可以保證相加之后的值可以反解。這個情況的核心原理在于,給定任何數(shù)值的前面數(shù)值相加和,一定小于當前數(shù)值。如何進行反解呢?

例如我們拿到一個值為N,那么我們可以首先找到最大的2^n,確定2^n是一定有的,如果沒有2^n,就不可能相加得到N。

接下來我們獲得M = N - 2^n,找到最大的2^m,再進行M - 2^m,如此推論下去,直到減完為止。

那么怎獲得最大的2^n呢?

$n = (int)log(N,2);

log函數(shù)在PHP4+之后內(nèi)置,用于取對數(shù),返回值為float類型,但我們僅需要整數(shù)部分,因此前面加(int)。

例如N=22,那么$n=4,再去計算2^4,就是16。

通過這個方法,我們可以非常順利的在一個數(shù)據(jù)表中用一個值保存多種情況。但是,這也有一定的適用范圍,比如這些情況最好是固定不變的,2n值不能太大等等。通過這種方法可以用該值進行權(quán)重設(shè)計,進行排序,但是不能用于條件檢索,比如你想檢索數(shù)據(jù)庫中包含“床”的房間,你就不好進行檢索,因為大部分房間的該值可能都大于2.所以,在使用這種方法時,應(yīng)該根據(jù)實際需要進行考慮。

更新:

在數(shù)據(jù)庫中,我們可以使用一種序列化的類二進制字符串來保存多個值,當這個二進制值是以01組成時,實際上就可以換算成為一個十進制數(shù),從而也就實現(xiàn)了一個十進制值保存多種情況的目的。

下面我們來做一個演示。

例如我們在訂票系統(tǒng)中,規(guī)定某一個活動每天分為6個場次,每個場次2個小時,因此實際上就把一天的12個小時分為了6份,分別是9:00-11:00,11:00-13:00,13:00-15:00,15:00-17:00,17:00-19:00:19:00-21:00,我們用“xxxxxx”(x取0或1)來表示,現(xiàn)在,我們要記錄這些場次是否全部被定完了,用1表示全部被訂完,所以“010110”就表示11:00-13:00,15:00-17:00,17:00-19:00這三個場次已經(jīng)被訂完了,不能再對外售票。

我們在數(shù)據(jù)庫中怎么保存呢?

php提供了將二進制轉(zhuǎn)換為十進制的函數(shù)bindec(),我們先將二進制值轉(zhuǎn)換為十進制值后,再保存到數(shù)據(jù)庫中。而當我們要使用時,從數(shù)據(jù)庫中取出十進制值,再使用decbin()將值轉(zhuǎn)換為二進制值,當然,我們要補全最后得到的二進制值的位數(shù),也就是前面加0,然后再進行字符串數(shù)組處理,進行對比。

在編程世界中,還有一個比較好玩的算法,叫“按位異或”。按位,就是以二進制的形式進行計算,“按位異或”就是兩個位的值不同時返回1,否則返回0。通過這個運算,我們可以得到看上去非常復(fù)雜的結(jié)果。在php中,運算為“^”。下面我們來進行一下演算。

001011 ^ 011010 = 010001 (1式,注意,開頭的0會被忽略,因此不要把開頭的0也算進來)

提按位異或有什么意義呢?因為二進制值可以和十進制值進行轉(zhuǎn)換,因此我們將二進制值轉(zhuǎn)換為十進制值進行按位異或之后,得到的值也是十進制的,我們只有將這些十進制數(shù)轉(zhuǎn)換為二進制字串后,才能發(fā)現(xiàn)規(guī)律,但是如果我們直接用十進制進行計算,卻能快速得到結(jié)果。

下面我們就來演算一次,我們拿(1式)來看。如果將二進制數(shù)轉(zhuǎn)換為十進制,我們就能得到

11 ^ 26 = 17

那事實的結(jié)果是不是這樣呢?你可以在你的php程序中寫上:


是的,結(jié)果就是這樣??墒?,這個復(fù)雜的運算有什么用呢?它可以用于比較。比如我們的數(shù)據(jù)庫中存放了11,轉(zhuǎn)換為二進制就是“001011”,也就是表示這一天的場次中,對應(yīng)的那三個時段已經(jīng)滿票了。但是如果我們現(xiàn)在正好要進行對比,看看這一天中17:00-19:00這個時段是否滿票,我們怎么能準確知道11這個值轉(zhuǎn)換為001011后,第5個位上的值是否為1呢?

我們只需要用這種思路來解決即可:

xxxxxx ^ 000010 = ?

其中xxxxxx是我們要對比的值,比如當它等于11時,也就是001011時,等式的右邊會得到001001(9)。我們再來看另一個算式:

xxxxxx ^ 000000 = ?

等式右邊會得到本身。

如果我們再用001001(9)去按位異或000010,則會得到001011(11)。

我們得到的結(jié)論就是,凡是用xxxxx去按位異或yyyyyy(其中只有一個y為1,其他全為0),得到的結(jié)果比自身小的,則對應(yīng)位置上的值為1,得到的結(jié)果比自身大的,對應(yīng)的位置上為0。通過這種方法,也就找到了哪個時間段是被訂滿票的。

為什么大于自身的,對應(yīng)的位置上就一定為0呢?因為0^1=1,而二進制數(shù)是01構(gòu)成的,也就是說0和1碰上0時,都不會變化,而只有0碰上1時才會變化。說白了,用任何一個二進制數(shù)去按位異或000100,結(jié)果發(fā)生的情況就兩種,一種是第四個位置上的值由1變?yōu)?(結(jié)果值相對于本身值而言),這種情況下該值變小,一種是第四個位置上的值由0變?yōu)?,這種情況下該值變大。了解了這個原理之后,我們只需要在數(shù)據(jù)庫中保存二進制轉(zhuǎn)換而來的十進制值,在查詢時,用對比值(二進制轉(zhuǎn)換而來的十進制值)去按位異或一下,即可得到我們想要的結(jié)果。

我們創(chuàng)建如下表結(jié)構(gòu),sale_over在實際存儲時,我們轉(zhuǎn)換為十進制整數(shù)進行存儲,這里方便演示用二進制表示。每次在用戶下訂單時對票數(shù)進行檢查,如果該時段已經(jīng)有20張票被訂出,就在下表中更新一條記錄,把對應(yīng)的時段改為1.

tablename = objectorder

id object_id day sale_over
1 5 2015-08-23 011000
2 8 2015-08-24 100101
3 5 2015-08-25 010001

例如:

SELECT COUNT(id) FROM object_order WHERE object_id=8 AND day="2015-08-20" AND (hours ^ 2)

這樣就可以判斷出8月20號這天17:00-19:00這個時間段是否被訂滿(如果返回1,則表示被訂滿了)。

如果我們不滿意用大小比較來進行判斷,我們還可以深入發(fā)現(xiàn),按位異或結(jié)果與原值之間的差值,正好是用來異或的值,也就是滿足下面的等式:

|m ^ n - m| = n (n為yyyyyy,只有一個y為1,其他為0)

|x|是指絕對值,當不取覺得值,得到的為負數(shù)時,說明結(jié)果變小了,那么原值對應(yīng)的位置上也就是1,而如果得到的為正數(shù),說明結(jié)果變大,對應(yīng)的位置上就為0。所以,上述sql,我們還可以這樣去改:

**SELECT COUNT(id) FROM object_order WHERE object_id=8 AND day="2015-08-20" AND (hours ^ 2 + 2)=hours;**

如果查到了結(jié)果,說明8這個活動8月20號這天17:00-19:00這個時間段被訂滿。

這種魔術(shù)般的使用方法,你是否思考過呢?

再議

實際上,一個二進制數(shù),我們將它轉(zhuǎn)換為十進制時,將它的各個位置值(從右往左,以0為開始)作為次數(shù)求2的次冪,再乘以該位置上的數(shù),再相加,即得到該二進制數(shù)對應(yīng)的十進制數(shù),例如:

10100 = 0(2^0) + 0(2^1) + 1(2^3) + 0(2^4) + 1*(2^5) = 8 + 32 = 40

這樣去觀察,就發(fā)現(xiàn)實際上8和32,就是我們第一次接觸這種算法時,將它們作為一個數(shù)組的索引值,進行物品的索引進行計算。

接下來,我們要更換場景,每個時段僅可以被一個人預(yù)訂,用戶每一次下訂單完成之后,形成一條記錄,這些記錄以上述形式存儲,得到如下訂單數(shù)據(jù)表:

tablename = userorder

id user_id object_id day hours
1 2 5 2015-08-23 011000
2 3 8 2015-08-24 100000
3 2 5 2015-08-24 000001

類似這樣的訂單記錄,hours字段中每個位置上的1最多出現(xiàn)1次,怎么樣確定某一天的所有票都已經(jīng)定出去了呢?

其實這是最簡單的,就是對該字段進行求和,例如:

SELECT SUM(hours) FROM user_order WHERE object_id=8 AND day="2015-08-20";

如果最終得到的值為111111,也就是十進制的63,則說明該天各個時段已訂滿,不能再進行預(yù)訂。

最后一種情況則是對上面兩張場景的結(jié)合,也就是每個時段最多可以被預(yù)訂20張票,數(shù)據(jù)庫中記錄的是單個用戶的訂單。

當然,遇到這種情況,其實我們可以準備兩張表,一張是用戶的訂單表:

tablename = userorder

id user_id object_id day hours
1 2 5 2015-08-23 011000
2 3 8 2015-08-24 100000
3 2 5 2015-08-24 000001

(第一條記錄表示用戶2在2015-08-23這天預(yù)訂了5這個活動的11點13點這兩個時段的票)

一張用來在每次用戶訂單完成時,對該時段進行判斷,如果這個時段已經(jīng)賣出20張,就改為1,進行更新操作的場次預(yù)訂情況表:

tablename = objectorder

id object_id day sale_over
1 5 2015-08-23 011000
2 8 2015-08-24 100101
3 5 2015-08-25 010001

但是這樣的話,我們通過該表,僅能判斷是否賣完,而不知道已經(jīng)賣了多少張。為了解決這個問題,我們夸張的做法是,直接在這個表的基礎(chǔ)上進行擴展,增加20個字段,每個字段對應(yīng)一個時段,用來記錄所賣出的票數(shù),但是這樣實在太蠢了。由于二進制方式,無法在每個位置上表示實際的值,例如在第2個位置上用3來表示賣出3張,這是我們無法做到的,所以,我們可以通過前面一張用戶下的訂單列表來進行計算,從而找出某個位置上是否已經(jīng)存在20個1.

實際上,我們現(xiàn)在要解決的,就是查出每個時段已經(jīng)訂出了多少張票。

我們可以用

SELECT COUNT(id) FROM user_order WHERE object_id=8 AND day="2015-08-20" AND (hours ^ 2 + 2)=hours;

這種方法就可以查出來某個時段的被訂數(shù)量,如果返回值等于20,則說明該時段已經(jīng)被定完了。但是,我們?nèi)绾螐乃械挠涗浿?,找出那些天的席位被全部定光呢?因為我們不打算使用objectorder表來記錄,而是想直接通過userorder進行查詢,所以我們不僅要判斷某個位置上的為1的記錄數(shù)是否為20,而且要判斷所有的位置。

最笨的方法就是連續(xù)判斷6次,對每個位置都進行統(tǒng)計,最終進行判斷。但是這明顯不符合我們的要求。

實際上,我們?nèi)匀皇褂们蠛图纯赏瓿桑覀冊谇懊孢M行求和時,只需要用111111進行對比,也就是十進制的63進行對比,而這次,我們用20個111111進行對比,也就是63*20 = 1260進行對比即可。

SELECT SUM(hours) FROM user_order WHERE object_id=8 AND day="2015-08-20";

如果得到的返回值等于1260,說明這一天的所有場次已經(jīng)完全訂出去了。

用這種方法處理數(shù)據(jù)庫中保存有規(guī)律的多種情況保存,就變得輕松有趣了。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/21003.html

相關(guān)文章

  • 數(shù)據(jù)庫中用一個值來保存多種情況進制和按異或

    摘要:通過這個方法,我們可以非常順利的在一個數(shù)據(jù)表中用一個值保存多種情況。了解了這個原理之后,我們只需要在數(shù)據(jù)庫中保存二進制轉(zhuǎn)換而來的十進制值,在查詢時,用對比值二進制轉(zhuǎn)換而來的十進制值去按位異或一下,即可得到我們想要的結(jié)果。 例如,某個房間可從[燈,床,桌,椅,杯子,飲水機……]這些器具中挑選,從而組成這個房間的裝潢。我們可能會設(shè)計一個房間表,再設(shè)計一個器具表,再設(shè)計一個關(guān)系表,通過這個關(guān)...

    luckyyulin 評論0 收藏0
  • 淺談JavaScript位操作符

    摘要:有符號的右移操作符由兩個大于符號表示這個操作符的含義就是將數(shù)值的位向右移指定的位數(shù)同時保留符號位的值正負號標記有符號的右移操作符與左移操作符剛好相反比如向右移動位就是同樣的在移位的過程中也會出 位操作符的基本概念 因為ECMAscript中所有數(shù)值都是以IEEE-75464格式存儲,所以才會誕生了位操作符的概念. 位操作符作用于最基本的層次上,因為數(shù)值按位存儲,所以位操作符的作用也就是...

    fasss 評論0 收藏0
  • 【STM32】位操作、按位與、按位或、按異或、取反、左移、右移等基礎(chǔ) C 語言知識補充

    摘要:總結(jié)對于原二進制數(shù)來說,是不變,是反轉(zhuǎn)。的位數(shù)對應(yīng)原二進制數(shù)的位數(shù),對各位進行屏蔽,全部置。左移左移與右移比較類似,是將目標二進制數(shù)字向左右移動相應(yīng)的位數(shù)。語言中的邏輯運算符按位與,按位或,按位異或,取反,左右移位不完全手冊立創(chuàng)開源 ...

    waruqi 評論0 收藏0

發(fā)表評論

0條評論

閱讀需要支付1元查看
<