Algorithm nxn矩阵的创建-类似数独
我正在解决一个问题,我需要创建一个NxN矩阵(N在这里作为输入给出),这样,所有条目都在[1,N]范围内,并且没有条目在特定行或列中重复两次。对角线上没有约束 另外,我需要使用一个随机数生成器来确保网格的输出随着每次执行而改变 另外,我得到了一个使用回溯来解决这个问题的提示 我想到了一个如下的算法Algorithm nxn矩阵的创建-类似数独,algorithm,recursion,backtracking,Algorithm,Recursion,Backtracking,我正在解决一个问题,我需要创建一个NxN矩阵(N在这里作为输入给出),这样,所有条目都在[1,N]范围内,并且没有条目在特定行或列中重复两次。对角线上没有约束 另外,我需要使用一个随机数生成器来确保网格的输出随着每次执行而改变 另外,我得到了一个使用回溯来解决这个问题的提示 我想到了一个如下的算法 func(i,j): grid[i][j] = 1 + rand()%N if(check(grid)==true) j++ if j == N
func(i,j):
grid[i][j] = 1 + rand()%N
if(check(grid)==true)
j++
if j == N
j = 0
i++
if i == N
return
else
//resetting the grid entry
grid[i][j] = -1;
//make a recursive call to func(i,j) again
func(i,j)
如果任何行/列中没有元素重复两次,则check(grid)返回true
我知道这是不正确的,因为它可能会在某个地方陷入一个无限循环中,而且我在这里没有使用回溯
有人能指导我如何使用回溯来解决给定的问题吗
如果有人能提供一些代码就好了。谢谢。以下是生成随机拉丁方的伪代码(本质上专门用于此问题):
complete(S):
If S is completely filled in, return true
find the index [i,j] where there's the fewest immediate choices.
for c in each choice for S[i,j] { // iterated over in a random order
S[i][j] = c
if complete(S) {
return true
}
}
S[i][j] = blank
return false
}
此过程使用随机有效解决方案(如果存在)完成数组S,并返回描述解决方案是否存在的布尔值。它可以返回任何可能的解决方案
请注意,在此过程中,空单元格的“选择”是一个不会立即导致问题的数字,即任何尚未出现在该行和列中的数字
有各种各样的优化可以使这个过程更快(一个简单的例子:传递一个额外的参数来计算剩余的空白单元格数量),但这是Knuth的有效解决方案
另一个不生成所有拉丁方的廉价解决方案是简单地排列另一个拉丁方:排列一个拉丁方的行、列和数字会生成另一个拉丁方。所以你可以在你的程序中烘焙10到20个不同的拉丁方块,随机挑选一个,然后通过排列来伪装它
这里有一个相当有效的Python解决方案。它在大约半秒钟内随机生成一个30x30的拉丁方。通过消除O(N^2)max操作,而不是维护优先级队列,应该仍然可以将速度提高N/logN倍,但它可能已经足够快了
import random
def bitcount(n):
i = 0
while n:
i += 1
n &= n-1
return i
def complete(S, rowset, colset, entries):
N = len(S)
if entries == N * N:
return True
i, j = max(
((i, j) for i in xrange(N) for j in xrange(N) if S[i][j] == 0),
key=(lambda (i, j): bitcount(rowset[i]|colset[j])))
bits = rowset[i]|colset[j]
p = [n for n in xrange(1, N+1) if not (bits >> (n-1)) & 1]
random.shuffle(p)
for n in p:
S[i][j] = n
rowset[i] |= 1 << (n-1)
colset[j] |= 1 << (n-1)
if complete(S, rowset, colset, entries+1): return True
rowset[i] &= ~(1 << (n-1))
colset[j] &= ~(1 << (n-1))
S[i][j] = 0
return False
N = 30
ns = len(str(N))
for _ in xrange(5):
S = [[0] * N for _ in xrange(N)]
assert complete(S, [0]*N, [0]*N, 0)
for row in S:
print ''.join('%*d ' % (ns, x) for x in row)
print
随机导入
def位计数(n):
i=0
而n:
i+=1
n&=n-1
返回i
def完成(行集、列集、条目):
N=长(S)
如果条目==N*N:
返回真值
i、 j=最大值(
((i,j)对于x范围内的i(N)对于x范围内的j(N),如果S[i][j]==0),
key=(lambda(i,j):位计数(行集[i]|列集[j]))
位=行集[i]|列集[j]
p=[n表示xrange(1,n+1)中的n,如果不是(位>>(n-1))&1]
随机。洗牌(p)
对于p中的n:
S[i][j]=n
行集[i]|=1以下是生成随机拉丁方的伪代码(本质上专门用于此问题):
complete(S):
If S is completely filled in, return true
find the index [i,j] where there's the fewest immediate choices.
for c in each choice for S[i,j] { // iterated over in a random order
S[i][j] = c
if complete(S) {
return true
}
}
S[i][j] = blank
return false
}
此过程使用随机有效解决方案(如果存在)完成数组S,并返回描述解决方案是否存在的布尔值。它可以返回任何可能的解决方案
请注意,在此过程中,空单元格的“选择”是一个不会立即导致问题的数字,即任何尚未出现在该行和列中的数字
有各种各样的优化可以使这个过程更快(一个简单的例子:传递一个额外的参数来计算剩余的空白单元格数量),但这是Knuth的有效解决方案
另一个不生成所有拉丁方的廉价解决方案是简单地排列另一个拉丁方:排列一个拉丁方的行、列和数字会生成另一个拉丁方。所以你可以在你的程序中烘焙10到20个不同的拉丁方块,随机挑选一个,然后通过排列来伪装它
这里有一个相当有效的Python解决方案。它在大约半秒钟内随机生成一个30x30的拉丁方。通过消除O(N^2)max操作,而不是维护优先级队列,应该仍然可以将速度提高N/logN倍,但它可能已经足够快了
import random
def bitcount(n):
i = 0
while n:
i += 1
n &= n-1
return i
def complete(S, rowset, colset, entries):
N = len(S)
if entries == N * N:
return True
i, j = max(
((i, j) for i in xrange(N) for j in xrange(N) if S[i][j] == 0),
key=(lambda (i, j): bitcount(rowset[i]|colset[j])))
bits = rowset[i]|colset[j]
p = [n for n in xrange(1, N+1) if not (bits >> (n-1)) & 1]
random.shuffle(p)
for n in p:
S[i][j] = n
rowset[i] |= 1 << (n-1)
colset[j] |= 1 << (n-1)
if complete(S, rowset, colset, entries+1): return True
rowset[i] &= ~(1 << (n-1))
colset[j] &= ~(1 << (n-1))
S[i][j] = 0
return False
N = 30
ns = len(str(N))
for _ in xrange(5):
S = [[0] * N for _ in xrange(N)]
assert complete(S, [0]*N, [0]*N, 0)
for row in S:
print ''.join('%*d ' % (ns, x) for x in row)
print
随机导入
def位计数(n):
i=0
而n:
i+=1
n&=n-1
返回i
def完成(行集、列集、条目):
N=长(S)
如果条目==N*N:
返回真值
i、 j=最大值(
((i,j)对于x范围内的i(N)对于x范围内的j(N),如果S[i][j]==0),
key=(lambda(i,j):位计数(行集[i]|列集[j]))
位=行集[i]|列集[j]
p=[n表示xrange(1,n+1)中的n,如果不是(位>>(n-1))&1]
随机。洗牌(p)
对于p中的n:
S[i][j]=n
行集[i]|=1