查找字符串中只出现一次的字符
我正在用 PHP 编写一个算法来解决给定的数独难题。 我已经设置了一个带有两个类的面向对象的实现:一个用于 9x9 棋盘上每个单独图块的 Square 类,以及一个具有矩阵的 Sudoku 类代表棋盘的方块
。
我正在使用的算法的实现是一种三层方法。 第一步,仅解决最基本的难题(但也是最有效的),是根据棋盘的初始设置填充只能取单个值的任何方格,并相应地调整其余部分的约束未解的方块。
通常,这种“不断传播”的过程并不能完全解决棋盘问题,但它确实解决了相当大的问题。 然后第二层将启动。它将解析每个单元(或 9 个方格,它们必须全部具有唯一的数字分配,例如行或列),以获取每个未解方格的“可能”值。 这个可能值的列表在 Square 类中表示为字符串:
class Square {
private $name; // 00, 01, 02, ... , 86, 87, 88
private $peers; // All squares in same row, col, and box
private $number; // Assigned value (0 if not assigned)
private $possibles; // String of possible numbers (1-9)
public function __construct($name, $p = 0) {
$this->name = $name;
$this->setNumber($p);
if ($p == 0) {
$this->possibles = "123456789";
}
}
// ... other functions
给定一个单元中未解的正方形的整个数组(如上面第二层中所述),第二层将连接所有字符串将“可能”组合成一个字符串。 然后,它将在该单个字符串中搜索任何唯一的字符值 - 不重复的值。 这将表明,在平方单位内,只有一个平方可以呈现该特定值。
我的问题是:为了实现第二层,如何解析一个单元中所有可能值的字符串并轻松检测唯一值? 我知道我可以创建一个数组,其中每个索引都由数字 1-9 表示,并且我可以为我找到的该数字的每个可能值将相应索引处的值增加 1,然后再次扫描数组以查找任何值为 1,但这似乎效率极低,每个单元都需要对数组进行两次线性扫描,而数独谜题中有 27 个单元。
I'm writing an algorithm in PHP to solve a given Sudoku puzzle. I've set up a somewhat object-oriented implementation with two classes: a Square
class for each individual tile on the 9x9 board, and a Sudoku
class, which has a matrix of Square
s to represent the board.
The implementation of the algorithm I'm using is a sort of triple-tier approach. The first step, which will solve only the most basic puzzles (but is the most efficient), is to fill in any squares which can only take a single value based on the board's initial setup, and to adjust the constraints accordingly on the rest of the unsolved squares.
Usually, this process of "constant propagation" doesn't solve the board entirely, but it does solve a sizable chunk. The second tier will then kick in. This parses each unit (or 9 squares which must all have unique number assignments, e.g. a row or column) for the "possible" values of each unsolved square. This list of possible values is represented as a string in the Square
class:
class Square {
private $name; // 00, 01, 02, ... , 86, 87, 88
private $peers; // All squares in same row, col, and box
private $number; // Assigned value (0 if not assigned)
private $possibles; // String of possible numbers (1-9)
public function __construct($name, $p = 0) {
$this->name = $name;
$this->setNumber($p);
if ($p == 0) {
$this->possibles = "123456789";
}
}
// ... other functions
Given a whole array of unsolved squares in a unit (as described in the second tier above), the second tier will concatenate all the strings of "possibles" into a single string. It will then search through that single string for any unique character values - values which do not repeat themselves. This will indicate that, within the unit of squares, there is only one square that can take on that particular value.
My question is: for implementing this second tier, how can I parse this string of all the possible values in a unit and easily detect the unique value(s)? I know I could create an array where each index is represented by the numbers 1-9, and I could increment the value at the corresponding index by 1 for each possible-value of that number that I find, then scan the array again for any values of 1, but this seems extremely inefficient, requiring two linear scans of an array for each unit, and in a Sudoku puzzle there are 27 units.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
这有点像您已经排除的“效率极低”,但使用内置函数,因此它可能非常有效:
打印:“56789”
This is somewhat like what you have already ruled out as "extremely inefficient", but with builtin functions so it might be quite efficient:
Prints: "56789"
考虑使用二进制数来表示“可能”,因为像 AND、OR、XOR 这样的二进制运算往往比字符串运算快得多。
例如,如果一个正方形可能为“2”和“3”,则使用二进制数 000000110 来表示该正方形的可能性。
以下是找到独特之处的方法:
我不确定这种方法是否真的会更快,但值得研究。
Consider using a binary number to represent your "possibles" instead, because binary operations like AND, OR, XOR tend to be much faster than string operations.
E.g. if "2" and "3" are possible for a square, use the binary number 000000110 to represent the possibilities for that square.
Here's how you could find uniques:
I'm not sure if this method will actually work faster but it's worth looking into.
这会给你每一个单身人士。 获取该数组中某个数字的第一个和最后一个位置,如果它们匹配且不都是 FALSE(严格比较,以防开头有一个单例),则该数组中只有一个这样的数字。
如果您非常担心这里的速度,您可能可以用
最少的计算次数替换该循环的内部。 好吧,假设 PHP 的本机 strpos 是处理这些事情的最佳方法,据我所知,这并非不合理。
That'll give you every singleton. Get the first and last positions of a number in that array, and if they match and aren't both FALSE (strict comparison in case there's a singleton right at the start) then there's only one such number in that array.
If you're super super worried about speed here, you can probably replace the interior of that loop with
for a minimum number of computations. Well, assuming PHP's native strpos is the best way to go about these things, which as far as I know is not unreasonable.
上次我愚弄数独解决时,我有第三堂课,名为“跑步”。 为每行、每列和 3x3 正方形创建一个 Run 实例。 每个方格都有与其相关的三个运行。 Run 类包含尚未放入运行中的一组数字。 然后,求解棋盘需要迭代地在每个方格处相交集合。 这可以处理 80% 的大多数中型板和 60% 的大多数硬板。 一旦您完成了整个板且没有任何更改,您就可以继续进行更高级别的逻辑。 每次您的更高级别逻辑填充一个正方形时,您都会再次遍历您的正方形。
此设置的好处是您可以轻松地向求解器添加变体。 假设您使用两个对角线也是唯一的变体。 您只需在这 18 个方格中添加第 4 次游程即可。
The last time I fooled with Sudoku solving, I had a third class called "Run". A Run instance is created for each row, col and 3x3 square. Every square has three runs associated with it. The Run class contains the set of numbers not yet placed within the run. Solving the board then involves intersecting the sets at each square iteratively. This takes care of 80% of most medium boards and 60% of most hard boards. Once you've gone through the whole board with no changes, you can move on to higher level logic. Each time your higher level logic fills a square, you run through your squares again.
The nice thing about this setup is you can easily add variants to the solver. Say you use the variant where the two diagonals are also unique. You just add a 4th run to those 18 squares.
我要做的实际上是使用二进制位来存储实际值,正如另一个答案所建议的那样。 这允许进行有效的检查,并且通常可能使数独本身具有更数学(=高效且更短)的解决方案(只是我的印象,我还没有研究过这一点)。
基本上,您不用数字而是用 2 的幂来表示方格中的数字,
这样,您可以简单地添加单个方格的内容来找出 3x3 或行或数独的任何其他子集中缺少哪些数字,例如这样:
现在 $possibles 在该方块中可能的数字的位位置中包含“1”,您可以对其他方块的结果进行按位运算以将它们匹配在一起,如下所示
: 比方说:
What I would do, is actually use binary bits for storing actual values as another answer suggested. That allows to do efficient checks and in general might lend Sudoku itself to more mathematical(=efficient and shorter) solution (just my impression, I have not researched this).
Basically, you represent the numbers in squares not with digits, but with powers of 2
this way, you can simply add contents' of single squares to find out what numbers are missing in a 3x3 or a row or any other subset of sudoku, like this:
now the $possibles contains "1" in bit positions of digits that are possible in this square and you can do bitwise operations with the results for other squares to match them together, like this:
eg. let's say:
这是一种仅使用 PHP 内置函数的方法,应该相当快。
Here is a way using only PHP built-in functions which should be pretty fast.