很酷的问题。这是一个分析和一个算法。
使用此算法的一个关键优势是它全部使用简单的整数计算完成;它没有“if”语句,因此没有分支,这意味着如果它被编译,即使对于非常大的 n 值,它也会非常快速地执行。这也意味着它可以很容易地并行化以将工作分配到多个处理器上以获得非常大的 n 值。
考虑一个 8x8 网格(这里,输入在技术上是 n = 64,但为了简单起见,我将在下面的公式中使用 n = 8)遵循这个 zigzag 模式,就像这样(使用 0 索引的行和列轴):
[ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7]
[ 0] 1 3 4 10 11 21 22 36
[ 1] 2 5 9 12 20 23 35 37
[ 2] 6 8 13 19 24 34 38 49
[ 3] 7 14 18 25 33 39 48 50
[ 4] 15 17 26 32 40 47 51 58
[ 5] 16 27 31 41 46 52 57 59
[ 6] 28 30 42 45 53 56 60 63
[ 7] 29 43 44 54 55 61 62 64
首先注意从左下角 (0,7) 到右上角 (7,0) 的对角线将网格划分为两个近乎镜像的分量:
[ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7]
[ 0] 1 3 4 10 11 21 22 36
[ 1] 2 5 9 12 20 23 35
[ 2] 6 8 13 19 24 34
[ 3] 7 14 18 25 33
[ 4] 15 17 26 32
[ 5] 16 27 31
[ 6] 28 30
[ 7] 29
和
[ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7]
[ 0] 36
[ 1] 35 37
[ 2] 34 38 49
[ 3] 33 39 48 50
[ 4] 32 40 47 51 58
[ 5] 31 41 46 52 57 59
[ 6] 30 42 45 53 56 60 63
[ 7] 29 43 44 54 55 61 62 64
您可以看到右下角只是左上角的镜像,并从平方加上 1(在本例中为 65)减去。
如果我们可以计算左上部分,那么右下部分可以很容易地计算,只需将平方加 1 ( n * n + 1
) 并减去反坐标 ( value(n - x - 1, n - y - 1)
) 处的值。
例如,考虑右下角的任意一对坐标,例如 (6,3),其值为 48。按照这个公式,可以得到(8 * 8 + 1) - value(8 - 6 - 1, 8 - 3 - 1)
,简化为65 - value(1, 4)
。查看左上角,(1,4) 处的值为 17 65 - 17 == 48
。
但是我们仍然需要计算左上角的部分。请注意,这也可以进一步细分为两个重叠的组件,其中一个组件的数字会随着您向右上方的增加而增加:
[ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7]
[ 0] 3 10 21 36
[ 1] 2 9 20 35
[ 2] 8 19 34
[ 3] 7 18 33
[ 4] 17 32
[ 5] 16 31
[ 6] 30
[ 7] 29
当您向左下方移动时,其中一个组件的数字会增加:
[ 0] [ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7]
[ 0] 1 4 11 22
[ 1] 5 12 23
[ 2] 6 13 24
[ 3] 14 25
[ 4] 15 26
[ 5] 27
[ 6] 28
[ 7]
前者也可以定义为坐标x + y
和为奇数的数,后者定义为坐标和为偶数的数。
现在,这里的关键见解是我们在这里绘制三角形,所以毫不奇怪,三角形数在这里扮演着重要的角色。三角形数序为:1、3、6、10、15、21、28、36、...
如您所见,在奇和分量中,每隔一个以 3 开头的三角形数出现在第一行(3、10、21、36)中,而在偶数分量中,每隔一个以 1 开头的三角形数出现在第一列 (1, 6, 15, 28)。
具体来说,对于给定的坐标对 (x,0) 或 (0,y),对应的三角形数是 triangle(x + 1) 或 triangle(y + 1)。
图形的其余部分可以通过从这些三角形数中向上或向下递增地减去对角线来计算,这相当于减去给定的行数或列数。
请注意,对角线可以正式定义为具有给定坐标和的所有单元格的集合。例如,坐标和为 3 的对角线具有坐标 (0,3)、(1,2)、(2,1) 和 (3,0)。因此,单个数字定义了每个对角线,并且该数字也用于确定起始三角形数。
所以从简单的检查来看,计算奇和分量的公式很简单:
triangle(x + y + 1) - y
计算偶和分量的公式很简单:
triangle(x + y + 1) - x
众所周知的三角形数公式也很简单:
triangle(n) = (n * (n + 1)) / 2
所以,算法是:
- 初始化一个 nxn 数组,其中 n 是输入大小的平方根。
- 计算左上部分的偶数和坐标的索引。这可以通过嵌套两个循环来完成,一个外循环“y 从 0 到 n - 1”和一个内循环“x 从 y % 2 到 y,步长为 2”(通过在当前 y 上限制 x,我们只根据需要查看左上部分,并且从 y % 2 开始并以 2 为步长,我们只能得到偶数和坐标)。可以将循环索引插入上面的公式以获得结果。
value[x, y] = triangle(x + y + 1) - x
.
- 计算左上部分的奇数和坐标的索引。这可以通过类似的循环来完成,除了内部循环是“x 从 y % 2 + 1 到 y 以 2 为步长”,只得到奇数坐标。
value[x, y] = triangle(x + y + 1) - y
.
n * n + 1
如本文第一部分所述,通过简单的减法计算右下部分的索引。这可以通过两个向后计数的嵌套循环来完成(并将内部循环限制在外部循环上以仅获得右下部分)。value[x, y] = (n * n + 1) - value[n - x - 1, n - y - 1]
.
- 将网格展平为一个数组(排列所有行),然后使用网格中生成的数字作为新索引将给定的输入(大小为 n * n)转换为输出。