почему существует аномалия в асимптотическом времени и фактическом времени для каждого решения?

#c #c 11 #time #stl #time-complexity

#c #c 11 #время #stl #временная сложность

Вопрос:

Я решал эту проблему: — Вам даны строки J, представляющие типы камней, которые являются драгоценными камнями, и S, представляющие камни, которые у вас есть. Каждый символ в S — это тип камня, который у вас есть. Вы хотите знать, сколько камней у вас есть, также являются драгоценными камнями.

Буквы в J гарантированно различаются, а все символы в J и S являются буквами. Буквы чувствительны к регистру, поэтому «a» считается камнем, отличным от «A».

Пример 1:

Ввод: J = «aA», S = «aAAbbbb» Вывод: 3 Пример 2:

Ввод: J = «z», S = «ZZ» Вывод: 0

Моя логика для решения 1.
Хэшируйте камни (unordered_map), чтобы у нас была частота каждого типа, и нам нужно было найти только разные камни в заданных драгоценностях один раз. Функция поиска принимает o (n) для каждого камня n, следовательно, временная сложность равна O (n ^ 2).

 int numJewelsInStones(string j, string s) {
        
        unordered_map<char, int>stones;
        for(char s1:s)
              stones[s1];
       
        auto it = stones.begin(); 
        int count = 0;
        for(it = stones.begin(); it != stones.end();   it)
        {
            char s1 = it->first;
            if(j.find(s1) != string::npos)
                count  = it->second;
            
        }
        return count;
  

Итак, я подумал, что o (n ^ 2) слишком много, и решил попытаться оптимизировать это.
Поэтому я также хэшировал драгоценности, поместив их в unordered_set . Таким образом, все дубликаты удаляются, и требуется o (1) времени, чтобы найти в нем камень.
Итак, для каждого камня требуется o (1) времени, и, следовательно, временная сложность становится o (n).

 int numJewelsInStones(string j, string s) {
        
        unordered_map<char, int>stones;
        for(char s1:s)
              stones[s1];
        unordered_set<char>uset(j.begin(), j.end()); 
        auto it = stones.begin(); 
        int count = 0;
        for(it = stones.begin(); it != stones.end();   it)
        {
            char s1 = it->first;
            
            if(uset.find(s1) != uset.end())
                count  = it->second;
                
        }
        return count;
  

Проблема возникает здесь — когда я использовал функцию clock от time.h для измерения времени
выполнения решения 1 я получил 0,000126 единиц времени
, для решения 2 я получил 0,000145 единиц времени
, что не имеет смысла, когда первое равно o (n ^ 2), а второе — o (n).

кстати, это мой код для получения времени-

 int main()
{
    clock_t tStart = clock();
    Solution ob;
    string j = "aA", s = "aAAbbbb";
    cout << ob.numJewelsInStones(j, s) << endl;
    cout << (double)(clock() - tStart)/CLOCKS_PER_SEC;
    cout << endl;
    return 0;
}
  

Кто-нибудь может объяснить мне эту аномалию?

Комментарии:

1. Вы компилируете с включенной оптимизацией?

2. Размер и время кажутся слишком малыми, чтобы сделать вывод

3. @cigien нет, я не

4. Тогда сравнение результатов в принципе бессмысленно. Компиляция с оптимизацией и прилично большими входными данными.

Ответ №1:

TL; DR Протестируйте свой алгоритм с достаточно большим n

При малых значениях n производительность кэша является более доминирующей. unordered_set реализовано в виде хеш-таблицы в C , следовательно, поиск включает в себя обход указателей (здесь, я полагаю, вы знаете, как реализованы хеш-карты). Перемещение указателей означает чтение из разных частей памяти. Это влияет на производительность кэша, поскольку следующий объект, который хочет проверить хэш-карта, скорее всего, отсутствует в кэше и должен быть извлечен с более высокого уровня памяти.
Массивы, с другой стороны, отображают локальность ссылки. Это позволяет эффективно использовать кеш, следовательно, вы достигаете лучшей производительности с массивами на небольших примерах.

Временная сложность алгоритма используется для определения того, насколько хорошо ваш алгоритм масштабируется в зависимости от размера. O(n^2) Алгоритм может работать лучше, чем O(n) алгоритм на меньших входных данных (как это происходит в этом примере), но на достаточно больших входных данных алгоритм с меньшей временной сложностью должен работать лучше.

Комментарии:

1. о, я понимаю. Итак, в основном требуется время O (1) для поиска элемента, потому что каждый элемент хэшируется хэш-функцией, но для фактического извлечения его из памяти он действует сравнительно медленнее по сравнению с массивами (массивы имеют локальность ссылки).

2. ДА. Кроме того, если вы хотите повысить производительность кэша, возможно, вы можете реализовать свой хэш-набор в схеме с открытой адресацией . Это даст вам лучшее из обоих миров. Производительность будет зависеть от размера ввода и размера вашего кэша (и относительной скорости доступа)