我正在研究一个问题,我需要创建一个NxN矩阵(N在这里作为输入给出),这样,所有条目都在[1,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。
我知道这是不正确的,因为它可能会卡在某个无限循环中,而且我也没有在这里使用回溯 有人可以指导我如何使用回溯来解决我遇到的问题吗?
如果有人可以提供一些代码,那就太好了。感谢。
答案 0 :(得分:2)
这里的伪代码(基本上是Knuth's "Algorithm X",专门针对这个问题)生成一个随机拉丁方:
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(如果有),返回描述解决方案是否存在的bool。它可以返回任何可能的解决方案。
请注意,在此过程中,"选择"对于空单元格而言,这是一个不会立即导致问题的数字 - 也就是说,任何数字都不会出现在该行和列中。
您可以采取各种优化措施来加快速度(一个简单的示例:传递额外的参数来计算剩余的空白单元数),但https://en.wikipedia.org/wiki/Dancing_Links是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