4

假设我有一个n:n矩阵,其中short只有正值,如下所示:

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

我正在这个矩阵中搜索一个m:m矩阵,其中包含大多数大于 0 的值。我的问题是我到目前为止的解决方案不能很好地扩展n(也不m)。

事实上,n:n矩阵代表产品的价格,轴代表给定(任意)日期的天数。因此,您可以在给定的时间间隔内搜索价格。该m:m矩阵实际上是一个 7 x 7 矩阵,其中包含价格的子集(如视图)。我正在寻找n:n我填写的价格最多的矩阵部分。

在上面的例子中,m:m矩阵是

7 1
6 2

2在哪里m

以下是我迄今为止编写的原型的相关部分:

private static class ResultMatrixData {
    private byte fillCount;
    private short distanceFromToday;

    public ResultMatrixData() {
        fillCount = 0;
        distanceFromToday = Short.MAX_VALUE;
    }

    public ResultMatrixData(short[][] pricesMatrix, short iArg, short jArg) {
        byte fillCount = 0;
        for (int i = iArg; i < iArg + 7; i++) {
            for (int j = jArg; j < jArg + 7; j++) {
                if (pricesMatrix[i][j] > 0) {
                    fillCount++;
                }
            }
        }
        this.fillCount = fillCount;
        distanceFromToday = iArg > jArg ? iArg : jArg;
    }
}

private ResultMatrixData calculateSingleResult(short[][] pricesMatrix) {
    ResultMatrixData bestSoFar = new ResultMatrixData();
    ResultMatrixDataComparator comparator = new ResultMatrixDataComparator();
    for (short i = 0; i < NUMBER_OF_DAYS - 6; i++) {
        for (short j = 0; j < NUMBER_OF_DAYS - 6; j++) {
            ResultMatrixData current = new ResultMatrixData(pricesMatrix, i, j);
            if (comparator.compare(current, bestSoFar) >= ResultMatrixDataComparator.GREATER_THAN) {
                bestSoFar = current;
            }
        }
    }
    return bestSoFar;
}

private static class ResultMatrixDataComparator implements Comparator<ResultMatrixData> {
    private static final int LESS_THAN = -1;
    private static final int EQUAL = 0;
    private static final int GREATER_THAN = 1;

    @Override
    public int compare(ResultMatrixData first, ResultMatrixData second) {
        if (first.fillCount > second.fillCount) {
            return GREATER_THAN;
        } else if (first.fillCount < second.fillCount) {
            return LESS_THAN;
        } else {
            if (first.distanceFromToday < second.distanceFromToday) {
                return GREATER_THAN;
            } else if (first.distanceFromToday > second.distanceFromToday) {
                return LESS_THAN;
            }
        }
        return EQUAL;
    }
}

我的问题是运行时间似乎是二次或指数的(我没有进行准确的渐近分析):

n (days)  | running time in ms 
1 *  365  | 48
2 *  365  | 123
3 *  365  | 278
4 *  365  | 482
5 *  365  | 733
6 *  365  | 1069
7 *  365  | 1438
8 *  365  | 1890
9 *  365  | 2383
10 * 365  | 2926
11 * 365  | 3646
12 * 365  | 4208
13 * 365  | 5009

你有什么建议我该如何优化这个算法?

注意:这不是家庭作业。

编辑:正如其他人在他们的回答中所说,这里的时间复杂度大约是 O(( n- m)^2)。我正在寻找亚二次型的东西,它在n收敛到无穷大的同时可以很好地扩展。

4

4 回答 4

2

从理论上讲,如果在计算复杂度时谈到最坏的情况,你不能做得比 O((mn)^2) 更好,如果 n 不大于 C*m,则实际上是 O(n^2),当 C是一些正常数。原因是,即使您知道除了一次单元格之外唯一可能的矩阵全为零,在最坏的情况下,如果不遍历 m:m 矩阵的所有单元格,除了 n^2 个单元格,您也无法回答这个问题。

我建议使用以下算法,它甚至可以提供比要求更多的选项。

  1. 创建一个与原始矩阵 M 大小相同的矩阵 A,其中单元格 (i,j) 将在 (0,0) 之间的矩形处保存非零的数量。您可以简单地通过从右到左逐行填充它,然后计算: A(i,j)=A(i-1,j)+ (A(i,j-1)-A(i -1,j-1)) + (M(i,j) != 0 )。或者代替 (A(i,j-1)-A(i-1,j-1)) 你现在可以有你在 i-the 行中遇到的数量的计数器。在这个伪代码中,A(0,j) 或 A(i,0) 表示 0,假设索引从 1 开始。

  2. 现在您可以在 O(1) 中查询 M 的每个三角形子矩阵 M((i,j)(l,k)) 中的非零数,包括 n:n 矩阵(如前所述,您有 (mn)^2 个: num_of_non-zeros in M((i,j)(l,k)) = A(l,k)-A(l,j)-A(i,k)+A(我,j)

请注意,您可以将 1 和 2 组合到同一个双循环中,并在计算 A(i ,j)。

所以你得到了一个简单的二次算法,可以很容易地扩展到其他类似的应用程序。

于 2013-07-23T21:50:58.213 回答
1

有 2 条数据您没有使用足够的数据:

  1. 无论如何,您都在保持“最佳”结果。您可以突破您正在评估的特定“矩形”,如果它无法击败您当前的“最佳”(因此,如果您已经看到具有 3 个非零元素的 2x2,并且您只需点击第二个零评估某个矩形,您就可以打破摆脱它

  2. 您还知道矩形的最大可能计数 = mxm(因此请注意 4)。当你找到一个有 4 个非零的矩形时,你就可以破坏整个东西——这是你能得到的最好的。

这些建议都不是算法改进。

您可以尝试“扫描窗口”方法: 1.从 0,0 开始,像现在一样使用全扫描计算“左上”mxm 窗口的分数。

  1. 向右扫描一列 - 获取您的分数,减去最左侧(最低索引)列的分数,然后将相邻列的分数添加到右侧。

  2. 继续执行第 2 步,直到到达行尾,然后向下扫描一行(减去第一行的分数,添加下一行的分数

  3. 前进“左”(朝向较低的列索引)直到到达边缘,此时向下扫描一个索引并再次开始向右移动(步骤 2)。

如果 m 很大,这将为您节省一些重新计算。只是为了演示该算法的迭代顺序,这是从 7x7“板”中扫描一个 3x3 矩形:

    going right -->        hitting the edge, moving     hit edge, go down     etc
                          down one row, heading left        head right
xxx.... .xxx... ..xxx..     ....xxx ....... .......     ....... .......     .......
xxx.... .xxx... ..xxx..     ....xxx ....xxx ...xxx.     xxx.... .......     .......
xxx.... .xxx... ..xxx..     ....xxx ....xxx ...xxx.     xxx.... xxx....     ....xxx
....... ....... ....... ... ....... ....xxx ...xxx. ... xxx.... xxx.... ... ....xxx
....... ....... .......     ....... ....... .......     ....... xxx....     ....xxx
....... ....... .......     ....... ....... .......     ....... .......     .......
....... ....... .......     ....... ....... .......     ....... .......     .......

这样,每次我移动到下一个矩形(边缘)时,我只“计算”6 个元素而不是 9 个元素。您的 m 越大,收益越大。

并行化这个

您可以将每个“行”作为单独的任务进行扫描(跨多个内核甚至机器)。

      Task 1        |        Task 2       |        Task N
xxx....     ....xxx | .......     ....... | .......     .......
xxx....     ....xxx | xxx....     ....xxx | .......     .......
xxx.... --> ....xxx | xxx.... --> ....xxx | ....... --> .......
.......     ....... | xxx....     ....xxx | xxx....     ....xxx
.......     ....... | .......     ....... | xxx....     ....xxx
.......     ....... | .......     ....... | xxx....     ....xxx

那么你只需要从每个任务返回的结果中选择最好的结果(每个任务返回其行的最佳结果)

理论界限: 因为 m 是“窗口”的大小,M 是板的大小,所以有 (Mm)x(Mm) 个这样的窗口,最坏的情况是遍历所有窗口。所以我认为你不能在这里避免 O(n^2) 曲线。你可以玩弄系数

于 2013-07-16T17:09:59.540 回答
0

我完全不确定你能用这种方法走多远,但我们开始吧:
我会尝试交换行和列,以便将零从左上角移开。
因此,生成的矩阵 m:m 将在左上角找到。

我们需要评估交换行/列是否有趣。
为此,我们基于这个权重矩阵构建了一个成本函数:

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

换句话说,第 i 行第 j 列的权重(从 0 开始)是 min(ni,nj)。

每个 0 找到的成本对应的权重,我们希望最小化总成本。
如果交换能降低总成本,它就会很有趣。

为了降低评估成本,我们可以使用一种稀疏矩阵结构:

  • 每行映射零位置,即每个 rowIndex 的 (columnIndex) 集合
  • 每列映射零位置,即每个 columnIndex 的 (rowIndex) 集合

我们现在有一个排序行和列的问题。
次优方法包括分别解决子问题:

  • 交换行,
  • 交换列,
  • 迭代直到成本不发生变化。

如果满足以下条件,则交换两行 i 和 k 是有优势的:

weigth.atRow(i).sumOfIndices(zerosPerRow.at(i)) + weigth.atRow(k).sumOfIndices(zerosPerRow.at(k)) >
weigth.atRow(i).sumOfIndices(zerosPerRow.at(k)) + weigth.atRow(k).sumOfIndices(zerosPerRow.at(i))

请注意,这不是完整的顺序关系,因此并非所有排序算法都会成功。

也许有兴趣通过额外的启发式来减少更多的组合:将具有最多零的行交换到底部,将具有最多零的列交换到右侧。

显然,具有满秩的行/列一旦向上/向左移动就不需要排序。

所以也许排序相同等级的行/列的子集是一种合理的(次)最优算法。

于 2013-07-16T22:15:37.153 回答
0

从问题中我假设如下:在 A 中找到最大 (nxn) 子矩阵 B_{i,j},其中包含最少数量的零

如果这是正确的:

  1. 计算(或猜测)矩阵中出现的最大元素,并将其取反,将此值命名为:POISON
  2. 遍历所有元素 A 和 POISON 不需要的数字 (<=0)
  3. 计算每一行的完整前缀(http://en.wikipedia.org/wiki/Prefix_sum

    y_0=x_0;
    for(int i=1;i<n;i++){
    y_i=y_{i-1}+x_i
    

    }

  4. 在所有行的前缀上应用以下内容:

    x_i = x_i - x_{im}

    删除第一个“m-1”元素

  5. 行前缀正在形成一个矩阵,翻译它(你可以根据你的实现来解决这个问题,但如果你这样做,实现会更复杂)

  6. 在步骤 5 的矩阵上重复步骤 3

  7. 在步骤 6 的输出上重复步骤 4
  8. 找到矩阵中的最大元素...如果它位于 (i,j) 则在原始矩阵中:从 (j,i) 开始会有最大 sumbatrix

基于A的大小(n)的复杂度:O(n * n)

于 2013-07-16T20:04:50.827 回答