Сранивание просто мэпов и хешмэпов
От: Barbar1an Украина  
Дата: 28.02.17 12:46
Оценка:
если у меня есть два std::map или std::set одинакового типа и мне нужно их сравнить я ж могу сделать простую последоватлеьную итерацию по элементам? т.е. если там бинарное дерево то порядок обхода одинаковых будет всегда одинаков?
а вот с хешмэпами и хешсетами так низя да? потому что хеши будут одинаковые для одинаковых ключей, но порядок в конкретном бакете будет какой угодно да?
Я изъездил эту страну вдоль и поперек, общался с умнейшими людьми и я могу вам ручаться в том, что обработка данных является лишь причудой, мода на которую продержится не более года. (с) Эксперт, авторитет и профессионал из 1957 г.
Re: Сранивание просто мэпов и хешмэпов
От: Кодт Россия  
Дата: 28.02.17 13:23
Оценка: 4 (1)
Здравствуйте, Barbar1an, Вы писали:

B>если у меня есть два std::map или std::set одинакового типа и мне нужно их сравнить я ж могу сделать простую последоватлеьную итерацию по элементам? т.е. если там бинарное дерево то порядок обхода одинаковых будет всегда одинаков?


Да, map и set упорядочены.

B>а вот с хешмэпами и хешсетами так низя да? потому что хеши будут одинаковые для одинаковых ключей, но порядок в конкретном бакете будет какой угодно да?


И с multimap и multiset та же проблема — в пределах одного класса эквивалентности элементы лежат как угодно.
(С другой стороны, — если элементы эквивалентны, то можно считать их неразличимыми, и тупо сравнить количество...)

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

Если коллизий мало, то пару хеш-таблиц можно сравнить (на равенство) путём забега по одной и проверке вхождений в другую. Ну и, разумеется, сверке общего размера таблиц.
Перекуём баги на фичи!
Re[2]: Сранивание просто мэпов и хешмэпов
От: Barbar1an Украина  
Дата: 28.02.17 13:40
Оценка:
Здравствуйте, Кодт, Вы писали:

К>(С другой стороны, — если элементы эквивалентны, то можно считать их неразличимыми, и тупо сравнить количество...)


нэ совсем понял на каких данных это сработает
Я изъездил эту страну вдоль и поперек, общался с умнейшими людьми и я могу вам ручаться в том, что обработка данных является лишь причудой, мода на которую продержится не более года. (с) Эксперт, авторитет и профессионал из 1957 г.
Re: Сранивание просто мэпов и хешмэпов
От: uzhas Ниоткуда  
Дата: 28.02.17 16:26
Оценка:
Здравствуйте, Barbar1an, Вы писали:

B>если у меня есть два std::map или std::set одинакового типа и мне нужно их сравнить я ж могу сделать простую последоватлеьную итерацию по элементам? т.е. если там бинарное дерево то порядок обхода одинаковых будет всегда одинаков?

B>а вот с хешмэпами и хешсетами так низя да? потому что хеши будут одинаковые для одинаковых ключей, но порядок в конкретном бакете будет какой угодно да?

для std контейнеров есть стандартные операторы сравнения на равенство
они вас не устраивают?

для хешированных контейнеров подход такой:
1) если размеры не совпадают, то не равны
2) берется каждый элемент из первого конейнера и ищется во втором. если не нашли, то не равны
3) равны

отдельное приседание для мультихешей (когда ключи могут повторяться), нужно убедиться, что кол-во повторов каждого ключа совпадают
Re[3]: Сранивание просто мэпов и хешмэпов
От: Кодт Россия  
Дата: 28.02.17 16:38
Оценка:
Здравствуйте, Barbar1an, Вы писали:

B>Здравствуйте, Кодт, Вы писали:


К>>(С другой стороны, — если элементы эквивалентны, то можно считать их неразличимыми, и тупо сравнить количество...)


B>нэ совсем понял на каких данных это сработает


Например, на сравнении строк без учёта регистра.

http://ideone.com/fbUpn9
#include <algorithm>
#include <cctype>
#include <cassert>
#include <set>
#include <string>

struct icase_less {
  bool operator()(const std::string& x, const std::string& y) const {
    // для однобайтных кодировок без учёта диграфов типа эсцета - сойдёт
    return std::lexicographical_compare(
      std::begin(x), std::end(x),
      std::begin(y), std::end(y),
      [](char a, char b)->bool { return std::toupper(a) < std::toupper(b); } );
  }
};

template<class LESS>
struct dependent_equal {
 LESS less;
 template<class X, class Y>
 bool operator()(X&& x, Y&& y) const {
   return !less(std::forward<X>(x), std::forward<Y>(y))
       && !less(std::forward<Y>(y), std::forward<X>(x));
 }
};

int main() {
  using icase_multiset = std::multiset<std::string, icase_less>;
  icase_multiset xx = { "alfa", "beta", "Beta", "BETA", "gamma" };
  icase_multiset yy = { "alfa", "Beta", "BETA", "BeTa", "gamma" };
  //                            <--------------------> класс эквивалентности "beta"

  using icase_equal = dependent_equal<icase_less>;
  // выполняем попарное сравнение
  assert( std::equal(std::begin(xx), std::end(xx),
                     std::begin(yy), std::end(yy),
                     icase_equal()));
  // попарное сравнение без "без учёта регистра" не прокатит
  assert(!std::equal(std::begin(xx), std::end(xx),
                     std::begin(yy), std::end(yy)));
}
Перекуём баги на фичи!
Re[2]: Сранивание просто мэпов и хешмэпов
От: Videoman Россия https://hts.tv/
Дата: 01.03.17 10:49
Оценка:
Здравствуйте, Кодт, Вы писали:

К>И с multimap и multiset та же проблема — в пределах одного класса эквивалентности элементы лежат как угодно.

Это уже не так и начиная с С++11 порядок всегда детермининован.

К>(С другой стороны, — если элементы эквивалентны, то можно считать их неразличимыми, и тупо сравнить количество...)

А можете пояснить, это как? Вот у меня multimap с целочисленным ключом и строчкой в качестве значения, и я одному ключу задал в соответствие кучу разных строчек...
std::multimap<uint_t, string_t> m;
m.emplace(0, string_t("dfdf"));
m.emplace(0, string_t("nnnf"));
Re[3]: Сранивание просто мэпов и хешмэпов
От: uzhas Ниоткуда  
Дата: 01.03.17 12:01
Оценка:
Здравствуйте, Videoman, Вы писали:

V>Здравствуйте, Кодт, Вы писали:


К>>И с multimap и multiset та же проблема — в пределах одного класса эквивалентности элементы лежат как угодно.

V>Это уже не так и начиная с С++11 порядок всегда детермининован.

и каким образом он детерминирован? думаю, что все же порядок одниаковых элементов не детерминирован
что нашел интересного:

23.2.5 Unordered associative containers [unord.req]
...
6 An unordered associative container supports unique keys if it may contain at most one element for each
key. Otherwise, it supports equivalent keys. unordered_set and unordered_map support unique keys.
unordered_multiset and unordered_multimap support equivalent keys. In containers that support equivalent
keys, elements with equivalent keys are adjacent to each other in the iteration order of the container.
Thus, although the absolute order of elements in an unordered container is not specified, its elements are
grouped into equivalent-key groups such that all elements of each group have equivalent keys. Mutating
operations on unordered containers shall preserve the relative order of elements within each equivalent-key
group unless otherwise specified.
...
9 The elements of an unordered associative container are organized into buckets. Keys with the same hash
code appear in the same bucket. The number of buckets is automatically increased as elements are added
to an unordered associative container, so that the average number of elements per bucket is kept below
a bound. Rehashing invalidates iterators, changes ordering between elements, and changes which buckets
elements appear in, but does not invalidate pointers or references to elements. For unordered_multiset and
unordered_multimap, rehashing preserves the relative ordering of equivalent elements.

Re[4]: Сранивание просто мэпов и хешмэпов
От: Videoman Россия https://hts.tv/
Дата: 01.03.17 12:32
Оценка: 4 (1)
Здравствуйте, uzhas, Вы писали:

U>

U>23.2.5 Unordered associative containers [unord.req]
U>...
U>6 An unordered associative container supports unique keys if it may contain at most one element for each
U>key. Otherwise, it supports equivalent keys. unordered_set and unordered_map support unique keys.
U>unordered_multiset and unordered_multimap support equivalent keys. In containers that support equivalent
U>keys, elements with equivalent keys are adjacent to each other in the iteration order of the container.
U>Thus, although the absolute order of elements in an unordered container is not specified, its elements are
U>grouped into equivalent-key groups such that all elements of each group have equivalent keys. Mutating
U>operations on unordered containers shall preserve the relative order of elements within each equivalent-key
U>group unless otherwise specified.
U>...
U>9 The elements of an unordered associative container are organized into buckets. Keys with the same hash
U>code appear in the same bucket. The number of buckets is automatically increased as elements are added
U>to an unordered associative container, so that the average number of elements per bucket is kept below
U>a bound. Rehashing invalidates iterators, changes ordering between elements, and changes which buckets
U>elements appear in, but does not invalidate pointers or references to elements. For unordered_multiset and
U>unordered_multimap, rehashing preserves the relative ordering of equivalent elements.


Я имел ввиду что порядок элементов для одного и того же ключа сохраняется и равен порядку добавления элементов (хотя раньше было не так). Цитата, которую вы привели, относится к хешам. Я не спорю, что абсолютный порядок элементов не определен в этом случае.
Re[3]: Сранивание просто мэпов и хешмэпов
От: Кодт Россия  
Дата: 01.03.17 12:51
Оценка:
Здравствуйте, Videoman, Вы писали:

К>>И с multimap и multiset та же проблема — в пределах одного класса эквивалентности элементы лежат как угодно.

V>Это уже не так и начиная с С++11 порядок всегда детермининован.

Детерминирован в порядке добавления, это правда.
Я делал акцент на том, что если порядок добавления разный (например, в один мультисет понапихали из несортированного вектора, а в другой из сортированного или из перевёрнутого), то тщательное сравнение покажет, что два мультисета различаются.
Тщательность — если для проверки равенства мы используем более сильный компаратор, чем для упорядочивания.

К>>(С другой стороны, — если элементы эквивалентны, то можно считать их неразличимыми, и тупо сравнить количество...)

V>А можете пояснить, это как? Вот у меня multimap с целочисленным ключом и строчкой в качестве значения, и я одному ключу задал в соответствие кучу разных строчек...
V>
V>std::multimap<uint_t, string_t> m;
V>m.emplace(0, string_t("dfdf"));
V>m.emplace(0, string_t("nnnf"));
V>


Предикат эквивалентности по паре (ключ,значение) — более сильный, чем по ключу.

Опять же, multimap<Key,Value> по смыслу можно заменить на set<Key, vector<Node>> или set<Key, unordered_multiset<Node>> в зависимости от того, важен нам порядок добавления элементов или нет;
причём Node — это или Value, если мы не различаем эквивалентные ключи, или pair<Key,Value> если в принципе можем различать.

Один пример слабого сравнения я уже привёл — сравнение строк без учёта регистра.
Для целых чисел это может быть, например, сравнение функций от чисел: округление, взятие остатка, абсолютное значение и т.п.

struct comparator {
  bool operator()(int x, int y) const { return abs(x) < abs(y); }
};

std::multimap<int, std::string, comparator> m;
m.emplace( 1, "hello");
m.emplace( 1, "world");
m.emplace(-1, "antimatter");
m.emplace( 1, "matter");

Все три значения заданы в соответствие ключу ±1.
Естественно, взяв [lower_bound(1), upper_bound(1)) и пробежав по элементам, мы обнаружим, что их ключи чем-то неуловимо отличаются
Вопрос лишь в том, а насколько нам это важно.
Перекуём баги на фичи!
Re[4]: Сранивание просто мэпов и хешмэпов
От: Кодт Россия  
Дата: 01.03.17 13:09
Оценка: 13 (3)
Здравствуйте, uzhas, Вы писали:

К>>>И с multimap и multiset та же проблема — в пределах одного класса эквивалентности элементы лежат как угодно.

V>>Это уже не так и начиная с С++11 порядок всегда детермининован.

U>и каким образом он детерминирован? думаю, что все же порядок одниаковых элементов не детерминирован

U>что нашел интересного:

23.2.4, таблица 102 — Associative container requirements

В черновике C++11 про a_eq.insert есть оговорка, что эквивалентный ключ добавляется в конец диапазона либо как можно ближе слева от итератора подсказки. А про a_eq.emplace такой оговорки нет. И более того, прямо сказано, что реализации могут игнорировать подсказку, то есть, добавят куда бог пошлёт.

а уже в черновике C++14 этот косяк поправили, и emplace ведёт себя так же, как и insert.
Перекуём баги на фичи!
Отредактировано 01.03.2017 13:10 Кодт . Предыдущая версия .
Re[5]: Сранивание просто мэпов и хешмэпов
От: uzhas Ниоткуда  
Дата: 01.03.17 14:24
Оценка:
Здравствуйте, Кодт, Вы писали:

К>23.2.4, таблица 102 — Associative container requirements


К>В черновике C++11 про a_eq.insert есть оговорка, что эквивалентный ключ добавляется в конец диапазона либо как можно ближе слева от итератора подсказки. А про a_eq.emplace такой оговорки нет. И более того, прямо сказано, что реализации могут игнорировать подсказку, то есть, добавят куда бог пошлёт.


К>а уже в черновике C++14 этот косяк поправили, и emplace ведёт себя так же, как и insert.


действительно (из N4604, C++14)

23.2.4 Associative containers [associative.reqmts]
...
Table 86 — Associative container requirements (in addition to container)
...
a_eq.emplace(args)
Effects: Inserts a value_type
object t constructed with
std::forward<Args>(args)...
and returns the iterator
pointing to the newly inserted
element. If a range containing
elements equivalent to t exists
in a_eq, t is inserted at the
end of that range.

 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.