我正在尝试创建自己的益智游戏实现。
要创建我的游戏板,我需要遍历数组中的每个方块一次且仅一次。
遍历需要链接到相邻的邻居(水平、垂直或对角线)。
我正在使用以下形式的数组结构:
board[n,m] = byte
Each bit of the byte represents a direction 0..7 and exactly 2 bits are always set
Directions are numbered clockwise
0 1 2
7 . 3
6 5 4
Board[0,0] must have some combination of bits 3,4,5 set
我当前构建随机路径的方法是:
Start at a random position
While (Squares remain)
if no directions from this square are valid, step backwards
Pick random direction from those remaining in bitfield for this square
Erase the direction to this square from those not picked
Move to the next square
该算法在某些中等大小的网格上花费的时间太长,因为早期的选择删除了考虑范围。
我想要的是一个函数,它将索引放入每个可能的路径中,并返回填充该路径的数组。这将让我提供一个“种子”值以返回到这个特定的板。
欢迎其他建议..
I'm trying to create my own implementation of a puzzle game.
To create my game board, I need to traverse each square in my array once and only once.
The traversal needs to be linked to an adjacent neighbor (horizontal, vertical or diagonal).
I'm using an array structure of the form:
board[n,m] = byte
Each bit of the byte represents a direction 0..7 and exactly 2 bits are always set
Directions are numbered clockwise
0 1 2
7 . 3
6 5 4
Board[0,0] must have some combination of bits 3,4,5 set
My current approach for constructing a random path is:
Start at a random position
While (Squares remain)
if no directions from this square are valid, step backwards
Pick random direction from those remaining in bitfield for this square
Erase the direction to this square from those not picked
Move to the next square
This algorithm takes far too long on some medium sized grids, because earlier choices remove areas from consideration.
What I'd like to have is a function that takes an index into every possible path, and returns with the array filled in for that path. This would let me provide a 'seed' value to return to this particular board.
Other suggestions are welcome..
发布评论
评论(4)
我将从穿过网格的简单路径开始,然后随机选择板上的位置(使用输入种子为随机数生成器提供种子)并在可能的情况下在路径上执行“切换”。例如:
有了足够的开关,你应该得到一条看起来随机的路径。
I would start with a trivial path through the grid, then randomly select locations on the board (using the input seed to seed the random number generator) and perform "switches" on the path where possible. e.g.:
With enough switches you should get a random-looking path.
奇怪的是,我在数学学位上花了一个本科暑假来研究这个问题,并提出解决它的算法。首先我对其他答案进行评论。
Martin B:正确地将其识别为哈密顿路径问题。但是,如果图形是规则网格(正如你们两个在评论中讨论的那样),那么可以轻松找到哈密顿路径(例如,蛇行行主顺序。)
agnorest:正确地讨论了一类哈密顿路径问题困难的问题。 agnorest 还提到了可能利用图形结构,这很有趣,但在常规网格的情况下是微不足道的。
我现在将通过详细阐述我认为您想要实现的目标来继续讨论。正如您在评论中提到的,在规则的格子/网格上找到某些类别的“空间填充”非相交“行走”是微不足道的。然而,您似乎不仅仅满足于这些,并且希望找到一种算法来找到随机覆盖您的网格的“有趣”行走。但在探讨这种可能性之前,我想指出这些路径的“不相交”属性极其重要,也是导致枚举它们的困难的原因。
事实上,我在暑期实习中研究的东西叫做自我回避行走 。令人惊讶的是,SAW(自避行走)的研究对于物理建模的一些子领域非常重要(并且是 曼哈顿项目!)您在问题中给出的算法实际上是称为“增长”算法或 Rosenbluth 算法(以 罗森布鲁斯元帅)。有关该算法的通用版本(用于估计 SAW 统计数据)及其与物理关系的更多详细信息,可以在此类文献中轻松找到 论文。
众所周知,二维声表面波很难研究。关于二维声表面波的理论结果知之甚少。奇怪的是,在高于 3 维的情况下,您可以说 SAW 的大多数特性在理论上都是已知的,例如它们的生长常数。 可以说,二维 SAW 是非常有趣的事情!
在这个讨论中谈论您手头的问题,您可能会发现您的增长算法的实现被相当“切断”频繁且无法扩展以填满整个格子。在这种情况下,将您的问题视为哈密顿路径问题更合适。我寻找有趣的哈密顿路径的方法是将问题表述为整数线性规划,并添加要在 ILP 中使用的固定随机边。随机边缘的固定将为生成过程提供随机性,并且 ILP 部分将有效地计算某些配置是否可行,如果可行则返回解决方案。
代码
以下代码实现了任意初始条件的哈密顿路径或循环问题。它在具有 4 个连通性的常规 2D 晶格上实现。该公式遵循标准子巡回消除 ILP 拉格朗日。子游览被延迟消除,这意味着可能需要多次迭代。
您可以对此进行扩展以满足随机条件或您认为对您的问题“有趣”的初始条件。如果初始条件不可行,它会提前终止并打印此内容。
此代码取决于 NetworkX 和 纸浆。
Bizarrely enough, I spent an undergrad summer in my Math degree studying this problem as well as coming up with algorithms to address it. First I will comment on the other answers.
Martin B: Correctly identified this as a Hamiltonian path problem. However, if the graph is a regular grid (as you two discussed in the comments) then a Hamiltonian path can trivially be found (snaking row-major order for example.)
agnorest: Correctly talked about the Hamiltonian path problem being in a class of hard problems. agnorest also alluded to possibly exploiting the graph structure, which funny enough, is trivial in the case of a regular grid.
I will now continue the discussion by elaborating on what I think you are trying to achieve. As you mentioned in the comments, it is trivial to find certain classes of "space-filling" non intersecting "walks" on a regular lattice/grid. However, it seems you are not satisfied only with these and would like to find an algorithm that finds "interesting" walks that cover your grid at random. But before I explore that possibility, I'd like to point out that the "non-intersecting" property of these walks is extremely important and what causes the difficulties of enumerating them.
In fact, the thing that I studied in my summer internship is called the Self Avoiding Walk. Surprisingly, the study of SAWs (self avoiding walks) is very important to a few sub-domains of modeling in physics (and was a key ingredient to the Manhattan project!) The algorithm you gave in your question is actually a variant on an algorithm known as the "growth" algorithm or Rosenbluth's algorithm (named after non other than Marshal Rosenbluth). More details on both the general version of this algorithm (used to estimate statistics on SAWs) as well as their relation to physics can be readily found in literature like this thesis.
SAWs in 2 dimensions are notoriously hard to study. Very few theoretical results are known about SAWs in 2 dimensions. Oddly enough, in higher than 3 dimensions you can say that most properties of SAWs are known theoretically, such as their growth constant. Suffice it to say, SAWs in 2 dimensions are very interesting things!
Reigning in this discussion to talk about your problem at hand, you are probably finding that your implementation of the growth algorithm gets "cut off" quite frequently and can not expand to fill the whole of your lattice. In this case, it is more appropriate to view your problem as a Hamiltonian Path problem. My approach to finding interesting Hamiltonian paths would be to formulate the problem as an Integer Linear Program, and add fix random edges to be used in the ILP. The fixing of random edges would give randomness to the generation process, and the ILP portion would efficiently compute whether certain configurations are feasible and if they are would return a solution.
The Code
The following code implements the Hamiltonian path or cycle problem for arbitrary initial conditions. It implements it on a regular 2D lattice with 4-connectivity. The formulation follows the standard sub-tour elimination ILP Lagrangian. The sub-tours are eliminated lazily, meaning there can be potentially many iterations required.
You could augment this to satisfy random or otherwise initial conditions that you deem "interesting" for your problem. If the initial conditions are infeasible it terminates early and prints this.
This code depends on NetworkX and PuLP.
本质上,您想要构建一个 哈密尔顿路径:精确访问图的每个节点的路径一次。
一般来说,即使您只想测试图是否包含哈密顿路径,那也已经是 NP 完全的了。在这种情况下,很明显该图至少包含一条哈密顿路径,并且该图具有很好的规则结构——但是枚举所有哈密顿路径似乎仍然是一个难题。
关于哈密顿路径问题的维基百科条目有一个随机算法,用于查找声称的哈密顿路径“在大多数图表上都很快”。它与您的算法不同,因为它会交换路径的整个分支,而不是仅回溯一个节点。这种更“激进”的策略可能会更快——尝试一下看看。
您可以让用户输入随机数生成器的种子来重建特定路径。您仍然不会枚举所有可能的路径,但我想这对于您的应用程序来说可能不是必需的。
Essentially, you want to construct a Hamiltonian path: A path that visits each node of a graph exactly once.
In general, even if you only want to test whether a graph contains a Hamiltonian path, that's already NP-complete. In this case, it's obvious that the graph contains at least one Hamiltonian path, and the graph has a nice regular structure -- but enumerating all Hamiltonian paths still seems to be a difficult problem.
The Wikipedia entry on the Hamiltonian path problem has a randomized algorithm for finding a Hamiltonian path that is claimed to be "fast on most graphs". It's different from your algorithm in that it swaps around a whole branch of the path instead of backtracking by just one node. This more "aggressive" strategy might be faster -- try it and see.
You could let your users enter the seed for the random number generator to reconstruct a certain path. You still wouldn't be enumerating all possible paths, but I guess that's probably not necessary for your application.
这最初是对 Martin B 的帖子的评论,但它增长了一点,所以我将其发布为“答案”。
首先,枚举所有可能的哈密顿路径的问题与#P复杂度类——NP的可怕哥哥非常相似。 维基百科一如既往地可以解释。正是出于这个原因,我希望您不需要真正了解每一条路径,而只是了解大多数路径。如果没有,那么,如果您可以利用图表的结构,那么仍然有希望。
(这里有一个有趣的方式来解释为什么枚举所有路径真的很困难:假设你的图表有 10 条路径,你认为你已经完成了。邪恶的老我走过来说“好吧,向我证明有不是第 11 条路径”。有一个既能说服我又不需要花时间来证明的答案 --- 这将是相当令人印象深刻的!证明不存在任何路径是 co -NP,这也很难证明只存在某个数字,这听起来很困难,所以我有点糊涂,但到目前为止我认为我所说的一切都是正确的。 )
无论如何,我的谷歌-fu在这个问题上似乎特别弱,这令人惊讶,所以我没有一个冷酷的答案给你,但我有一些提示?
所以这些只是一些想法。如果您的图表还有任何其他方面(需要特定的起点,或特定的终点,或关于哪个节点可以连接到哪个节点的附加规则),它可能会非常有帮助! NP 完全问题和 P(快速)算法之间的界限通常很窄。
希望这有帮助,-Agor。
This was originally a comment to Martin B's post, but it grew a bit, so I'm posting it as an "answer".
Firstly, the problem of enumerating all possible Hamiltonian Paths is quite similar to the #P complexity class --- NP's terrifying older brother. Wikipedia, as always, can explain. It is for this reason that I hope you don't really need to know every path, but just most. If not, well, there's still hope if you can exploit the structure of your graphs.
(Here's a fun way of looking at why enumerating all paths is really hard: Let's say you have 10 paths for the graph, and you think you're done. And evil old me comes over and says "well, prove to me that there isn't an 11th path". Having an answer that could both convince me and not take a while to demonstrate --- that would be pretty impressive! Proving that there doesn't exist any path is co-NP, and that's also quite hard. Proving that there only exists a certain number, that sounds very hard. Its late here, so I'm a bit fuzzy-headed, but so far I think everything I've said is correct.)
Anyways, my google-fu seems particularly weak on this subject, which is surprising, so I don't have a cold answer for you, but I have some hints?
So those were just a few ideas. If there is any other aspect of your graph (a particular starting point is required, or a particular ending point, or additional rules about what node can connect to which), it may be quite helpful! The border between NP-complete problems and P (fast) algorithms is often quite thin.
Hope this helps, -Agor.