日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > java >内容正文

java

java实现九宫格解锁_Java计算手机九宫格锁屏图案连接9个点的方案总数

發(fā)布時(shí)間:2023/12/15 java 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java实现九宫格解锁_Java计算手机九宫格锁屏图案连接9个点的方案总数 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

(一)問(wèn)題

九宮格圖案解鎖連接9個(gè)點(diǎn)共有多少種方案?

(二)初步思考

可以把問(wèn)題抽象為求滿足一定條件的1-9的排列數(shù)(類似于“八皇后問(wèn)題”),例如123456789和987654321都是合法的(按照從上到下、從左到右、從1到9編號(hào)),解決此類問(wèn)題一般都用遞歸方法,因?yàn)閱?wèn)題規(guī)模較大,且沒(méi)有明確的計(jì)算方法

(三)深度思考

不難想出下面的簡(jiǎn)單思路:

1.先窮舉,再排除不合法結(jié)果(過(guò)濾窮舉的結(jié)果)

大略估計(jì)一下復(fù)雜度,A99=362880(計(jì)算機(jī)應(yīng)該可以接受),方案總數(shù)不超過(guò)A99,也就是說(shuō)窮舉的結(jié)果是A99,再濾去不合法的結(jié)果即可,理論上此方法可行

2.按條件窮舉(在窮舉的過(guò)程中排除不合法結(jié)果)

1>正常思維

---a.選擇起點(diǎn)位置(i,j)

---b.在起點(diǎn)周圍尋找合法終點(diǎn),規(guī)則如下:(共有12個(gè)方向)

------1.上(i - 1, j)--5.左上斜(i - 1, j - 1)---9.左上長(zhǎng)斜(i - 2, j - 1)

------2.下(i + 1, j) -6.左下斜(i + 1, j - 1) -10.左下長(zhǎng)斜(i + 2, j - 1)

------3.左(i, j - 1)--7.右上斜(i - 1, j + 1)--11.右上長(zhǎng)斜(i - 2, j + 1)

------4.右(i, j + 1) -8.右下斜(i + 1, j + 1)-12.右下長(zhǎng)斜(i + 2, j + 1)

---c.記錄路徑(起點(diǎn) + 終點(diǎn))

---d.判滿,若路徑長(zhǎng)度小于9執(zhí)行e步驟,否則執(zhí)行f步驟

---e.終點(diǎn)變起點(diǎn),返回a步驟

---f.輸出結(jié)果(路徑)

2>逆向思維

---a.排除(1.排除已選擇的點(diǎn)2.排除從起點(diǎn)不能到達(dá)的點(diǎn))得到臨時(shí)剩余點(diǎn)集

---b.在臨時(shí)剩余點(diǎn)集中選擇下一個(gè)點(diǎn)

---c.判滿,路徑長(zhǎng)度小于9,執(zhí)行d步驟,否則執(zhí)行e步驟

---d.執(zhí)行a步驟

---e.輸出結(jié)果(路徑)

P.S.正常思維比較容易理解,逆向思維更容易實(shí)現(xiàn)

(四)編碼

[最初想用方案2的逆向思維方案來(lái)實(shí)現(xiàn),結(jié)果失敗了,原因是遞歸內(nèi)嵌循環(huán),程序執(zhí)行軌跡難以捉摸...頭疼良久之后放棄了,改用方案1,簡(jiǎn)單粗暴]

[核心代碼類]

import java.util.ArrayList;

import java.util.Arrays;

import java.util.List;

public class ScreenLock {

//將NUM設(shè)置為待排列數(shù)組的長(zhǎng)度即實(shí)現(xiàn)全排列

private int NUM = 0;

private int count = 0;

private String[] strSource;

String string = null;

private static String[] wrongPos = {//各點(diǎn)對(duì)應(yīng)的不能到達(dá)的位置

"379","8","179",

"6","#","4",

"139","2","137"};

public ScreenLock(String[] strSource)

{//strSource格式為以逗號(hào)分隔的數(shù)字串,如1,2,3,4

//初始化

this.strSource = strSource;

this.NUM = strSource.length;

}

public int getCount()

{

sort(Arrays.asList(strSource), new ArrayList());

return count;

}

private void sort(List datas, List target) {

if (target.size() == NUM) {

StringBuilder sb = new StringBuilder();

for (Object obj : target)

sb.append(obj);

String ans = sb.toString();

if(isValid(ans))

count++;

return;

}

for (int i = 0; i < datas.size(); i++) {

List newDatas = new ArrayList(datas);

List newTarget = new ArrayList(target);

newTarget.add(newDatas.get(i));

newDatas.remove(i);

sort(newDatas, newTarget);

}

}

private boolean isValid(String ans)

{//判斷ans是否合法

for(int i = 0;i < ans.length() - 1;i++)

{

//獲取當(dāng)前位置的字符的值

int currPos = Integer.parseInt(ans.charAt(i) + "");

//獲取路徑子串

String path = ans.substring(0, i + 1);

//獲取錯(cuò)誤值

String wrrPos = wrongPos[currPos - 1];

//獲取可變錯(cuò)誤值

if(currPos != 5)//5不可能變

{

for(int j = 0;j < wrrPos.length();j++)

{

//獲取中間值

String wrong = wrrPos.charAt(j) + "";

int mid = (currPos + Integer.parseInt(wrong)) / 2;

//若中間值已被連接,則錯(cuò)誤終點(diǎn)可用

if(path.contains(mid + ""))

wrrPos = wrrPos.replace(wrong, "#");

//若下一位是錯(cuò)誤值則ans不合法

if(wrrPos.contains(ans.charAt(i + 1) + ""))

return false;

}

}

}

return true;

}

}

[測(cè)試類]

public class CountLockPlans {

public static void main(String[] args) {

//計(jì)算手機(jī)九宮格鎖屏圖案連接9個(gè)點(diǎn)的方案總數(shù)

String s = "1,2,3,4,5,6,7,8,9";

String[] str = s.split(",");

ScreenLock lock = new ScreenLock(str);

int num = lock.getCount();

System.out.println("連接9個(gè)點(diǎn)共有 " + num + " 種方案");

}

}

(五)運(yùn)行結(jié)果

連接9個(gè)點(diǎn)共有 62632 種方案【此結(jié)果是錯(cuò)的,詳情見(jiàn)最下方第(十)點(diǎn)】

(六)程序正確性檢驗(yàn)

1.能否生成1-9的全排列?

注釋掉無(wú)關(guān)代碼,直接輸出所有全排列,同時(shí)計(jì)數(shù),結(jié)果無(wú)誤(全部輸出需要17秒左右)

2.isValid()方法是否能夠正確判斷方案的合法性?

單獨(dú)調(diào)用isValid()傳入各種參數(shù)測(cè)試,結(jié)果無(wú)誤

3.算法邏輯是否無(wú)誤?

求排列的同時(shí)過(guò)濾不合法解,邏輯無(wú)誤

[綜上,理論上輸出的結(jié)果是正確的]

(七)答案正確性確認(rèn)

上網(wǎng)找找有沒(méi)有人算出結(jié)果,濾去所有看起來(lái)不靠譜的答案,選定果殼網(wǎng)答案(據(jù)說(shuō)用了Mathematica,乍看高上大)

文章中的解決思路也是:合法方案數(shù) = 全排列總數(shù) - 不合法方案數(shù)

仔細(xì)看過(guò)文章后發(fā)現(xiàn)原文的結(jié)論可能是錯(cuò)的(雖然不知道其具體算法)

1.從原文的貼圖可以看到先求出了方案總數(shù)985 824(這個(gè)我們不必關(guān)心,只需要關(guān)注連接9個(gè)點(diǎn)的計(jì)算結(jié)果就好)

2.原文貼圖記下不能直接連的點(diǎn)對(duì)(和我們的wrongPos數(shù)組作用類似,用來(lái)排除)

3.接著往下看是:根據(jù)上一步的點(diǎn)對(duì)生成所有不合法方案(仍然不知道具體是怎么算的,但是這里肯定存在漏洞)

原作者的思路應(yīng)該是按照相鄰點(diǎn)來(lái)判斷生成不合法方案(例如:假設(shè)第一位是1那么如果第二位選擇3,則以13開(kāi)頭的所有方案全部PASS掉)

不難發(fā)現(xiàn)這樣一個(gè)BUG:第一位選擇2,第二位選擇1,那么第三位能不能選擇3呢?

實(shí)際情況是Yes,但如果按照上面假設(shè)的判斷方法則是No,因?yàn)?1,3)屬于不合法點(diǎn)對(duì)!

這就又出現(xiàn)新問(wèn)題了,因?yàn)槲覀儼l(fā)現(xiàn)所謂的不合法點(diǎn)對(duì)好像是一個(gè)動(dòng)態(tài)的集合,如果中間點(diǎn)被選了,那么非法點(diǎn)對(duì)就變成合法的了(例如2被選了之后1可以和3連接,3和1也可以連接)

我們的算法會(huì)不會(huì)存在這個(gè)問(wèn)題呢?

private boolean isValid(String ans)

{//判斷ans是否合法

for(int i = 0;i < ans.length() - 1;i++)

{

//獲取當(dāng)前位置的字符的值

int currPos = Integer.parseInt(ans.charAt(i) + "");

//獲取路徑子串

String path = ans.substring(0, i + 1);

//獲取錯(cuò)誤值

String wrrPos = wrongPos[currPos - 1];

//獲取可變錯(cuò)誤值

if(currPos != 5)//5不可能變

{

for(int j = 0;j < wrrPos.length();j++)

{

//獲取中間值

String wrong = wrrPos.charAt(j) + "";

int mid = (currPos + Integer.parseInt(wrong)) / 2;

//若中間值已被連接,則錯(cuò)誤終點(diǎn)可用

if(path.contains(mid + ""))

wrrPos = wrrPos.replace(wrong, "#");

//若下一位是錯(cuò)誤值則ans不合法

if(wrrPos.contains(ans.charAt(i + 1) + ""))

return false;

}

}

}

return true;

}

從上面的代碼不難看出我們的算法已經(jīng)考慮到了這樣的情況(動(dòng)態(tài)修正wrrPos)

(八)思考延伸

按照這樣的方法,我們不難算出一共有多少種方案(從四個(gè)點(diǎn)到九個(gè)點(diǎn)),在此作簡(jiǎn)單分析:

1>9個(gè)點(diǎn)和8個(gè)點(diǎn)的數(shù)目應(yīng)該相等(前8位數(shù)都定了,最后一位也就不能變了)

2>9個(gè)點(diǎn)和7個(gè)點(diǎn)的數(shù)目應(yīng)該是2倍關(guān)系(前7位數(shù)定了,后兩位只有兩種排列方式,如果去掉后2位則前7位數(shù)有一半重復(fù)了)

...

設(shè)總方案數(shù)為 num,連接 i 個(gè)點(diǎn)的方案總數(shù)為 n( i ),例如n( 9 ) = 62632

則:1式:num = n( 4 ) + n( 5 ) + n( 6 ) + n( 7 ) + n( 8 ) + n( 9 )

2式:n( 8 ) = n( 9 ), n( 7 ) = n( 9 ) / 2, n( 6 ) = n( 9 ) / 6, n( 5 ) = n( 9 ) / 24, n( 4 ) = n( 9 ) / 120 [規(guī)律:n( i ) = n( 9 ) / A(9 - i)(9 - i)]

把2式帶入1式即可求得方案總數(shù),在此不再贅述

(九)總結(jié)

探索過(guò)程中雖然走了很多彎路,但也有不少收獲,例如動(dòng)態(tài)不合法判斷的BUG是在嘗試方案2時(shí)發(fā)現(xiàn)的,雖然方案2失敗了,但避免了在方案1中出現(xiàn)類似的問(wèn)題

只要思路明晰,敢于嘗試,絕對(duì)沒(méi)有plain try

(十)BUG修正

文中對(duì)果殼網(wǎng)算法提出的質(zhì)疑是錯(cuò)誤的,原文結(jié)果是對(duì)的

經(jīng)過(guò)驗(yàn)證,本文算法存在BUG,信息如下:

當(dāng)前路徑是 12345687

錯(cuò)誤原因是下一位 9被錯(cuò)誤值#39包含

錯(cuò)誤串為 123456879

BUG分析:出現(xiàn)這個(gè)BUG的原因是對(duì)自己的算法太過(guò)自信,設(shè)計(jì)算法的時(shí)候過(guò)分考慮了算法復(fù)雜度,省掉了一個(gè)不能省的循環(huán)(應(yīng)該先動(dòng)態(tài)修改wrrPos再對(duì)結(jié)果進(jìn)行判斷,原算法把二者放在一個(gè)循環(huán)里了)

現(xiàn)對(duì)isValid方法修改如下:

private static boolean isValid(String ans)

{//判斷ans是否合法

for(int i = 0;i < ans.length() - 1;i++)

{

//獲取當(dāng)前位置的字符的值

int currPos = Integer.parseInt(ans.charAt(i) + "");

//獲取路徑子串

String path = ans.substring(0, i + 1);

//獲取錯(cuò)誤值

String wrrPos = wrongPos[currPos - 1];

//獲取可變錯(cuò)誤值

if(currPos != 5)//5不可能變

{

for(int j = 0;j < wrrPos.length();j++)

{

//獲取中間值

String wrong = wrrPos.charAt(j) + "";

int mid = (currPos + Integer.parseInt(wrong)) / 2;

//若中間值已被連接,則錯(cuò)誤終點(diǎn)可用

if(path.contains(mid + ""))

wrrPos = wrrPos.replace(wrong, "#");

}

//若下一位是錯(cuò)誤值則ans不合法

if(wrrPos.contains(ans.charAt(i + 1) + ""))

return false;

}

}

return true;

}

[與原算法唯一的區(qū)別是把if判斷語(yǔ)句從循環(huán)里拿出來(lái)了,當(dāng)時(shí)想法是為了節(jié)省一個(gè)循環(huán)...結(jié)果,哎...]

修正后運(yùn)行結(jié)果:

連接9個(gè)點(diǎn)共有 140704 種方案

結(jié)論:果殼網(wǎng)的結(jié)論是正確的!之前對(duì)其內(nèi)部算法的猜測(cè)可能有誤,實(shí)屬抱歉。

總結(jié)

以上是生活随笔為你收集整理的java实现九宫格解锁_Java计算手机九宫格锁屏图案连接9个点的方案总数的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。