W密码解密算法
柵欄密碼
文章目錄
- 柵欄密碼
- 條形柵欄密碼
- 加密算法
- 解密算法
- W形柵欄密碼
- WWW加密算法設(shè)計(jì)
- WWW解密算法推導(dǎo)
- 確定字符對各行的分配規(guī)則cnt數(shù)組
- 定義"周期"
- 如果cntphasecnt_{phase}cntphase?是一個(gè)偶數(shù)
- 如果cntphasecnt_{phase}cntphase?是一個(gè)奇數(shù)
- 奇數(shù)和偶數(shù)時(shí)的區(qū)別
- 總結(jié)成算法O(n)
- 另一種更簡單的cnt數(shù)組的計(jì)算方式
- 根據(jù)cnt數(shù)組指示,從密碼上連續(xù)截取相應(yīng)數(shù)量的字符
- 方向指針解密
- 完整算法
條形柵欄密碼
甚么是條形密碼?看完加密方法就有直觀的認(rèn)識了
加密算法
這里將柵欄橫著放用行l(wèi)ine表示,有0到h-1共h行對應(yīng)h個(gè)柵欄
設(shè)要加密的文本text,其字符下標(biāo)從0開始
然后text[0]分配給0號柵欄
text[1]分配給1號柵欄
…
text[n]分配給n號柵欄
text[h-1]分配給h-1號柵欄
text[h]分配給1號柵欄
text[h+1]分配給2號柵欄
以此類推
也就是說已知當(dāng)前行(柵欄號)為n則下一個(gè)柵欄應(yīng)該是(n+1)%h
最終text的字符被這樣輪流分配完畢,然后將各行按順序合并得到加密密碼
密碼=line[0]+line[1]+line[2]+...+line[h-1]
算法設(shè)計(jì):
bool isSpace(const char &c) {//忽略所有空格回車換行if (c == ' ' || c == '\n' || c == '\r')return true;return false; }//柵欄密碼算法 string fence_encrypt(const string &text, const int &cnt_fence) {//這里cnt_fence柵欄數(shù)就是前文的hstring fences[cnt_fence];int length = text.length();int now = 0;for (auto c : text) {if (isSpace(c))continue;fences[now] += c;//c放入當(dāng)前柵欄now = (now + 1) % cnt_fence;//now指向下一個(gè)柵欄的位置}string encrypted;for (int i = 0; i < cnt_fence; i++) {encrypted += fences[i];}return encrypted; }解密算法
那拿來的放回哪里去,實(shí)際上解密算法和加密算法是完全相同的
string fence_decrypt(const string &encrypted, const int &cnt_fence) {string fences[cnt_fence];int length = encrypted.length();int now = 0;for (auto c : encrypted) {fences[now] += c;now = (now + 1) % cnt_fence;}string text;for (int i = 0; i < cnt_fence; i++) {text += fences[i];}return text; }W形柵欄密碼
啥叫WWW加密呢?
區(qū)別于條形柵欄加密
條形柵欄加密:
將123456789A進(jìn)行深度為3(柵欄數(shù)為3)的條形柵欄加密得到
line[0]= 1 4 7 A line[1]= 2 5 8 line[2]= 3 6 9密文為第0行+第1行+第2行=147A258369
注意實(shí)際編程的時(shí)候下標(biāo)從0開始
考慮用W形將明文123456789A加密,深度為3
line[0]= --1---5---9-- line[1]= ---2-4-6-8-A- line[2]= ----3---7----密文為第0行+第1行+第2行=1592468A37
也就是說,方向是這樣的:
是個(gè)W形狀,因此叫做WWW加密算法
WWW加密算法設(shè)計(jì)
我們需要加密的明文為text,text[0]表示明文的第0個(gè)字符
當(dāng)深度為3的時(shí)候,
text[0]放在第0行
text[1]放在第1行
text[2]放在第2行
text[3]放在第1行
…
即text的字符會依次放在第0,1,2,1,0,1,2,1,0…行
規(guī)定一個(gè)方向指示director,下(+1),上(-1),表示下一個(gè)字符應(yīng)該放在當(dāng)前字符歸屬行的上一行還是下一行
這個(gè)方向只能在達(dá)到最高處(第0行)或者達(dá)到最低處(第h-1行)轉(zhuǎn)向,在中間的行(1到h-2行)只能遵守規(guī)定號的方向跳轉(zhuǎn),
這個(gè)過程神似穿針引線,從左上縫補(bǔ)到右下,然后到右上然后右下…
具體算法如下:
int now=0;//當(dāng)前行指針int director;//方向標(biāo)識for (auto c : text) {if (now == 0)director = 1;//如果當(dāng)前位置now在最高行則規(guī)定方向=1表示下一行應(yīng)往低處(0->h-1方向)跳轉(zhuǎn)else if (now == h - 1)director = -1;//如果當(dāng)前位置now在最低行則規(guī)定方向=-1表示下一行應(yīng)往高處(h-1->0)方向跳轉(zhuǎn)//注意這里沒有[2,h-2]這些行的條件判斷,意味著這些行不會修改director方向,只會遵守方向lines[now] += c;//字符c加入當(dāng)前行now = now + director;//根據(jù)director指示跳轉(zhuǎn)到下一行}完整代碼:
string WWW_encrypt(const string &text, const int &h) {string lines[h];int length = text.length();int now = 0;int director;for (auto c : text) {if (now == 0)director = 1;else if (now == h - 1)director = -1;lines[now] += c;now = now + director;}string encrypted;for (int i = 0; i < h; i++) {//按照從0到h-1的順序?qū)⒏餍衅唇悠饋?/span>encrypted += lines[i];}return encrypted; }WWW解密算法推導(dǎo)
還是觀察明文123456789A加密,深度為3的例子
line[0]= --1---5---9-- line[1]= ---2-4-6-8-A- line[2]= ----3---7----密文為第0行+第1行+第2行=1592468A37
發(fā)現(xiàn)我們只需要還原出這三行的內(nèi)容來然后借用剛才加密的思想,使用方向指示就可以還原出未加密的文本
關(guān)鍵在于怎么確定這幾行每行的內(nèi)容是啥
密文的前三個(gè)159歸屬第0行
然后2468A五個(gè)歸屬于第1行,
然后37兩個(gè)歸屬于第2行,
貌似沒有規(guī)律,不能確定哪幾個(gè)歸屬于哪一行
但是可以確定的是,歸屬于同一行的字符一定挨著,并且最開始一定是歸屬于第一行的,然后是歸屬于第二行的,以此類推
確定字符對各行的分配規(guī)則cnt數(shù)組
定義"周期"
我們把紅框稱為"上周期",其中不含最低行(第h-1行)的字符
我們把黑框稱為"下周期",其中不含最高行(第0行)的字符
兩種周期的共同點(diǎn)是,每個(gè)周期里有h-1個(gè)字符,我們把"周期"作為"上周期"和"下周期"的籠統(tǒng)稱呼
兩種周期的差異點(diǎn)是上周期優(yōu)先填充(或者說分配)高處行,方向是高到低(0→h?10\rightarrow h-10→h?1),下周期有限填充低處行,方向是(h?1→0h-1\rightarrow 0h?1→0)
現(xiàn)在給定密文長度為n,已知采用深度為h的加密方法
那么完整的"周期"數(shù)為cntphase=?nh?1?cnt_{phase}=\lfloor \frac{n}{h-1}\rfloorcntphase?=?h?1n??
如果cntphasecnt_{phase}cntphase?是一個(gè)偶數(shù)
則上下周期各占一半,cntupperphase=cntlowerphase=12cntphase=12?nh?1?cnt_{upperphase}=cnt_{lowerphase}=\frac{1}{2}cnt_{phase}=\frac{1}{2}\lfloor \frac{n}{h-1}\rfloorcntupperphase?=cntlowerphase?=21?cntphase?=21??h?1n??
到此,各行分到的字符數(shù)為
cnt[0]=cnt_upperphase; //最高行只在所有上周期中每個(gè)上周期獲得一個(gè)字符 cnt[1~h-2]=cnt_upperphase+cnt_lowerphase; //中間行在所有上下周期中都可以每個(gè)周期獲得一個(gè)字符 cnt[h-1]=cnt_lowerphase; //最低行只在所有下周期中每個(gè)下周期獲得一個(gè)字符此時(shí)剩余沒有分配的字符數(shù)量為remain=n?cntphase×charactersperphase=n?cntphase×(h?1)=n??nh?1?×(h?1)remain=n-cnt_{phase}\times characters\ per\ phase=n-cnt_{phase}\times (h-1)=n-\lfloor \frac{n}{h-1}\rfloor\times (h-1)remain=n?cntphase?×characters?per?phase=n?cntphase?×(h?1)=n??h?1n??×(h?1)
如果remain=0說明所有字符恰好被完整個(gè)周期分配完畢,否則remain>0說明有剩余字符
這些剩余字符屬于一個(gè)新的上周期,因此第0到remain-1行各自拿走一個(gè)
到此,各行分到的字符數(shù)為
cnt[0]=cnt_upperphase+1; //最高行再獲取一個(gè) cnt[1~remain-1]=cnt_upperphase+cnt_lowerphase+1; //前remain行都各自再獲取一個(gè) cnt[remain~h-1]=cnt_upperphase+cnt_lowerphase; //此后各行保持不變 cnt[h-1]=cnt_lowerphase;分配完畢
如果cntphasecnt_{phase}cntphase?是一個(gè)奇數(shù)
如果cntphasecnt_{phase}cntphase?是一個(gè)奇數(shù),則上周期比下周期多一個(gè)
cntlowerphase=?12cntphase?cnt_{lowerphase}=\lfloor\frac{1}{2}cnt_{phase}\rfloorcntlowerphase?=?21?cntphase??
cntupperphase=?12cntphase?=cntphase?cntlowerphasecnt_{upperphase}=\lceil\frac{1}{2}cnt_{phase}\rceil=cnt_{phase}-cnt_{lowerphase}cntupperphase?=?21?cntphase??=cntphase??cntlowerphase?,用計(jì)算機(jī)取上整數(shù)比較麻煩
顯然cntphasecnt_{phase}cntphase?是一個(gè)奇數(shù)時(shí)的計(jì)算方法同樣適用于偶數(shù)
{cntphase=?nh?1?cntlowerphase=?12cntphase?cntupperphase=?12cntphase?=cntphase?cntlowerphase\begin{cases} cnt_{phase}=\lfloor \frac{n}{h-1}\rfloor\\ cnt_{lowerphase}=\lfloor\frac{1}{2}cnt_{phase}\rfloor\\ cnt_{upperphase}=\lceil\frac{1}{2}cnt_{phase}\rceil=cnt_{phase}-cnt_{lowerphase}\end{cases} ??????cntphase?=?h?1n??cntlowerphase?=?21?cntphase??cntupperphase?=?21?cntphase??=cntphase??cntlowerphase??
到此,各行分到的字符數(shù)為
此時(shí)剩余沒有分配的字符數(shù)量為remain=n?cntphase×charactersperphase=n?cntphase×(h?1)=n??nh?1?×(h?1)remain=n-cnt_{phase}\times characters\ per\ phase=n-cnt_{phase}\times (h-1)=n-\lfloor \frac{n}{h-1}\rfloor\times (h-1)remain=n?cntphase?×characters?per?phase=n?cntphase?×(h?1)=n??h?1n??×(h?1)
如果remain=0說明所有字符恰好被完整個(gè)周期分配完畢,否則remain>0說明有剩余字符
這些剩余字符屬于一個(gè)新的下周期,因此第h-1到h-2+remain行各自拿走一個(gè)
到此各行分到的字符數(shù)為
cnt[0]=cnt_upperphase; cnt[1~h-remain-1]=cnt_upperphase+cnt_lowerphase //h-remain-1往上的行不變 cnt[h-remain~h-1]=cnt_upperphase+cnt_lowerphase+1; //[h-1,h-remain]行均從新的下周期中獲得一個(gè)字符 cnt[h-1]=cnt_lowerphase+1; //最低行在新的下周期中獲得一個(gè)字符奇數(shù)和偶數(shù)時(shí)的區(qū)別
由于奇數(shù)時(shí)計(jì)算周期的公式同樣適用于偶數(shù)
{cntphase=?nh?1?cntlowerphase=?12cntphase?cntupperphase=?12cntphase?=cntphase?cntlowerphase\begin{cases} cnt_{phase}=\lfloor \frac{n}{h-1}\rfloor\\ cnt_{lowerphase}=\lfloor\frac{1}{2}cnt_{phase}\rfloor\\ cnt_{upperphase}=\lceil\frac{1}{2}cnt_{phase}\rceil=cnt_{phase}-cnt_{lowerphase}\end{cases} ??????cntphase?=?h?1n??cntlowerphase?=?21?cntphase??cntupperphase?=?21?cntphase??=cntphase??cntlowerphase??
因此在兩種情況,只有在處理最后剩下的不完整的新周期時(shí),才有不同,
不同的原因是上下周期分配字符的方向和起點(diǎn)不同
總結(jié)成算法O(n)
vector<int> allocate(const int &n, const int &h) { //n個(gè)字符,加密深度為hvector<int> cnt(h);int cnt_phase = n / (h - 1);int cnt_lowerphase = cnt_phase / 2;int cnt_upperphase = cnt_phase - cnt_lowerphase;cnt[0] = cnt_upperphase;cnt[h - 1] = cnt_lowerphase;for (int i = 1; i <= h - 2; i++) {cnt[i] = cnt_phase;}int remain = n - cnt_phase * (h - 1);if (remain == 0)return cnt;if (cnt_phase % 2 == 0) {for (int i = 0; i <= remain - 1; ++i)++cnt[i];} else {for (int i = h - 1; i >= h - remain; --i)++cnt[i];}return cnt; }以1592468A37為例驗(yàn)證:
#include <iostream> #include <vector> #include <algorithm> using namespace std; vector<int> allocate(const int &n, const int &h);//詳見上文 int main() {string text = "1592468A37";vector<int> cnt = allocate(text.length(), 3);for (auto i : cnt)cout << i << " ";return 0; }結(jié)果
3 5 2是正確的
另一種更簡單的cnt數(shù)組的計(jì)算方式
注意到
vector<int> allocate(const int &n, const int &h);這個(gè)函數(shù)并沒有獲取密碼的具體字符信息,而是只給定了密碼長度和加密深度,就可以求出一個(gè)cnt數(shù)組
也就是說,cnt數(shù)組和密碼是什么字符沒有關(guān)系
那么我們豈不是可以正著來,直接用加密算法求得cnt數(shù)組?
給加密算法encrypt函數(shù)傳入一個(gè)n位長的任意字符串可以求得各行分配到的字符,自然也就求得了各行應(yīng)該分配的字符個(gè)數(shù)
也就是說,不管是對123456789還是987654321加密,其排列出的W的形狀是相同的
我們現(xiàn)有的加密算法:
string WWW_encrypt(const string &text, const int &h) {string lines[h];int length = text.length();int now = 0;int director;//方向指示標(biāo)記for (auto c : text) {if (now == 0)director = 1;else if (now == h - 1)director = -1;lines[now] += c;now = now + director;}string encrypted;for (int i = 0; i < h; i++) {encrypted += lines[i];}return encrypted; }只需要對其進(jìn)行一些改造
vector<int> allocate(const int &n, const int &h) {vector<int> cnt(h);int director;//方向指示標(biāo)記int now = 0;for (int i = 1; i <= n; i++) {if (now == 0)director = 1;else if (now == h - 1)director = -1;++cnt[now];now = now + director;}return cnt; }于是同樣用O(n)O(n)O(n)甚至更實(shí)際更優(yōu)于前一種的方法出現(xiàn)了
根據(jù)cnt數(shù)組指示,從密碼上連續(xù)截取相應(yīng)數(shù)量的字符
根據(jù)剛才我們推測得到的算法,
第0行應(yīng)當(dāng)截取密碼的從0開始,長度為cnt[0]這一段字串encrypted.substr(0,cnt[0]);
第1行應(yīng)從第0行截取剩下的開始截取,長度為cnt[1]這一字串encrypted.substr(cnt[0],cnt[1])
以此類推,第n行應(yīng)該截取encrypted.substr(cnt[0]+cnt[1]+....+cnt[n-1],cnt[n])
line[0]=encrypted.substr(0,cnt[0]); line[n]=encrypted.substr(length,cnt[n]); length=sum(cnt[0],cnt[1],...,cnt[n-1]);總結(jié)算法:
vector<string> getLines(const string &encrypted, const int &h) {vector<string> line(h);vector<int> cnt = allocate(encrypted.length(), h);line[0] = encrypted.substr(0, cnt[0]);int length = 0;//累加前面的字串長度for (int i = 1; i <= h - 1; i++) {length += cnt[i - 1];//累加line[i] = encrypted.substr(length, cnt[i]);}return line; }方向指針解密
現(xiàn)在各行都已經(jīng)求出來了,得到了我們想象中的這樣的結(jié)構(gòu):
line[0]= --1---5---9-- line[1]= ---2-4-6-8-A- line[2]= ----3---7----實(shí)際上是這樣的:
line[0]=1,5,9 line[1]=2,4,6,8,A line[2]=3,7現(xiàn)在又到了穿針引線的時(shí)間,director指示跳轉(zhuǎn)的方向,每行還需要維護(hù)一個(gè)標(biāo)記,記錄當(dāng)前行應(yīng)該取哪個(gè)字符了,方便下一次取
什么意思呢?具體來說
注意下標(biāo),排名都是從0開始
初始時(shí)各行的標(biāo)記mark[i]=0都是0,意思是,如果要取,肯定從首個(gè)(第0個(gè))字符開始
從第0行開始取direction置1,取第0個(gè)元素1,++mark[0],意思是第一行的首個(gè)元素已經(jīng)取出,如果下次跳轉(zhuǎn)到第一行取數(shù)時(shí)應(yīng)該取下一個(gè)元素
按照direction=1下跳到第2行,取第0個(gè)元素2,++mark[1],意思時(shí)第二行的首個(gè)元素已經(jīng)取出,如果下次跳轉(zhuǎn)到第二行取數(shù)時(shí)應(yīng)該取下一元素
按照direction=1下跳到第2行,置direction=-1,取第0個(gè)元素3,++mark[2],意思時(shí)第二行的首個(gè)元素已經(jīng)取出,如果下次跳轉(zhuǎn)到第二行取數(shù)時(shí)應(yīng)該取下一元素
按照direction=-1上跳到第1行,取第1個(gè)元素4,++mark[1],意思是第二行的第二個(gè)元素已經(jīng)取出,如果下次跳轉(zhuǎn)到第二行取數(shù)時(shí)應(yīng)該取下一元素
…
string decrypt(const string &encrypted, const int &h) {vector<string>line = getLines(encrypted, h);int length = encrypted.length();int director = 0;int now = 0;int mark[h];for (int i = 0; i < h; i++) {mark[i] = 0;//mark數(shù)組置0}string decrypted;for (int i = 0; i < length; i++) {if (now == 0)director = 1;else if (now == h - 1)director = -1;decrypted += line[now][mark[now]];++mark[now];//當(dāng)前行標(biāo)記后移now += director;//now按照director指示跳轉(zhuǎn)}return decrypted; }完整算法
#include <iostream> #include <vector> #include <algorithm> using namespace std;vector<int> allocate(const int &n, const int &h) { //n個(gè)字符,加密深度為hvector<int> cnt(h);int cnt_phase = n / (h - 1);int cnt_lowerphase = cnt_phase / 2;int cnt_upperphase = cnt_phase - cnt_lowerphase;cnt[0] = cnt_upperphase;cnt[h - 1] = cnt_lowerphase;for (int i = 1; i <= h - 2; i++) {cnt[i] = cnt_phase;}int remain = n - cnt_phase * (h - 1);if (remain == 0)return cnt;if (cnt_phase % 2 == 0) {for (int i = 0; i <= remain - 1; ++i)++cnt[i];} else {for (int i = h - 1; i >= h - remain; --i)++cnt[i];}return cnt;}vector<string> getLines(const string &encrypted, const int &h) {vector<string> line(h);vector<int> cnt = allocate(encrypted.length(), h);line[0] = encrypted.substr(0, cnt[0]);int length = 0;for (int i = 1; i <= h - 1; i++) {length += cnt[i - 1];line[i] = encrypted.substr(length, cnt[i]);}return line; }string decrypt(const string &encrypted, const int &h) {vector<string>line = getLines(encrypted, h);int length = encrypted.length();int director = 0;int now = 0;int mark[h];for (int i = 0; i < h; i++) {mark[i] = 0;}string decrypted;for (int i = 0; i < length; i++) {if (now == 0)director = 1;else if (now == h - 1)director = -1;decrypted += line[now][mark[now]];++mark[now];now += director;}return decrypted; }int main() {string text = "1592468A37";cout << decrypt(text, 3);return 0; }總結(jié)
- 上一篇: PostgreSQL 生成任意基数数独
- 下一篇: 栅栏密码补充