【问题标题】:index to coordinates in diagonal (zigzag) traverse对角线(之字形)遍历坐标的索引
【发布时间】:2018-11-22 23:54:12
【问题描述】:

所以我有一个像这样的 4x4 矩阵

 |0 1 2 3
-+-------
0|0 1 3 6
1|2 4 7 a
2|5 8 b d
3|9 c e f

我正在按照其中的十六进制字符指定的顺序遍历它。所以从 (0, 0) 开始,然后是 (1, 0), (0, 1), (2, 0), (1, 1), (0, 2)...

代码如下:

def diagonal(n):
    for a in range(n):
        for b in range(a + 1):
            yield a - b, b
    for a in range(n - 1):
        for b in range(n - a - 1):
            yield n - b - 1, b + 1 + a

遍历这个给出

for x, y in diagonal(4):
    print((x, y))

# (0, 0)
# (1, 0)
# (0, 1)
# (2, 0)
# (1, 1)
# (0, 2)
# (3, 0)
# (2, 1)
# (1, 2)
# (0, 3)
# (3, 1)
# (2, 2)
# (1, 3)
# (3, 2)
# (2, 3)
# (3, 3)

这正是我想要的。我坚持的部分是试图创建一个函数,我给它索引,它给我坐标。所以对于我的 4x4 矩阵,(这仍然是十六进制)

0 -> (0, 0)
1 -> (1, 0)
2 -> (0, 1)
3 -> (2, 0)
4 -> (1, 1)
5 -> (0, 2)
6 -> (3, 0)
7 -> (2, 1)
8 -> (1, 2)
9 -> (0, 3)
a -> (3, 1)
b -> (2, 2)
c -> (1, 3)
d -> (3, 2)
e -> (2, 3)
f -> (3, 3)

我打算继续使用可变大小的方阵,所以我不能将这些值硬编码到字典中。

我一直在修补几个小时试图让它工作,但我一辈子都无法让它工作。

这不是家庭作业,只是我在业余时间正在做的事情,它正在慢慢把我逼上墙。

如果有任何不清楚的地方,请不要犹豫。

提前致谢。

编辑: 我想有人会评论这篇文章Traverse Matrix in Diagonal strips,这很相似,但与我的第一个函数一样,它只迭代坐标,我无法从索引中计算出坐标。

【问题讨论】:

    标签: python matrix


    【解决方案1】:

    这是一个似乎可以做你想做的事情的函数。代码后有解释。

    from math import sqrt
    
    def triangular(n):
        return n * (n + 1) // 2
    
    def coords_from_index(ndx, n=4):
        if ndx < triangular(n):
            basecol = (int(sqrt(8 * ndx + 1)) - 1) // 2
            row = ndx - triangular(basecol)
            col = basecol - row
        else:
            oldcol, oldrow = coords_from_index(n**2 - 1 - ndx, n)
            row = n - 1 - oldrow
            col = n - 1 - oldcol
        return col, row
    
    # Test code
    n = 4
    for ndx in range(n**2):
        print(hex(ndx)[2:], '->', coords_from_index(ndx, n))
    

    该测试代码的打印输出是:

    0 -> (0, 0)
    1 -> (1, 0)
    2 -> (0, 1)
    3 -> (2, 0)
    4 -> (1, 1)
    5 -> (0, 2)
    6 -> (3, 0)
    7 -> (2, 1)
    8 -> (1, 2)
    9 -> (0, 3)
    a -> (3, 1)
    b -> (2, 2)
    c -> (1, 3)
    d -> (3, 2)
    e -> (2, 3)
    f -> (3, 3)
    

    这是我的代码的简要说明。

    就像您按顺序生成坐标的代码一样,我将正方形的左上三角形与右下三角形区别对待。让我们先看看左上三角形,对于大小为4 的正方形,它包括索引09

    如果您查看每一列中的顶部数字,您会发现这些数字是“三角形数字”,它们是从0 开始的连续整数的总和。所以顶行是00+10+1+20+1+2+3。这些数字的著名公式是

    triangular(n) = n * (n + 1) // 2
    

    所以我为此写了一个小程序。如果你知道三角数(叫它ndx)并想找到n,你可以用代数解二次方程,你得到

    n = (sqrt(8 * ndx + 1) - 1) // 2
    

    如果您将sqrt 替换为int(sqrt(,您会得到与三角数相同的结果,并且您还会得到介于两个三角数之间的任何ndx 的“基”数。然后,您可以使用您的索引和“基数”来查找索引对应的列和行。

    现在,如果您查看包含索引af 的右下三角形,您会发现左上三角形存在对称性。我选择使用这种对称性来计算任何这些索引的行和列。我本可以更直接地计算它们,但我使用的方法效果很好。

    请注意,如果您使用非常大的 nndx 值,那么由于浮点运算的变幻莫测,int(sqrt( 并不总是给出正确的答案。但是ndx 需要非常 大,在2**50 的数量级上,然后才发生。如果您需要更可靠的例程来计算整数平方根,请告诉我。

    【讨论】:

      【解决方案2】:

      这基本上是一个算法问题:)

      我建议使用以下算法:

      1. 查找请求的索引所依赖的对角线
      2. 在对角线上查找索引的位置
      3. 推断坐标

      我建议的实现如下:

      作为初始步骤,准备每个对角线中第一个线性索引的数组。
      例如,在 4x4 矩阵中,数组将如下所示:[0, 1, 3, 6, 10, 30, 15]
      注意 这些基本上是箱。这意味着所有维持a[i] &lt;= index &lt; a[i+1] 的线性索引都在第i 个对角线上。

      比,接收索引时:

      1. 查找请求索引所在的对角线。
        您可以通过找到满足 bin[d] &lt;= index &lt; bin[d]

      2. d 来做到这一点
      3. 找到该对角线上的位置。
        这是 bin[d] 和索引之间的距离。这意味着如果 index - bin[d] = k,则索引位于该对角线上的第 k 个位置。

      4. 推断坐标。
        到目前为止,您已经发现索引位于第 d 条对角线上的第 k 个位置。

        让我们表示矩阵的大小是MxM
        如果 x 如果 x > M-1 比我们在对角线上 (d - (m-1) + k, (m-1) - k)
        当我们插入之前找到的 k 时,我们会找到请求的坐标。

      实施

      def ind2cor(index):
          d = bisect_left(a, index)
          if index != a[d]:
              d -= 1
      
          k = index - a[d]
      
          if d <= m-1:
              return k, d - k
          else:
              return d - (m - 1) + k, (m - 1) - k
      

      示例:

      m = 4
      a = [0, 3, 6, 10, 13, 15]
      for i in range(16):
          print('{}: {}'.format(i, ind2cor(i)))
      

      产量:

      0: (0, 0)
      1: (0, 1)
      2: (1, 0)
      3: (0, 2)
      4: (1, 1)
      5: (2, 0)
      6: (0, 3)
      7: (1, 2)
      8: (2, 1)
      9: (3, 0)
      10: (1, 3)
      11: (2, 2)
      12: (3, 1)
      13: (2, 3)
      14: (3, 2)
      15: (3, 3)
      

      我还可以提供一种方法来生成对角线的索引:

      def genereate_diagonal_indices(m):
          a = [0]
          for i in range(1, m):
              a.append(a[-1] + i)
          for i in range(m, 1, -1):
              a.append(a[-1] + i)
           return a
      

      所以你可以在上面的代码中使用a = genereate_diagonal_indices(m)

      【讨论】:

      • 出于兴趣,我比较了我和 Rory 提供的算法的复杂性。似乎在时间复杂度上,Rory 的算法和我的算法都是 O(log(m)),其中 m 是矩阵的一个轴的长度。在内存复杂度中,Rory 的算法是 O(1),而我的算法是 Theta(m)。
      • 我相信你误解了我的算法。有一个if 语句和一个结果递归调用,但这些最多发生一次并且不会增加算法复杂性。我相信,sqrt 运算符在现代 CPU 中是在 O(1) 中完成的,尽管对于某些 CPU 来说我可能是错误的。结果,我的算法进行直接计算,因此在时间和内存上都是 O(1)。除非您的意思是m 是正方形一侧长度的数?
      • 我可能误解了,我很乐意讨论它!我声称由于 sqrt 的计算,您的算法的时间复杂度为 O(log(m))。我在互联网上搜索了 sqrt 计算的复杂性,发现这个 stackoverflow.com/questions/23103847/… 声称它是 O(log(m))。然而,它可能是错误的。你能支持 sqrt 时间复杂度为 O(1) 的说法吗? :)
      • 这一点值得商榷。我可以声称我注意到我的代码适用于ndx &lt; 2**50,因此限制使代码为 O(1)。相反,我会在我的电脑上给出计时结果。在int(sqrt(n)) 上使用%timeitn=1 提供295 ns,为n=2 提供299 ns,为n=2**25-1 提供313 ns,为n=2**50-1 提供371 ns。这比我声称的 O(1) 差,也比你声称的 O(log(n)) 好。我不确定那是什么——它看起来比 O(log(log(n))) 好一点。
      • 如果我们假设ndx &lt; c,那么我也可以推断出m &lt; c2,这也将使我的算法成为O(1)。那么,为了论证,我们是否应该在没有这个假设的情况下继续?感谢您尝试收集计时结果!结果很有趣,但由于我的行为有许多其他因素,我不确定我们能否可靠地尝试通过它们推断复杂性。因此,我想找到理论上的复杂性。我搜索了更多,发现了这个cstheory.stackexchange.com/questions/9706/…
      猜你喜欢
      • 1970-01-01
      • 2022-10-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多