异或魔术矩形

问题描述

问题:

在尺寸为 m x n 的魔术矩形中,每个条目都是行和列的异或,索引为零。 示例 (8 x 5):

0 1 2 3 4 5 6 7
1 0 3 2 5 4 7 6
2 3 0 1 6 7 4 5
3 2 1 0 7 6 5 4
4 5 6 7 0 1 2 3

我需要找到每个条目的总和,但是,当输入范围在数百万之内时,暴力破解将不起作用。

我目前的工作:

我发现,对于 m 或 n 是 2 的幂的 amxn 矩阵,您可以将总和计算为 sum_range(0,m-1) * n,其中总和范围实际上只是将第一个和第二个输入。

当 m 和 n 都不是 2 的幂时,事情就会变得有趣。

您可以将 m x n 矩形拆分为由 2 的幂的子 m x n 个矩形组成的矩形,如下所示:(15 x 15)

0,1,2,3,4,5,6,7 | 8,9,10,11 | 12,13 | 14 | 
1,7,6 | 9,8,11,10 | 13,12 | 15 | 
2,5 | 10,9 | 14,15 | 12 | 
3,4 | 11,8 | 15,14 | 13 | 
4,3 | 12,13,14,15 | 8,9 | 10 | 
5,2 | 13,12,15,14 | 9,8 | 11 | 
6,1 | 14,13 | 10,11 | 8 | 
7,0 | 15,12 | 11,10 | 9 | 
----------------------------------------------------
8,15 | 0,3 | 4,5 | 6 | 
9,14 | 1,2 | 5,4 | 7 | 
10,13 | 2,1 | 6,7 | 4 | 
11,12 | 3,0 | 7,6 | 5 | 
----------------------------------------------------
12,11 | 4,7 | 0,1 | 2 | 
13,10 | 5,6 | 1,0 | 3 | 
----------------------------------------------------
14,9 | 6,5 | 2,3 | 0 | 
----------------------------------------------------

然后,使用我上面描述的公式,您可以得到沿对角线的每个正方形的总和(我的解释没有意义,所以这里是一张图片): not enough reputation for embeds ._.

这就是我

我不明白如何在没有蛮力的情况下获得其他部分的总和,就 m 和 n 而言。 记住,m 和 n 将是随机整数,它们可能非常非常大

我看到了一些模式,例如魔术矩形如何具有某种对称性,并且边是数字 0 - (m-1) 的顺序,但是,我无法想出将其转换为代码的逻辑。

指向正确方向的指针将不胜感激。然而,不是在寻找代码,因为这是一个代码战争问题,这是作弊

参考问题:https://www.codewars.com/kata/59568be9cc15b57637000054/train/java

解决方法

我们可以将矩阵划分为四个子指标并反复利用 sum_range(start,m-1) * n。为简单起见,我将使用递归进行解释,您可以使用队列轻松地将其转换为非递归解决方案。

让我们考虑更小的情况,一个 7x7 的矩阵。其中,m=7(列),n=7(行)。总和 168。

[0,1,2,3,4,5,6]
[1,7]
[2,6,7,4]
[3,5]
[4,2]
[5,3]
[6,0]

我们可以将这个矩阵划分为以下 4 个矩阵:

[0,3] [4,6] [4,7] [0,2]
[1,2] [5,7] [5,6] [1,3]
[2,1] [6,4] [6,5] [2,0]
[3,0] [7,5] 

这里第一个矩阵的维度等于 2 的最大可能幂,即 4。其余的只是父矩阵的剩余 3 部分。
我们可以使用公式来获得第一个矩阵的总和:rowSum*rows -> (m*(m-1)/2)*n -> (4*3/2)*4 -> 24

接下来我们考虑第二个子矩阵。并使用相同的策略分成 4 个部分:

[4,5] [6] [6,7] [4]
[5,4] [7] [7,6] [5]

这里,第一个子矩阵可以使用公式 (rowSum - startSum)*rows->((m*(m-1)/2) - (start*(start-1)/2)) * n -> (6*5/2 - 4*3/2)*2 -> 18 计算。在这里,start = 4 xor 0
根据上面的讨论,我们可以创建这样的递归函数:

int result = calculate(0,m,n);
System.out.println("Total: " + result);

int calculate(int m0,int n0,int m,int n) {
     System.out.print("calculating: (" +m0+","+n0 + ") (" + m +","+n+") ");

     if( (m0,n0) (m,n) is single dimensional array then)
            //use for loops do xor and return sum.

     //get maximum possible power of 2 that can be used to derive first sub matrix
     int pow = (int)(Math.log(m-m0) / Math.log(2));
     pow = (int)Math.pow(2,pow);
      
     //sum = sum(first sub matrix (m0,pow)(n0,pow))

     //sum += calculate(dimensions of second sub matrix)
     //sum += calculate(dimensions of third sub matrix)
     //sum += calculate(dimensions of fourth sub matrix)
    
     return sum;
}

int sum(int start,int n) {
    m=m+start;
    int sum = (m*(m-1)/2);
    sum -= (start*(start-1)/2);
    sum *=n;
    System.out.println("sum="+sum);
    return sum;
}

上面的代码应该产生如下输出:

calculating: (0,0) (7,7) sum=24
calculating: (4,4) sum=18
calculating: (6,2) sum=13
calculating: (4,2) (6,4) sum=26
calculating: (6,2) (7,4) sum=9
calculating: (0,4) (4,7) sum=66
calculating: (4,4) (7,7) sum=2
calculating: (6,6) sum=5
calculating: (4,6) (6,7) sum=5
calculating: (6,6) (7,7) sum=0
Total: 168

要保持最少的递归,请确保 m>n。如果不是,则交换值。
注:应作者要求,不提供工作代码。