【问题标题】:Fastest algorithm for finding two array elements that sum to x [duplicate]查找总和为x的两个数组元素的最快算法[重复]
【发布时间】:2021-02-14 23:18:53
【问题描述】:

演练公告:https://cses.fi/problemset/task/1640/

我想了解为什么一种算法比另一种算法快得多(大约 25 倍),因为我认为它们具有相同的复杂度 O(n log n),但最快的算法原则上应该在我的列表中多循环 1 次,所以我真的会认为它会慢一些。

慢:

#include <bits/stdc++.h>

using namespace std;

 
int main() {
    int n, x; cin >> n >> x;
    vector<int> a(n);
    map<int, int> vals;
    for (int i = 0; i < n; i++) {
        cin >> a[i];
    }
    for (int i = 0; i < n; i++) {
        if(vals.count(x - a[i])){
            cout << i + 1 << " " << vals[x - a[i]] << "\n";
            return 0;
        }
        vals[a[i]] = i + 1;
    }
    cout << "IMPOSSIBLE" << '\n';
}

快速:

#include <bits/stdc++.h>
 
using namespace std;
 
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n, x;
    cin >> n >> x;
    pair<int, int> arr[n];
    for (int i = 0; i < n; i++) {
        cin >>arr[i].first;
        arr[i].second = i + 1;
    }
    sort(arr, arr + n);
    int i = 0, j = n - 1;
    while (i < j) {
        int sum = arr[i].first + arr[j].first;
        if (sum == x) {
            cout << arr[i].second << ' ' << arr[j].second;
            return 0;
        } else if (sum < x) {
            i++;
        } else {
            j--;
        }
    }
    cout << "IMPOSSIBLE";
    return 0;
}

【问题讨论】:

  • 为什么只有第二个有ios::sync_with_stdio(false); cin.tie(0);?您是否认为差异可能是由于这个原因?
  • 请提供minimal reproducible example 输入和输出以及代码应该做什么
  • 我做了测试,差异要小得多,但第二个仍然快两倍。我尝试使用 unordered_map 并且我对一次测试有时间限制。我真的不明白
  • 我放了一个链接,所有的东西都写好了,代码是可测试的,以了解性能,这不是一个好习惯吗?
  • 在第一个代码中,您正在迭代地执行排序,这相对于第二个代码来说是次优的,其中排序是一次性执行的。第二部分只有 O(n)。

标签: c++ algorithm optimization


【解决方案1】:

请注意,大 O 复杂性故意忽略了常量因素以及特定于架构的细节,例如分支预测和缓存。第一个工作是N + N * (log_2(N) + log_2(N))(构造向量+每个元素两次映射访问),而第二个工作是N + N * (log_2(N) + 1)(构造向量,排序,然后遍历一次)。

如果您随后查看低级访问模式,您会看到(假设是二叉搜索树)每个映射访问最坏的情况下需要log_2(2*10^5)=18 内存访问,其中一些将是缓存未命中,因此它们将有等待(相对较慢的)主内存。

将此与第二个进行比较,您从左到右(缓存的最佳情况)和从右到左(大致相同)线性获取数据。假设 4 字节 ints 和 64 字节缓存行,这意味着每 16 次循环迭代只需要两次主内存访问,其他内存访问可以从 L1 缓存中满足。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-08-03
    • 2020-05-13
    • 1970-01-01
    • 2013-11-15
    • 1970-01-01
    • 1970-01-01
    • 2014-04-19
    • 1970-01-01
    相关资源
    最近更新 更多