Algorithm 识别唯一自由PolyMino(或PolyMino哈希)的算法
简而言之:如何散列免费的PolyMino? 这可以概括为:如何有效地散列二维整数坐标的任意集合,其中一个集合包含唯一的非负整数对,并且当且仅当没有平移、旋转或翻转可以将其完全映射到另一个集合时,一个集合才被认为是唯一的? 对于不耐烦的读者,请注意我完全了解暴力手段。我在寻找一个更好的方法——或者一个非常令人信服的证据,证明没有其他方法可以存在 我正在研究一些不同的算法来生成随机变量。我想测试它们的输出,以确定它们的随机性——即给定顺序的某些实例比其他实例生成的频率更高。从视觉上看,很容易识别一个自由的Polymino的不同方向,例如,下面的维基百科插图显示了“F”pentomino()的所有8个方向: 如何在这个polymino上输入一个数字——也就是说,散列一个免费的polymino?我不想依赖于“命名”Polyminos的预先列表。无论如何,广泛一致的名称只适用于第4和第5号令 这不一定等同于枚举给定顺序的所有自由(或单边或固定)多米诺。我只想计算给定配置出现的次数。如果生成算法从未生成某个polymino,那么它将不会被计算在内 计数的基本逻辑是:Algorithm 识别唯一自由PolyMino(或PolyMino哈希)的算法,algorithm,hash,coordinates,Algorithm,Hash,Coordinates,简而言之:如何散列免费的PolyMino? 这可以概括为:如何有效地散列二维整数坐标的任意集合,其中一个集合包含唯一的非负整数对,并且当且仅当没有平移、旋转或翻转可以将其完全映射到另一个集合时,一个集合才被认为是唯一的? 对于不耐烦的读者,请注意我完全了解暴力手段。我在寻找一个更好的方法——或者一个非常令人信服的证据,证明没有其他方法可以存在 我正在研究一些不同的算法来生成随机变量。我想测试它们的输出,以确定它们的随机性——即给定顺序的某些实例比其他实例生成的频率更高。从视觉上看,很容易识别一个
testcount = 10000 // Arbitrary
order = 6 // Create hexominos in this test
hashcounts = new hashtable
for i = 1 to testcount
poly = GenerateRandomPolyomino(order)
hash = PolyHash(poly)
if hashcounts.contains(hash) then
hashcounts[hash]++
else
hashcounts[hash] = 1
我要找的是一个高效的PolyHash
算法。输入PolyMinos仅定义为一组坐标。T tetronimo的一个方向可以是,例如:
[[1,0], [0,1], [1,1], [2,1]]:
|012
-+---
0| X
1|XXX
您可以假设输入PolyMino已被规范化,以便与X轴和Y轴对齐,并且只有正坐标。正式而言,每套:
- 在x值为0时,将至少有1个坐标
- y值为0时,将至少有1个坐标
- 在x<0或y<0的位置没有任何坐标
- 旋转(6倍)
- 翻转(1x)
- 翻译(7x)
- 散列(8x)
- 查找计算哈希的最小值(1x)
如果你真的害怕散列冲突,一个有效的散列函数是为坐标生成一个散列函数x+order*y,然后循环遍历一个工件的所有坐标,将(order^i)*散列(coord[i])添加到工件散列中。这样,您就可以保证不会发生任何哈希冲突。我最近也处理过同样的问题。我用简单的方法解决了这个问题 (1) 为PolyMino生成唯一的ID,这样每个相同的poly将具有相同的UID。例如,查找边界框,规格化边界框的角,并收集非空单元格集。 (2) 通过旋转(和翻转,如果合适的话)Polymino生成所有可能的排列,并寻找重复的排列 除了简单之外,这种野蛮方法的优点是,如果
多边形可以通过其他方式进行区分,例如,如果其中一些多边形是彩色或编号的。您可以将其减少为8个哈希操作,而无需翻转、旋转或重新转换 请注意,此算法假定您使用的是相对于自身的坐标。也就是说它不是在野外 与其应用翻转、旋转和平移操作,不如简单地更改散列的顺序 例如,让我们以上面的F pent为例。在这个简单的示例中,我们假设哈希操作是这样的:
int hashPolySingle(Poly p)
int hash = 0
for x = 0 to p.width
fory = 0 to p.height
hash = hash * 31 + p.contains(x,y) ? 1 : 0
hashPolySingle = hash
int hashPoly(Poly p)
int hash = hashPolySingle(p)
p.rotateClockwise() // assume it translates inside
hash = hash * 31 + hashPolySingle(p)
// keep rotating for all 4 oritentations
p.flip()
// hash those 4
我将对1个多边形应用8个不同的散列函数,而不是将函数应用于多边形的所有8个不同方向
int hashPolySingle(Poly p, bool flip, int corner)
int hash = 0
int xstart, xstop, ystart, ystop
bool yfirst
switch(corner)
case 1: xstart = 0
xstop = p.width
ystart = 0
ystop = p.height
yfirst = false
break
case 2: xstart = p.width
xstop = 0
ystart = 0
ystop = p.height
yfirst = true
break
case 3: xstart = p.width
xstop = 0
ystart = p.height
ystop = 0
yfirst = false
break
case 4: xstart = 0
xstop = p.width
ystart = p.height
ystop = 0
yfirst = true
break
default: error()
if(flip) swap(xstart, xstop)
if(flip) swap(ystart, ystop)
if(yfirst)
for y = ystart to ystop
for x = xstart to xstop
hash = hash * 31 + p.contains(x,y) ? 1 : 0
else
for x = xstart to xstop
for y = ystart to ystop
hash = hash * 31 + p.contains(x,y) ? 1 : 0
hashPolySingle = hash
然后以8种不同的方式调用。您还可以将hashPolySingle封装在拐角处的for循环中,以及翻转与否。尽管如此
int hashPoly(Poly p)
// approach from each of the 4 corners
int hash = hashPolySingle(p, false, 1)
hash = hash * 31 + hashPolySingle(p, false, 2)
hash = hash * 31 + hashPolySingle(p, false, 3)
hash = hash * 31 + hashPolySingle(p, false, 4)
// flip it
hash = hash * 31 + hashPolySingle(p, true, 1)
hash = hash * 31 + hashPolySingle(p, true, 2)
hash = hash * 31 + hashPolySingle(p, true, 3)
hash = hash * 31 + hashPolySingle(p, true, 4)
hashPoly = hash
通过这种方式,可以隐式地从每个方向旋转多边形,但实际上并没有执行旋转和平移。它执行8个散列,这似乎是完全必要的,以便准确地散列所有8个方向,但不浪费任何经过多边形的过程,而多边形实际上没有进行散列。在我看来,这是最优雅的解决方案
请注意,可能有更好的hashPolySingle()算法可供使用。Mine使用笛卡尔穷举算法,其顺序为O(n^2)
。其最坏情况是L形,这将导致只有N
元素才会出现N/2*(N-1)/2
大小的正方形,或者效率为1:(N-1)/4
,而I形为1:1
。也可能是架构强加的固有不变量实际上会使其效率低于原始算法
我的怀疑是,通过模拟笛卡尔穷举,将节点集转换为可遍历的双向图,从而导致节点被击中,可以缓解上述问题
2,2,3,1,2,2,3,2,2,3,2,1
1,2,2,3,1,2,2,3,2,2,3,2
1- 2- 2- 3- 1- 2- 2- 3- 2- 2- 3- 2
= 01-10-10-11-01-10-10-11-10-10-11-10
= 00000000011010110110101110101110
= 0x006B6BAE
1*3^11 + 2*3^10 + 2*3^9 + 3*3^8 + 1*3^7 + 2*3^6
+ 2*3^5 + 3*3^4 + 2*3^3 + 2*3^2 + 3*3^1 + 2*3^0
= 0x0005795F