加速递归行列式算法

时间:2014-11-06 14:44:03

标签: c++ performance recursion

如何加快这种递归功能?当它达到10x10矩阵时,只需要一分钟左右来解决问题。我还包括了事件功能,因此您可以查看何时进行计算。

void determinantsFrame::OnCalculateClick(wxCommandEvent &event)
{
    double elem[MAX][MAX]; double det; string test; bool doIt = true;
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < n; j++)
        {
            test = (numbers[i][j]->GetValue()).mb_str();
            if (test == "")
            {
                doIt = false;
                break;
            }

            for (int k = 0; k < test.length(); k++)
                if (isalpha(test[k]) || test[k] == ' ')
                {
                    doIt = false;
                    break;
                }
                else if (ispunct(test[k]))
                {
                    if (test[k] == '.' && test.length() == 1)
                        doIt = false;
                    else if (test[k] == '.' && test.length() != 1)
                        doIt = true;
                    else if (test[k] != '.')
                        doIt = false;
                }

            if (doIt == false)
                break;
        }
        if (doIt == false)
            break;
    }

    if (doIt)
    {
        for (int i = 0; i < n; i++)
            for (int j = 0; j < n; j++)
                elem[i][j] = static_cast<double>(wxAtof(numbers[i][j]->GetValue()));

        det = determinant(elem, n);
        wxMessageBox(wxString::Format(wxT("The determinant is: %.4lf"),det));
    }
    else
        wxMessageBox(wxT("You may have entered an invalid character. Please try again"));
}

double determinantsFrame::determinant(double matrix[MAX][MAX], int order) // Here's the recursive algorithm
{
    double det = 0; double temp[MAX][MAX]; int row, col;

    if (order == 1)
        return matrix[0][0];
    else if (order == 2)
        return ((matrix[0][0] * matrix[1][1]) - (matrix[0][1] * matrix[1][0]));
    else
    {
        for (int r = 0; r < order; r++)
        {
            col = 0; row = 0;
            for (int i = 1; i < order; i++)
            {
                for (int j = 0; j < order; j++)
                {
                    if (j == r)
                        continue;

                    temp[row][col] = matrix[i][j];
                    col++;

                    if (col == order - 1)
                        col = 0;
                }
                row++;
            }
            det = det + (matrix[0][r] * pow(-1, r) * determinant(temp, order - 1));
        }
        return det;
    }
}

2 个答案:

答案 0 :(得分:0)

可能是branch mispredict问题(see also)。测试

if (col == order - 1)
    col = 0;

就我所见,不需要。

每个循环的测试失败1 / order次,并且对于小order占主导地位,这就是为什么较大的N不受影响的原因。时机仍然很大O(N!^ 3)(afaik)所以不要期待奇迹。

        col = 0; row = 0;
        for (int i = 1; i < order; i++) {
            for (int j = 0; j < order; j++) {
                if (j == r)
                    continue;

                temp[row][col] = matrix[i][j];
                col++;

                //if (col == order - 1)
                //    col = 0;
            }
            col = 0; // no need to test
            row++;
        }

算法在达到L2缓存时会进一步减速,最迟在N = 64时。

矩阵副本也可能无效,对于大order而言,这可能会更有效,代价是低order的低效率。

    for (int r = 0; r < order; r++) {
        row = 0;
        for (int i = 1; i < order; i++) {
            memcpy(temp[row], matrix[i], r*sizeof(double)); // if r==0 will this work?
            memcpy(&temp[row][r], &matrix[i][r+1], (order-r-1)*sizeof(double));
            // amount of copied elements r+(order-r-1)=order-1.

            row++;
        }

使用原始代码进行测试,以获得我的索引正确的决定因素!

答案 1 :(得分:0)

你可以保持相同的算法做得更好,但它至少是O(n!)(可能更糟),所以无论你对它进行多少优化,高阶矩阵都会很慢。注意我在MSVC 2010中进行了基准测试时间,仅用于粗略比较。当您沿着列表向下进行比较时,每个更改都是累积的,并与原始算法进行比较。

  1. Skip Col Check - 正如Surt建议的那样,删除它会使我们的速度提高1%。
  2. 添加3x3案例 - 为3x3矩阵添加另一个显式检查让我们获得最多,55%
  3. 更改权限() - 将pow()来电更改为(r % 2 ? -1.0 : 1.0)让我们多一点,57%
  4. 更改为切换 - 将订单检查更改为交换机让我们多了一点,58%
  5. 添加4x4案例 - 为4x4矩阵添加另一个显式检查得到更多,85%
  6. 不起作用的事情包括:

    1. memcpy - 正如Surt所说,这实际上失去了很大的速度,-100%
    2. 主题 - 创建order主题根本不起作用,-160%
    3. 我希望使用线程可以让我们获得显着的性能提升,但即使进行了所有优化,它也比原来慢。我认为复制所有内存使它不是很平行。

      添加3x3和4x4的情况效果最好,并且是速度超过x6的主要原因。从理论上讲,您可以添加更多显式案例(可能通过创建一个程序来输出所需的代码),以进一步降低速度。当然,在某种程度上,这种方式会破坏使用递归算法的目的。

      要获得更高的性能,您可能需要考虑不同的算法。理论上,您可以通过管理自己的堆栈将递归函数更改为迭代函数,但这是相当大的工作,并且无论如何都不能保证性能提升。