在生成數獨題目時,流程是:生成乙個完整的數獨——隨機挖空——解題——如果只有乙個解,則生成該題目,如果有多個解,則重新挖空。
數獨題目的生成涉及的乙個演算法是回溯法,這裡面的完整數獨的生成、求解都用到了回溯法。
1. 生成完整數獨
在生成部分,先生成所有宮的1,再生成所有宮的2,以此類推。
主要有三個值:
boolean placeable = new boolean[9][9][9];// 第幾個數字,第幾宮,第幾號位置
int steppos = new int[9][9];// 第幾個數字,第幾宮,值是位置
int result=new int[9][9];
placeable 用來儲存每個數字在每個宮的每個位置是否可以放置,初始時,只要該位置沒有被佔就可以放置,在生成數獨的過程中,嘗試在這個位置放置某數,如果與行列宮有衝突,則需更新placeable 。
steppos 用來儲存某個宮中已經放置的數字的位置。
result既是儲存生成的數獨。
生成階段的回溯是通過乙個雙重迴圈實現的:
private static int trygeneratefinalanswer()
removenum(result, number, gong);
}else else }}
} return result;
}
其實一開始寫數獨生成的時候,想的是先隨機生成第乙個宮的所有數字,第二個宮在不與第乙個宮衝突的情況下生成所有數字,以此類推,就是以宮為主順序生成數獨。這樣的做法理論上可行,但實際上需要非常大量的運算,而且極易容易衝突。因為後面一旦走不下去,往往要返回乙個宮的所有數字,但是以數字為主順序,走不下去就只需要返回乙個宮的乙個數字。
2. 隨機挖空
隨機挖空的部分比較簡單,但是要注意使用另乙個二維陣列來儲存生成的題目,不要在完整的陣列上動,因為後面解題部分要對比完整的陣列,看看解題是否正確。
3. 解題
解題部分時參考了另一位大佬的**,之前看的時候距今已經過了一年有餘,所以找不到他的部落格了。
解題部分的思想非常的巧妙,首先將數獨題目中1-9這9個數,轉化成二進位制中1的位置。000000100表示3,如001000000表示7,而題目中空缺的位置,則以111111111表示,這表示這個位置的候選數字是1~9。
然後根據已確定的數字分析更新空格的候選數字,比如候選值有2、3、6,則該空的二進位制數就是000100110。當二進位制中只有乙個1,則表示該值可以確定,使用integer.bitcount(data[m][i]) 方法判斷二進位制中有幾個1。
這裡的分析主要就是排除行列宮中已有的數字。
一直迴圈分析所有空格,直到不能從已確定的數字中分析出新的候選資訊,就從乙個空格的候選值中假設乙個值填進格仔中,再以此推測剩餘的空格,直到所有格仔都被填滿,或者是填補下去,那麼就返回之前的假設,填其他值,以此類推。這其實和我們正常解數獨的思路是一樣的。
如果題目已經求得兩個解,則返回,重新生成題目。因為這個題目是從乙個完整的數獨挖空而來,所以不可能沒有解。
解題的回溯部分主要通過遞迴實現:
private static void solve(int data)
analyse(data);
int result = check(data);
if (result == 1)
solve(copy);
}
}else if (result == 0)
}
解題中值得借鑑的就是將數字1~9表示成二進位制中1的位置,這樣可以很好的表示候選值。除了數獨中的數字與候選值表示成二進位制中1的位置,
在找候選值的方法中,也是用三個二進位制數分別表示當前值所在的行、列、宮已經存在的數字,來計算候選值的。
在判斷是否衝突的方法,也是用三個二進位制數分別表示當前值所在的行、列、宮已經存在的數字,來判斷是否衝突的。
唯一解的數獨題目生成器**:
Java 唯一ID生成器
前段時間,寫了乙個id 生成器,發在群裡,結果遭到別人嘲笑,心有不甘,於是思來想去,決定在重新寫乙個id生成器。此方法生成的id理論上也是會有重複,但是這個概率太低太低,低到可以忽略不計。使用當前時間戳 指定長度的隨機數,並隨機打亂字串。可以生成指定長度的純數字的id。普通 id生成器 用時間戳生成...
唯一ID生成器snowflake
sn fle k 很多場景需要使用全域性唯一id,用來標識唯一一條訊息,唯一一筆交易,唯一乙個使用者,唯一一張等等。傳統資料庫表的自增主鍵是很簡單的一種實現方式,前提是你沒有分庫,也沒有分表,如果你分表了,id就會重複,失去唯一性 用時間做唯一id,這個在併發比較高或者分布式環境中基本不可行,統一時...
IdGenerator 唯一Id生成器
public class idgenerator public idgenerator long processid this.processid processid protected long timegen public synchronized long nextid 剛剛生成的時間戳跟上次...