C ++
Burada sunacağım şey, 3x3'lük bir durum için bir örnekle gösterilen bir algoritma. Teorik olarak NxN davasına genişletilebilir, ancak bunun çok daha güçlü bir bilgisayar ve / veya bazı ustaca ayarlamalar yapması gerekir. Ben ilerlerken bazı gelişmelerden bahsedeceğim.
Daha ileri gitmeden önce , Sudoku ızgarasının simetrilerini, yani başka bir ızgaraya giden dönüşümleri önemsiz bir şekilde not edelim . Blok boyutu 3 için simetriler aşağıdaki gibidir:
Yatay simetri
**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
Dikey simetri
**The N=3 sudoku is said to consist of 3 "stacks" of 3 "columns" each.**
the count is the same as for horizontal.
Izgaranın yatay ve dikey yansımalarının bunların bir kombinasyonu ile elde edilebileceğini unutmayın, bu yüzden sayılmaları gerekmez. Dikkate alınması gereken bir faktör olan bir başka uzaysal simetri daha vardır 2
. Bu, toplam uzamsal simetriyi verir.
2*(N!*(N!)^N)^2 = 2*(6*216)^2=3359232 spatial symmetries for the case N=3.
Sonra yeniden etiketleme adı verilen çok önemli bir simetri daha var.
Relabelling gives a further (N^2)!=9!=362880 symmetries for the case N=3. So the total
number of symmetries is 362880*3359232=1218998108160.
Toplam çözüm sayısı, simetriye özgü çözümlerin sayısının bu sayı ile çarpılmasıyla bulunamaz, çünkü bir dizi (% 1'den daha az) otomorfik çözüm vardır. Bu, bu özel çözümler için, bunları kendileriyle eşleştiren bir simetri işlemi veya bunları aynı diğer çözümle eşleştiren çoklu simetri işlemleri olduğu anlamına gelir.
Çözüm sayısını tahmin etmek için, soruna 4 adımda yaklaşıyorum:
1. r[362880][12]
0 ile 8 arasındaki tüm olası permütasyonları içeren bir diziyi doldurun (bu programlama ve C cinsindendir, bu yüzden 1'den 9'a kadar kullanmayacağız.) Eğer zekiyseniz ikinci abonenin Bunun nedeni, bunu yaparken bir "satır" olarak değerlendireceğimizi akılda tutarak, r[9,10,11] == 1<<a | 1<<b | 1<<c
9,10,11'in birinci, ikinci ve üçüncü yığına atıfta bulunduğu üç tamsayı da hesapladığımız için. ve a, b, c, bu satır için her yığınta bulunan üç sayıdır.
2. Bir diziyi b
3 sıralı bir bandın tüm olası çözümleriyle doldurun . Bunu oldukça küçük tutmak için, yalnızca üst sıranın 012.345.678 olduğu çözümleri ekleyin. Bunu kaba kuvvetle, tüm olası orta sıraları üreterek r[0][10,11,12]
ver[i][10,11,12]
. Herhangi bir pozitif değer, aynı karede iki özdeş sayı olduğu ve bandın geçersiz olduğu anlamına gelir. İlk iki satır için geçerli bir kombinasyon olduğunda, 3. (alt) satırı aynı teknikle ararım.
Diziyi b [2000000] [9] olarak boyutlandırdım, ancak program yalnızca 1306368 çözüm buluyor. Kaç tane olduğunu bilmiyordum, bu yüzden dizi boyutunu böyle bıraktım. Bu aslında tek bir bant için olası çözümlerin sadece yarısıdır (wikipedia'da doğrulanmıştır), çünkü mevcut değerden sadece 3. satırı i
yukarı doğru tararım. Çözeltilerin geri kalan yarısı, 2. ve 3. sıralar değiştirilerek önemsiz olarak bulunabilir.
Bilgilerin dizide saklanma şekli b
ilk başta biraz kafa karıştırıcıdır. 0..8
verilen bir konumda bulunan sayıları saklamak için her bir tamsayıyı kullanmak yerine , burada her bir tamsayı sayılardan birini dikkate alır 0..8
ve hangi sütunlarda bulunabileceğini belirtir. böylece b[x][7]==100100001
x çözümünün 7 sayısının 0,5 ve 8 sütunlarında (sağdan sola) bulunduğunu gösterecektir. Bu gösterimin nedeni, bandın diğer olanaklarını yeniden etiketleyerek üretmemiz gerektiğidir ve bu temsil bunu yapmayı kolaylaştırır.
Yukarıdaki iki adım kurulumu içerir ve yaklaşık bir dakika sürer (gereksiz veri çıkışını kaldırırsam daha az olur. Aşağıdaki iki adım gerçek aramadır.)
3 Çatışmayan (yani belirli bir sütunda iki kez aynı sayıya sahip olmayan) ilk iki bandın çözümlerini rasgele arayın. Her zaman permütasyon 0 olduğunu varsayarak bant 1 için rastgele bir çözüm ve Rastgele bir permütasyon.Normalde 9999 denemeden daha az bir sonuç bulunur (binlerce aralıktaki ilk aşama vuruş oranı) ve bir saniyenin bir kısmını alır. Permütasyon ile, ikinci bant için b [] [] burada ilk satır her zaman 012,345,678'dir ve ilk satırdaki olası herhangi bir sayı dizisi mümkün olacak şekilde yeniden etiketleyin.
4 3. adımda bir isabet bulunduğunda, diğer ikiyle çakışmayan üçüncü bant için bir çözüm arayın. Sadece bir deneme yapmak istemiyoruz, aksi takdirde 3. adım için işlem süresi boşa gider. Öte yandan, buna çok fazla çaba sarf etmek istemiyoruz.
Sadece eğlenmek için, dün gece bunu en aptalca şekilde yaptım, ama yine de ilginçti (çünkü çağlar için hiçbir şey yoktu, sonra patlamalarda çok sayıda çözüm buldu.) Küçük bir hack ile bile bir veri noktası elde etmek bütün gece sürdü. (!z)
Bunun k
geçerli bir çözüm olmadığını bildiğimiz anda son döngüyü iptal ettim (neredeyse 9 kat daha hızlı çalışmasını sağlar.) Tüm 1306368 kanonik çözümlerin 362880 yeniden etiketlemesini aradıktan sonra tüm ızgara için 1186585 çözüm buldu. blok, toplam 474054819840 olasılık. Bu, ikinci aşama için 400000'de 1'lik bir isabet oranı. Bir tarama yerine rastgele bir arama ile tekrar deneyeceğim. Birkaç milyon denemede makul bir cevap vermeli ve bu sadece birkaç saniye sürmelidir.
Genel cevap (362880 * (1306368 * 2)) ^ 3 * isabet oranı = 8,5E35 * isabet oranı olmalıdır. Sorudaki sayıdan geri hesaplayarak, 1 / 1.2E14'lük bir isabet oranı bekliyorum. Şimdiye kadar tek veri noktamda elde ettiğim şey 1 / (400000 * 1000), ki bu yaklaşık bir milyon faktör. Bu bir şans anomalisi, programımdaki bir hata veya matematiğimdeki bir hata olabilir. Birkaç test daha yapana kadar hangisi olduğunu bilmiyorum.
Bunu bu gece burada bırakacağım. Metin biraz cılız, yakında toplayacağım ve umarım daha fazla sonuç ekleyeceğim ve belki daha hızlı hale getirme ve kavramın N = 4'e nasıl genişletileceği hakkında birkaç kelime ekleyeceğim. Programımda çok daha fazla değişiklik yapacağımı sanmıyorum :-)
Ah .. program:
#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();
}