【问题标题】:What is this algorithm mapping coordinates to numbers called?这种将坐标映射到数字的算法叫什么?
【发布时间】:2015-06-22 21:39:41
【问题描述】:

我正在编写一个可视化晶体的程序。作为程序的一部分,我必须在晶格结构中生成所有不同的基本点。对于那些不熟悉晶体学的人,您可以在这里找到这些结构的最一般情况:https://en.wikipedia.org/wiki/Hermann%E2%80%93Mauguin_notation#Lattice_types

问题是我想跟踪所有这些点。所以我给了他们一个号码。我用笔和纸尝试了一下,发现了一个很好的算法,通过以二进制形式将坐标(2D 或 3D)与数字(反之亦然)连接起来。

因此,例如,如果您想要一个简单的 2D 立方晶格,并且您想知道点号 14 的坐标。您可以将此二进制写为 001110。您将数字分为 00|11|10,在其中最右边部分代表(x,y)*1,中间部分代表(x,y)*2,左边部分代表(x,y)*4(对14号没用,只是让一切都清楚)等等。所以数字 14 映射到点 (3, 2)。

为前 50 个整数生成坐标的简单 C++ 程序:

int x, y;

for (int n = 0; n < 50; n++)
{
    x = 0;
    y = 0;

    bitset<16>  nset(n);

    for (int i = 0; i < 16/2; i++)
    {
        x+=(nset[2*i]*pow(2.,i));
        y+=(nset[2*i+1]*pow(2.,i));
    }

    cout  << n << "\t" << x << "\t" << y << endl;
}

我通过为 z 值保留一个附加列来将此算法扩展到 3D,并为其他 Lattice 类型通过保留类型为 x+1/2、y+1/2、z 的前一列或两列+1/2 属性,每种晶格类型都不同。

所以我的问题是:这个算法已经存在了吗?它有名字吗?或者这只是二进制数学的一个明显应用?我读过一些关于哈希图的东西,但这对我来说似乎更有效,至少在处理整数时是这样。

这是我在 stackexchange 上的第一个问题,我怀疑我必须在此处或物理论坛上发布此问题。或者在数学论坛上,因为这是一种 R^2->R 双射。所以如果这个问题不在正确的地方,请纠正我。

【问题讨论】:

    标签: algorithm coordinates coordinate-systems coordinate


    【解决方案1】:

    所以这是我的问题:这个算法是否已经存在?它有名字吗?

    这种映射称为 Z 阶曲线或Morton code:

    在数学分析和计算机科学中,Z 顺序、莫顿顺序或莫顿代码是一种将多维数据映射到一维同时保留数据点的局部性的函数。它由 G. M. Morton 于 1966 年推出。 多维中点的 z 值只需通过交错其坐标值的二进制表示来计算。一旦数据按此顺序排序,就可以使用任何一维数据结构,例如二叉搜索树、B 树、跳过列表或(低有效位被截断)哈希表。由此产生的排序可以等效地描述为从四叉树的深度优先遍历中得到的顺序。

    如示例 C++ 代码所示,x 坐标存储在偶数位中,y 坐标存储在奇数位中。映射可以很容易地扩展到更高的维度。

    可以在here 找到一些使用位操作操作对这些数字进行快速交织的算法。

    【讨论】:

      【解决方案2】:

      我可能误解了您的代码,但您所做的似乎是取二进制数的偶数位,将它们连接在一起形成一个新数字,并将该数字用作您的 x 坐标。你似乎对 y 坐标做同样的事情。

      我认为这种算法没有名称,尽管它看起来是一种非常标准的技术。对于它的价值,我认为通过使用位运算符而不是 bitset 和 pow,有一种更简单的方法来完成你在这里所做的事情:

      for (int n = 0; n < kUpperBound; n++) {
          int x = 0;
          int y = 0;
      
          for (int i = 0; i < 8; i++) {
              if (n & (1 << (2*i)) != 0) {
                 x += 1 << i;
              }
              if (n & (1 << (2*i + 1)) != 0) {
                 y += 1 << i;
              }
          }
          cout << n << " " << x << " " << y << endl;
      }
      

      值1 &lt;&lt; k是一个第k位为1的数字,否则为0。如果n 的第 k 位为 0,则使用按位 AND 运算符将此数与 n 与将返回 0,否则返回非零数。因此,测试if (n &amp; (1 &lt;&lt; k) != 0) 会检查n 的第k 位是否已设置。然后,我们不使用pow 来评估2n,而是使用1 &lt;&lt; k 具有数值2k这一事实。

      希望这会有所帮助!

      【讨论】:

      • 好的,改用位运算符是有意义的,谢谢!三次示例(取偶数位并连接)确实相当简单。我没有以那种方式看到它;)但我认为使用最后 1 或 2 位来生成其他(更复杂的)晶格类型可能是一个优势。
      【解决方案3】:

      不幸的是,这可能对您没有帮助,但作为一个奇怪的琐事,这对应于 joke language INTERCAL 中的 INTERLEAVE(或 MINGLE)运算符。

      我不知道这种编码的其他名称。但它通常用得不多,因为使用大多数计算机上可用的指令,将两个(或许多)整数的二进制表示简单地连接在一起要简单得多,而且速度更快,这只需要 O(d) 时间(并且可能只需要很少作为 d-1 机器指令)用于 d 尺寸。关于您的编码我能想到的一个优势是它不需要您为每个维度承诺固定的位大小,因此您需要对格点进行编码的最大位数与对数成正比最大坐标值——这是你实际使用的东西吗?

      【讨论】:

      • 我不使用日志。我使用它的目的是,通过这种方式,很容易知道已经生成了多少点 + 知道程序从下一个数字生成尚未采用的坐标。感谢您的帮助!
      猜你喜欢
      • 2017-12-05
      • 1970-01-01
      • 1970-01-01
      • 2014-03-05
      • 2020-06-21
      • 2011-12-29
      • 2012-07-25
      • 1970-01-01
      相关资源
      最近更新 更多