在给定矩阵中搜索值密度的最佳方法是什么?

时间:2013-07-16 16:49:48

标签: java algorithm search matrix

假设我的n:n矩阵带有short - 只有正值 - 其中的值如下:

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

我正在搜索包含大于0的最大值的m:m矩阵。我的问题是我到目前为止的解决方案与{{1}无法很好地扩展}(也不是n)。

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

在上面的示例中,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;
    }
}

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

注意:这不是一项家庭作业。

编辑:正如其他人在答案中所说,这里的时间复杂度大约是O((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 - n)^ 2)。我正在寻找一些亚二次方的东西,并且在m收敛到无穷大的情况下可以很好地扩展。

4 个答案:

答案 0 :(得分:2)

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

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

  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(i,j)

  3. 请注意,您可以将1和2组合到同一个双循环中,并在计算后立即检查n:n子矩阵M((in,jn)(i,j))的非零值的数量A(I,J)。

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

答案 1 :(得分:1)

你有两件数据不够用:

  1. 无论如何都要保持“最佳”效果。你可以突破一个特定的“矩形”,你正在评估它是否无法击败你当前的“最佳”(所以如果你已经看到一个2x2有3个非零元素而你刚刚击中你的第二个零评估某个矩形就可以打破从它出来

  2. 你也知道矩形的最大可能数= mxm(所以你需要4个)。你发现一个有4个非零的矩形的那一刻你可以打破整个事情 - 这是你可能得到的最好的。

  3. 然而,这些建议中没有一个是算法改进。

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

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

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

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

    4. 如果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是板的大小,所以有(M-m)x(M-m)这样的窗口,最坏的情况涉及遍历所有这些窗口。 所以我不认为你可以在这里避免使用O(n ^ 2)曲线。你可以玩系数

答案 2 :(得分:0)

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

如果这是正确的:

  1. 计算(或猜测)矩阵中出现的最大元素,并将其取消,将此值命名为:POISON
  2. 通过A和POISON的所有元素进行非期望的数字(&lt; = 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_ {i-m}

    删除第一个'm-1'元素

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

  6. 在步骤5的矩阵

  7. 上重复步骤3
  8. 在步骤6的输出中重复步骤4
  9. 找到矩阵中的最大元素...如果它在(i,j)然后在原始矩阵中:从(j,i)开始将有最大的sumbatrix
  10. 基于A的大小(n)的复杂性:O(n * n)

答案 3 :(得分: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(n-i,n-j)。

每个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))

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

也许有兴趣通过额外的启发式方法减少更多的组合:将最多零的交换线放到底部,将最多零的列交换到右边。

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

因此,相同等级的行/列的排序子集可能是一个合理的(子)最优算法。