让我解释一下到目前为止我的计划。它是一个rubiks立方体解算器。我得到了一个混乱的立方体(这是初始状态)。这成为图的根节点。我正在使用iterative deepening depth first search
来“蛮力”这个混乱的立方体到一个可识别的状态,然后我可以使用模式识别来解决。
你可以想象,这是一个非常大的图,所以我想提出某种散列函数来检测这个图中的重复节点(从而加快遍历)。
我基本上不熟悉散列函数,但这就是我在想的......每个节点本质上都是rubik立方体的不同状态。所以如果我来到一个已经被看到的立方体状态(节点),我想跳过它。所以我需要一个哈希函数,它将我从状态变量带到校验和,其中状态变量是一个54个字符的字符串。唯一允许的字符是y, r, g, o, b, w
(对应于颜色)。
非常感谢设计此哈希函数的任何帮助。
答案 0 :(得分:2)
您始终可以尝试加密哈希函数。由于您的问题不是安全问题(没有攻击者故意尝试查找散列到相同值的不同状态),因此您可以使用已损坏的哈希函数。我建议尝试MD4,这非常快。您的54个字符的字符串非常适合MD4输入(MD4可以处理最多55个字节的输入作为单个块)。
基本的2.4 GHz PC每秒可以使用单个内核散布大约1,200万个这样的字符串,并使用简单的展开C实现(例如,在RFC中包含的示例代码中看起来像MD4Transform()
函数的那个1320)。这可能足以满足您的需求。
答案 1 :(得分:2)
1)不要使用哈希
您在rubik多维数据集上有9*6 = 54
个单独的面。即使浪费地每面使用1个字节,这也是432位,因此散列不会为您节省太多空间。每个面更好地打包3位到162位(21字节)。听起来我觉得你需要一种紧凑的方式来代表魔方。
OTOH,如果你想存储一组许多先前访问过的状态,那么我发现使用布隆过滤器而不是真实的设置会得到不错的结果(但通常是非最佳状态),空间要小得多利用率。
2)如果您与哈希的想法结婚: 只需使用MD5,它比建议的rubik状态稍微紧凑,相当快,并具有良好的碰撞属性 - 它不像你有恶意攻击者试图导致rubik立方体哈希碰撞; - )。
编辑:使用加密哈希函数(如MD4 / MD5),一旦有了实现算法的库或函数,通常很简单(例如:OpenSSL,GNU TLS和许多独立实现)。通常函数类似于void md5(unsigned char *buf, size_t len, unsigned char *digest)
,其中digest
指向预先分配的16字节缓冲区,buf
是要散列的数据(rubik立方体结构)。这是一些未经测试的C代码:
#include <openssl/md5.h>
void main()
{
unsigned char digest[16];
unsigned char buf[BUFLEN];
initializeBuffer(buf);
MD5(buf,BUFLEN,digest); // This is the openssl function
printDigest(digest);
}
请务必使用-lssl
编译/链接。
答案 2 :(得分:2)
对于最快的重复检测和删除 - 避免在第一时间生成许多重复的位置。这比生成和找到重复更容易做到更快。因此,例如,如果您有像F和B这样的移动,如果允许子序列FB也不允许BF,这会产生相同的结果。如果您刚刚完成了3F,请不要使用F进行跟踪。在最后三次移动的情况下,您可以生成一个允许下一步移动的小型查找表。
对于剩余的重复项,您需要快速哈希,因为有很多位置。为了使你的哈希变得快,正如其他人所评论的那样,你想要它的哈希值,位置的表示,是小的。有12个边缘立方体,有8个角落立方体。表示每个立方体的位置和方向需要每立方体仅占5位,即总共100位(12.5字节)。对于边缘,其位置为四位,翻转为一位。对于角落,其位置为3位,旋转为2位。您可以忽略最后一个边缘立方体,因为它的位置和翻转由其他边缘固定。使用此表示,您的位置已经减少到12个字节。
在rubik立方体位置有大约70个实际信息位,96位足够接近70位,这使得它实际上可以进一步散列这些位。即将此董事会表示视为您的哈希值。这可能听起来有点奇怪,但是根据你的问题,我正在设想你同时试验一个更简洁的立方体表示,更适合你的模式匹配。在这种情况下,可以将12字节值视为散列,其优点是一个永远不会发生冲突的散列。这使得重复测试代码和新值插入更短,更简单,更快捷。它将比目前为止建议的MD5解决方案便宜。
您可以使用许多其他技巧来减少搜索重复位置的工作。请查看http://cube20.org/的想法。
答案 3 :(得分:1)
只是为了建立理论上的最小表示 - 有效魔方的状态空间约为4.3*10^19
。然后,Log 2 (4.3 * 10 ^ 19)将确定表示该完整空间the ceiling of which is 66所需的位数。所以理论上 ,如果你可以对每个有效状态进行编号,那么任何给定的状态都可以用66位唯一表示。
虽然您可能想要关注他人的建议并找到更紧凑的方式来表示立方体,但请考虑用边缘,角落和面部来表示状态。由于合法立方体移动的交换规则,您应该能够连接12个4位边缘位置,8个3位角位置和6个3位面部位置的序列。这应该导致使用90位的唯一表示。
此表示可能不利于您创建树的方式,但它是唯一的,易于比较的,并且应该可以在现有表示中找到给定状态。
答案 4 :(得分:1)
8个角落:
8! = 40320
个排列排列,40320
可以用16位表示。每个角落立方体可以正确定向或顺时针旋转120°或逆时针旋转到三个不同的位置(分别表示为0,1和2)。
因此,如果要解码排列,角落立方体可以用28位表示,如果你想直接记录8个角中7位的位置,则可以用33位表示。
12个边框:
12! = 479001600
排列可以存储在29位中。每条边可以正确定位或翻转:
因此,边缘立方体可以用40位表示,如果你想解码排列,或者如果你想记录12个11边缘的所有位置和翻转,则用55位表示。
6个中心立方体
您不需要记录有关中心立方体的任何信息 - 它们相对于魔方立方体中心的球固定(假设您不担心立方体上任何徽标的方向)是不动的。
总强>: