C ++
สิ่งที่ฉันจะนำเสนอในที่นี้คืออัลกอริทึมที่แสดงด้วยตัวอย่างสำหรับกรณี 3x3 ในทางทฤษฎีมันสามารถขยายไปถึงเคส NxN ได้ แต่มันต้องมีคอมพิวเตอร์ที่ทรงพลังกว่าและ / หรือมีการปรับแต่งอันชาญฉลาด ฉันจะพูดถึงการปรับปรุงบางอย่างเมื่อฉันผ่าน
ก่อนที่จะไปเพิ่มเติมลองสังเกตความสมดุลของกริด Sudokuนั่นคือการแปลงที่นำไปสู่กริดอื่นในลักษณะที่ไม่สำคัญ สำหรับขนาดบล็อก 3 ความสมมาตรมีดังนี้:
สมมาตรแนวนอน
**The N=3 sudoku is said to consist of 3 "bands" of 3 "rows" each**
permute the three bands: 3! permutations = 6
permute the rows in each band: 3 bands, 3! permutations each =(3!)^3=216
แนวตั้งสมมาตร
**The N=3 sudoku is said to consist of 3 "stacks" of 3 "columns" each.**
the count is the same as for horizontal.
โปรดทราบว่าการรวมกันของแนวนอนและแนวตั้งของกริดสามารถทำได้โดยการรวมกันของเหล่านี้ดังนั้นพวกเขาจึงไม่จำเป็นต้องนับ มีอยู่คนหนึ่งสมมาตรเชิงพื้นที่มากขึ้นที่จะได้รับการพิจารณาซึ่งเป็น transposing 2
ซึ่งเป็นปัจจัยของความเป็น สิ่งนี้ทำให้สมมาตรเชิงพื้นที่ทั้งหมดของ
2*(N!*(N!)^N)^2 = 2*(6*216)^2=3359232 spatial symmetries for the case N=3.
จากนั้นก็มีความสมมาตรที่สำคัญมากอีกอย่างหนึ่งที่เรียกว่า relabelling
Relabelling gives a further (N^2)!=9!=362880 symmetries for the case N=3. So the total
number of symmetries is 362880*3359232=1218998108160.
จำนวนทั้งหมดของการแก้ปัญหาไม่สามารถหาได้ง่ายโดยการคูณจำนวนของการแก้ปัญหาที่ไม่ซ้ำกันแบบสมมาตรตามจำนวนนี้เพราะมีจำนวน (น้อยกว่า 1%) ของการแก้ปัญหาอัตโนมัติ นั่นหมายความว่าสำหรับการแก้ปัญหาพิเศษเหล่านี้มีการดำเนินการสมมาตรที่แมปพวกเขากับตัวเองหรือการดำเนินการสมมาตรหลายอย่างที่แมปพวกเขากับโซลูชั่นอื่น ๆ ที่เหมือนกัน
ในการประมาณจำนวนการแก้ปัญหาฉันเข้าใกล้ปัญหาใน 4 ขั้นตอน:
1. เติมอาร์เรย์ที่r[362880][12]
มีการเรียงสับเปลี่ยนที่เป็นไปได้ทั้งหมดของตัวเลข 0 ถึง 8 (นี่คือการเขียนโปรแกรมและเป็น C ดังนั้นเราจะไม่ใช้ 1 ถึง 9) หากคุณฉลาดคุณจะสังเกตเห็นว่าตัวห้อยที่สอง คือ 12 ไม่ใช่ 9 นี่เป็นเพราะในขณะที่ทำสิ่งนี้จำไว้ว่าเรากำลังจะพิจารณาสิ่งนี้ให้เป็น "แถว" เรายังคำนวณจำนวนเต็มอีกสามค่าr[9,10,11] == 1<<a | 1<<b | 1<<c
โดยที่ 9,10,11 อ้างถึงกองซ้อนที่หนึ่งสองและสาม และ a, b, c เป็นตัวเลขสามตัวที่ปรากฏในแต่ละสแต็กสำหรับแถวนั้น
2. กรอกข้อมูลอาร์เรย์b
ด้วยวิธีแก้ปัญหาที่เป็นไปได้ทั้งหมดของวง 3 แถว เพื่อให้มีขนาดเล็กพอสมควรให้รวมเฉพาะโซลูชันที่แถวบนสุดคือ 012,345,678 ฉันทำเช่นนี้โดยกำลังดุร้ายโดยการสร้างแถวกลางเป็นไปได้ทั้งหมดและ Anding กับr[0][10,11,12]
r[i][10,11,12]
ค่าบวกใด ๆ หมายถึงมีตัวเลขที่เหมือนกันสองตัวในจตุรัสเดียวกันและย่านนั้นไม่ถูกต้อง เมื่อมีการรวมกันที่ถูกต้องสำหรับสองแถวแรกฉันค้นหาแถวที่ 3 (ด้านล่าง) ด้วยเทคนิคเดียวกัน
ฉันกำหนดขนาดอาร์เรย์เป็น b [2000000] [9] แต่โปรแกรมค้นหาวิธีแก้ปัญหา 1306368 เท่านั้น ฉันไม่รู้ว่ามีอยู่เท่าไหร่ดังนั้นฉันจึงปล่อยให้มิติอาร์เรย์เป็นแบบนั้น นี่เป็นเพียงวิธีแก้ปัญหาที่เป็นไปได้เพียงครึ่งเดียวสำหรับวงดนตรีเดียว (ตรวจสอบในวิกิพีเดีย) เพราะฉันสแกนเฉพาะแถวที่ 3 จากค่าปัจจุบันi
ขึ้นไป ส่วนที่เหลือของโซลูชันสามารถพบได้เพียงเล็กน้อยโดยการแลกเปลี่ยนแถวที่ 2 และ 3
วิธีที่ข้อมูลถูกเก็บไว้ในอาร์เรย์b
จะทำให้เกิดความสับสนเล็กน้อยในตอนแรก แทนที่จะใช้จำนวนเต็มแต่ละตัวเพื่อเก็บตัวเลขที่0..8
พบในตำแหน่งที่กำหนดที่นี่แต่ละจำนวนเต็มพิจารณาหนึ่งในตัวเลข0..8
และระบุว่าคอลัมน์ใดที่สามารถพบได้ ดังนั้นb[x][7]==100100001
จะระบุว่าสำหรับวิธีการแก้ปัญหา x หมายเลข 7 ถูกพบในคอลัมน์ 0,5 และ 8 (จากขวาไปซ้าย) เหตุผลสำหรับการแทนค่านี้คือเราต้องสร้างส่วนที่เหลือของความเป็นไปได้สำหรับวงดนตรีโดย relabelling และสิ่งนี้ การเป็นตัวแทนทำให้สะดวกในการทำเช่นนี้
สองขั้นตอนข้างต้นประกอบด้วยการตั้งค่าและใช้เวลาประมาณหนึ่งนาที (อาจน้อยกว่านี้หากฉันลบการส่งออกข้อมูลที่ไม่จำเป็นออกไปสองขั้นตอนด้านล่างเป็นการค้นหาจริง)
3 ค้นหาแบบสุ่มเพื่อหาคำตอบสำหรับสองวงแรกที่ไม่ได้ปะทะกัน (เช่นไม่มีจำนวนเท่ากันสองครั้งในคอลัมน์ที่กำหนดเราเลือกวิธีสุ่มสำหรับวง 1 โดยสมมติว่ามีการเปลี่ยนแปลงเสมอ 0 และสุ่มวิธีสำหรับวง 2 ด้วย การเปลี่ยนรูปแบบสุ่มผลลัพธ์มักจะพบในการพยายามน้อยกว่า 9999 ครั้ง (อัตราการโจมตีระยะที่หนึ่งในช่วงพัน) และใช้เวลาเพียงเสี้ยววินาทีโดยการเรียงสับเปลี่ยนฉันหมายถึงว่าสำหรับวงที่สองเราใช้วิธีแก้ปัญหาจาก b [] [] โดยที่แถวแรกจะเป็น 012,345,678 เสมอและติดตั้งใหม่เพื่อให้สามารถเรียงลำดับหมายเลขในแถวแรกได้
4 เมื่อพบการโจมตีในขั้นตอนที่ 3 ให้ค้นหาวิธีแก้ปัญหาสำหรับวงดนตรีที่สามซึ่งไม่ขัดแย้งกับอีกสองวง เราไม่ต้องการลองเพียงครั้งเดียวมิฉะนั้นเวลาในการประมวลผลสำหรับขั้นตอนที่ 3 จะสูญเปล่า ในทางกลับกันเราไม่ต้องการที่จะใช้ความพยายามมากเกินไปในเรื่องนี้
เมื่อคืนที่ผ่านมาฉันทำมันอย่างที่เป็นไปได้ แต่มันก็ยังน่าสนใจ (เพราะมันไม่มีอะไรเลยสำหรับทุกวัยจากนั้นก็พบทางออกจำนวนมากในการระเบิด) มันใช้เวลาตลอดทั้งคืนเพื่อรับชุดข้อมูลเดียว(!z)
ฉันยกเลิกการk
วนลูปล่าสุดทันทีที่เรารู้ว่านี่ไม่ใช่วิธีแก้ปัญหาที่ถูกต้อง (ซึ่งทำให้มันทำงานได้เร็วขึ้นเกือบ 9 เท่า) พบ 1186585 โซลูชันสำหรับกริดที่สมบูรณ์ บล็อกทั้งหมดมีความเป็นไปได้ 474054819840 นั่นเป็นอัตราการเข้าชมที่ 1 ใน 400000 สำหรับด่านที่สอง ฉันจะลองอีกครั้งในไม่ช้าด้วยการค้นหาแบบสุ่มมากกว่าการสแกน ควรให้คำตอบที่สมเหตุสมผลเพียงไม่กี่ล้านครั้งซึ่งจะใช้เวลาเพียงไม่กี่วินาที
คำตอบโดยรวมควรเป็น (362880 * (1306368 * 2)) ^ 3 * อัตราการเข้าชม = 8.5E35 * อัตราการเข้าชม โดยการคำนวณย้อนหลังจากตัวเลขในคำถามนั้นฉันคาดว่าอัตราการเข้าชมที่ 1 / 1.2E14 สิ่งที่ฉันได้รับจนถึงตอนนี้กับดาต้าพอยท์เดียวของฉันคือ 1 / (400000 * 1,000) ซึ่งคิดเป็นประมาณหนึ่งล้าน นี่อาจเป็นความผิดปกติของโอกาสข้อผิดพลาดในโปรแกรมของฉันหรือข้อผิดพลาดในคณิตศาสตร์ของฉัน ฉันไม่รู้ว่ามันคืออะไรจนกระทั่งฉันทำการทดสอบเพิ่มเติมอีกสองสามครั้ง
ฉันจะออกจากที่นี่เพื่อคืนนี้ ข้อความเป็นกระท่อนกระแท่นเล็กน้อยฉันจะทำให้เป็นระเบียบขึ้นในไม่ช้าและหวังว่าจะเพิ่มผลลัพธ์เพิ่มเติมและอาจมีคำสองสามคำเกี่ยวกับวิธีทำให้เร็วขึ้นและวิธีขยายแนวคิดให้เป็น N = 4 ฉันไม่คิดว่าฉันจะทำการเปลี่ยนแปลงโปรแกรมของฉันมากเกินไปแม้ว่า :-)
อ่า .. โปรแกรม:
#include "stdafx.h"
#define _CRT_RAND_S
#include <algorithm>
#include <time.h>
unsigned int n[] = { 0,1,2,3,4,5,6,7,8 }, r[362880][12], b[2000000][9],i,j,k,l,u,v,w,x,y,z;
int main () {
//Run through all possible permutations of n[] and load them into r[][]
i=0;
do {
r[i][9] = r[i][10] = r[i][11]=0;
for (l = 0; l < 9; l++){
r[i][l] = n[l];
r[i][9 + l / 3] |= 1 << n[l];
}
if((i+1)%5040==0) printf("%d%d%d %d%d%d %d%d%d %o %o %o %o \n"
,r[i][0],r[i][1],r[i][2],r[i][3],r[i][4],r[i][5],r[i][6],r[i][7],r[i][8],r[i][9],r[i][10],r[i][11],r[i][9]+r[i][10]+r[i][11]);
i++;
} while ( std::next_permutation(n,n+9) );
//Initialise b[][]
for (l = 0; l<2000000; l++) for (k = 0; k<9; k++) b[l][k]=0;
//fill b[][] with all solutions of the first band, where row0 ={0,1,2,3,4,5,6,7,8} and row1<row2
l=0;
for (i = 0; i<362880; i++)
if (!(r[0][9] & r[i][9] | r[0][10] & r[i][10] | r[0][11] & r[i][11])){printf("%d %d \n",i,l);
for (j=i; j<362880;j++)
if(!(r[0][9]&r[j][9] | r[0][10]&r[j][10] | r[0][11]&r[j][11] | r[j][9]&r[i][9] | r[j][10]&r[i][10] | r[j][11]&r[i][11] )){
for (k = 0; k < 9; k++){
b[l][r[0][k]]|=1<<k;
b[l][r[i][k]]|=1<<k;
b[l][r[j][k]]|=1<<k;
}
l++;
}
// printf("%d%d%d %d%d%d %d%d%d %o %o %o %o \n"
// ,r[i][0],r[i][1],r[i][2],r[i][3],r[i][4],r[i][5],r[i][6],r[i][7],r[i][8],r[i][9],r[i][10],r[i][11],r[i][9]+r[i][10]+r[i][11]);
// printf("%d%d%d %d%d%d %d%d%d %o %o %o %o \n"
// ,r[j][0],r[j][1],r[j][2],r[j][3],r[j][4],r[j][5],r[j][6],r[j][7],r[j][8],r[j][9],r[j][10],r[j][11],r[j][9]+r[j][10]+r[j][11]);
// printf("%d %d %o %o %o %o %o %o %o %o %o \n",i,l,b[l][0],b[l][1],b[l][2],b[l][3],b[l][4],b[l][5],b[l][6],b[l][7],b[l][8]);
}
// find a random solution for the first 2 bands
l=0;
do{
rand_s(&u); u /= INT_MIN / -653184; //1st band selection
rand_s(&v); v /= INT_MIN / -181440; //2nd band permutation
rand_s(&w); w /= INT_MIN / -653184; //2nd band selection
z = 0;
for (k = 0; k < 9; k++) z |= b[u][k] & b[w][r[v][k]];
l++;
} while (z);
printf("finished random after %d tries \n",l);
printf("found solution with top band %d permutation 0, and middle band %d permutation %d \n",u,w,v);
getchar();
// scan all possibilities for the last band
l=0;
for (i = 0; i < 362880; i++) for (j = 0; j < 1306368; j++){
z=0;
for(k=0;(k<9)&&(!z);k++) z|= b[u][k] & b[j][r[i][k]] | b[j][r[i][k]] & b[w][r[v][k]];
if (!z){ l++; printf("solution %d : i= %d j=%d",l,i,j); }
}
printf("finished bottom band scan at %d millisec \n", clock()); getchar();
}