সি ++ আনর্ডারড_ম্যাপটি কাস্টম শ্রেণীর ধরণের কী হিসাবে ব্যবহার করে


285

আমি unordered_mapনিম্নলিখিতগুলির মতো, এর জন্য কী হিসাবে একটি কাস্টম ক্লাস ব্যবহার করার চেষ্টা করছি :

#include <iostream>
#include <algorithm>
#include <unordered_map>

using namespace std;

class node;
class Solution;

class Node {
public:
    int a;
    int b; 
    int c;
    Node(){}
    Node(vector<int> v) {
        sort(v.begin(), v.end());
        a = v[0];       
        b = v[1];       
        c = v[2];       
    }

    bool operator==(Node i) {
        if ( i.a==this->a && i.b==this->b &&i.c==this->c ) {
            return true;
        } else {
            return false;
        }
    }
};

int main() {
    unordered_map<Node, int> m;    

    vector<int> v;
    v.push_back(3);
    v.push_back(8);
    v.push_back(9);
    Node n(v);

    m[n] = 0;

    return 0;
}

তবে, g ++ আমাকে নিম্নলিখিত ত্রুটিটি দেয়:

In file included from /usr/include/c++/4.6/string:50:0,
                 from /usr/include/c++/4.6/bits/locale_classes.h:42,
                 from /usr/include/c++/4.6/bits/ios_base.h:43,
                 from /usr/include/c++/4.6/ios:43,
                 from /usr/include/c++/4.6/ostream:40,
                 from /usr/include/c++/4.6/iostream:40,
                 from 3sum.cpp:4:
/usr/include/c++/4.6/bits/stl_function.h: In member function bool std::equal_to<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp = Node]’:
/usr/include/c++/4.6/bits/hashtable_policy.h:768:48:   instantiated from bool std::__detail::_Hash_code_base<_Key, _Value, _ExtractKey, _Equal, _H1, _H2, std::__detail::_Default_ranged_hash, false>::_M_compare(const _Key&, std::__detail::_Hash_code_base<_Key, _Value, _ExtractKey, _Equal, _H1, _H2, std::__detail::_Default_ranged_hash, false>::_Hash_code_type, std::__detail::_Hash_node<_Value, false>*) const [with _Key = Node, _Value = std::pair<const Node, int>, _ExtractKey = std::_Select1st<std::pair<const Node, int> >, _Equal = std::equal_to<Node>, _H1 = std::hash<Node>, _H2 = std::__detail::_Mod_range_hashing, std::__detail::_Hash_code_base<_Key, _Value, _ExtractKey, _Equal, _H1, _H2, std::__detail::_Default_ranged_hash, false>::_Hash_code_type = long unsigned int]’
/usr/include/c++/4.6/bits/hashtable.h:897:2:   instantiated from std::_Hashtable<_Key, _Value, _Allocator, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, __cache_hash_code, __constant_iterators, __unique_keys>::_Node* std::_Hashtable<_Key, _Value, _Allocator, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, __cache_hash_code, __constant_iterators, __unique_keys>::_M_find_node(std::_Hashtable<_Key, _Value, _Allocator, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, __cache_hash_code, __constant_iterators, __unique_keys>::_Node*, const key_type&, typename std::_Hashtable<_Key, _Value, _Allocator, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, __cache_hash_code, __constant_iterators, __unique_keys>::_Hash_code_type) const [with _Key = Node, _Value = std::pair<const Node, int>, _Allocator = std::allocator<std::pair<const Node, int> >, _ExtractKey = std::_Select1st<std::pair<const Node, int> >, _Equal = std::equal_to<Node>, _H1 = std::hash<Node>, _H2 = std::__detail::_Mod_range_hashing, _Hash = std::__detail::_Default_ranged_hash, _RehashPolicy = std::__detail::_Prime_rehash_policy, bool __cache_hash_code = false, bool __constant_iterators = false, bool __unique_keys = true, std::_Hashtable<_Key, _Value, _Allocator, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, __cache_hash_code, __constant_iterators, __unique_keys>::_Node = std::__detail::_Hash_node<std::pair<const Node, int>, false>, std::_Hashtable<_Key, _Value, _Allocator, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, __cache_hash_code, __constant_iterators, __unique_keys>::key_type = Node, typename std::_Hashtable<_Key, _Value, _Allocator, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, __cache_hash_code, __constant_iterators, __unique_keys>::_Hash_code_type = long unsigned int]’
/usr/include/c++/4.6/bits/hashtable_policy.h:546:53:   instantiated from std::__detail::_Map_base<_Key, _Pair, std::_Select1st<_Pair>, true, _Hashtable>::mapped_type& std::__detail::_Map_base<_Key, _Pair, std::_Select1st<_Pair>, true, _Hashtable>::operator[](const _Key&) [with _Key = Node, _Pair = std::pair<const Node, int>, _Hashtable = std::_Hashtable<Node, std::pair<const Node, int>, std::allocator<std::pair<const Node, int> >, std::_Select1st<std::pair<const Node, int> >, std::equal_to<Node>, std::hash<Node>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, false, false, true>, std::__detail::_Map_base<_Key, _Pair, std::_Select1st<_Pair>, true, _Hashtable>::mapped_type = int]’
3sum.cpp:149:5:   instantiated from here
/usr/include/c++/4.6/bits/stl_function.h:209:23: error: passing const Node as this argument of bool Node::operator==(Node)’ discards qualifiers [-fpermissive]
make: *** [threeSum] Error 1

আমার অনুমান, আমার সি ++ কীভাবে হ্যাশ ক্লাস করবেন তা বলার দরকার আছে Nodeতবে আমি কীভাবে এটি করব তা সম্পর্কে নিশ্চিত নই। আমি এই কাজগুলি কীভাবে সম্পাদন করতে পারি?


2
তৃতীয় টেমপ্লেট যুক্তি হ্যাশ ফাংশন আপনাকে সরবরাহ প্রয়োজন।
খ্রিস্টিয়াকক

3
সিপ্রেফারেন্সের কীভাবে এটি করা যায় তার একটি সহজ এবং ব্যবহারিক উদাহরণ রয়েছে: en.cppreferences.com/w/cpp/container/unordered_map/unordered_map
jogojapan

উত্তর:


486

std::unordered_mapব্যবহারকারীর সংজ্ঞায়িত কী-প্রকারের সাথে (বা অন্য কোনও অর্ডারর্ড অ্যাসোসিয়েটিভ পাত্রে) ব্যবহার করতে সক্ষম হতে আপনাকে দুটি জিনিস সংজ্ঞায়িত করতে হবে:

  1. একটি হ্যাশ ফাংশন ; এটি অবশ্যই এমন একটি শ্রেণি হতে হবে operator()যা কী-টাইপের একটি অবজেক্টকে দেওয়া হ্যাশ মানকে ওভাররাইড করে এবং গণনা করে। এটি করার একটি বিশেষত সোজা-সামনের উপায় হ'ল std::hashআপনার কী-প্রকারের জন্য টেমপ্লেটকে বিশেষীকরণ করা ।

  2. সাম্য জন্য একটি তুলনা ফাংশন ; এটি প্রয়োজনীয় কারণ হ্যাশ ফাংশনটি সর্বদা প্রতিটি স্বতন্ত্র কী (যেমন, এটি সংঘর্ষের সাথে মোকাবিলা করতে সক্ষম হওয়া প্রয়োজন) এর জন্য একটি অনন্য হ্যাশ মান সরবরাহ করবে এই সত্যের উপর নির্ভর করতে পারে না, সুতরাং এটি দুটি প্রদত্ত কীগুলির সাথে তুলনা করার একটি উপায় প্রয়োজন একটি সঠিক ম্যাচের জন্য। আপনি এটিকে নিজের শ্রেণীর (যেমন আপনি ইতিমধ্যে করেছিলেন) ওভারলোড করে ওভাররাইড করে এমন শ্রেণীরূপে operator(), বা তার বিশেষজ্ঞ হিসাবে std::equal, বা - সবচেয়ে সহজ - হিসাবে প্রয়োগ করতে পারেন operator==()

হ্যাশ ফাংশনটির সাথে অসুবিধাটি হ'ল যদি আপনার কী টাইপটি বেশ কয়েকটি সদস্য নিয়ে থাকে তবে আপনার কাছে সাধারণত হ্যাশ ফাংশনটি পৃথক সদস্যদের জন্য হ্যাশ মানগুলি গণনা করতে হবে এবং তারপরে কোনওভাবে এটিকে পুরো অবজেক্টের জন্য একটি হ্যাশ মানের সাথে সংযুক্ত করে। ভাল পারফরম্যান্সের জন্য (অর্থাত্ কয়েকটি সংঘর্ষ) আপনাকে পৃথক হ্যাশ মানগুলি কীভাবে একত্রিত করা যায় সে সম্পর্কে আপনি খুব সাবধানে চিন্তা করতে হবে যাতে আপনি প্রায়শই বিভিন্ন সামগ্রীর জন্য একই আউটপুট পেতে না পারেন।

হ্যাশ ফাংশনটির জন্য মোটামুটি ভাল সূচনা পয়েন্ট হ'ল পৃথক হ্যাশ মানগুলিকে একত্রিত করতে বিট শিফটিং এবং বিটওয়াইজ এক্সওআর ব্যবহার করা হয়। উদাহরণস্বরূপ, এই জাতীয় কী-ধরণের ধরে নেওয়া:

struct Key
{
  std::string first;
  std::string second;
  int         third;

  bool operator==(const Key &other) const
  { return (first == other.first
            && second == other.second
            && third == other.third);
  }
};

এখানে একটি সাধারণ হ্যাশ ফাংশন ( ব্যবহারকারী-সংজ্ঞায়িত হ্যাশ ফাংশনগুলির জন্য সিপ্রেফারেন্স উদাহরণ হিসাবে ব্যবহৃত একটি থেকে অভিযোজিত ):

namespace std {

  template <>
  struct hash<Key>
  {
    std::size_t operator()(const Key& k) const
    {
      using std::size_t;
      using std::hash;
      using std::string;

      // Compute individual hash values for first,
      // second and third and combine them using XOR
      // and bit shifting:

      return ((hash<string>()(k.first)
               ^ (hash<string>()(k.second) << 1)) >> 1)
               ^ (hash<int>()(k.third) << 1);
    }
  };

}

এটি জায়গায় রেখে আপনি std::unordered_mapকী-টাইপের জন্য ইনস্ট্যান্ট করতে পারেন :

int main()
{
  std::unordered_map<Key,std::string> m6 = {
    { {"John", "Doe", 12}, "example"},
    { {"Mary", "Sue", 21}, "another"}
  };
}

এটি std::hash<Key>হ্যাশ মান গণনার জন্য উপরে operator==সংজ্ঞায়িত হিসাবে Keyএবং সমতা পরীক্ষার জন্য সদস্য ফাংশন হিসাবে সংজ্ঞায়িত হিসাবে স্বয়ংক্রিয়ভাবে ব্যবহার করবে ।

আপনি যদি stdনেমস্পেসের অভ্যন্তরে টেমপ্লেট বিশেষায়িত করতে না চান (যদিও এটি এক্ষেত্রে পুরোপুরি আইনী) তবে আপনি হ্যাশ ফাংশনটিকে একটি পৃথক শ্রেণি হিসাবে সংজ্ঞায়িত করতে এবং মানচিত্রের টেম্পলেট যুক্তির তালিকায় যুক্ত করতে পারেন:

struct KeyHasher
{
  std::size_t operator()(const Key& k) const
  {
    using std::size_t;
    using std::hash;
    using std::string;

    return ((hash<string>()(k.first)
             ^ (hash<string>()(k.second) << 1)) >> 1)
             ^ (hash<int>()(k.third) << 1);
  }
};

int main()
{
  std::unordered_map<Key,std::string,KeyHasher> m6 = {
    { {"John", "Doe", 12}, "example"},
    { {"Mary", "Sue", 21}, "another"}
  };
}

কিভাবে একটি ভাল হ্যাশ ফাংশন সংজ্ঞায়িত? উপরে যেমন বলা হয়েছে, সংঘর্ষ এড়াতে এবং ভাল পারফরম্যান্স পাওয়ার জন্য একটি ভাল হ্যাশ ফাংশন সংজ্ঞা দেওয়া গুরুত্বপূর্ণ is সত্যিকারের ভালটির জন্য আপনাকে সমস্ত ক্ষেত্রের সম্ভাব্য মানগুলির বিতরণকে ધ્યાનમાં নিতে হবে এবং একটি হ্যাশ ফাংশনটি সংজ্ঞায়িত করতে হবে যা সম্ভাব্য ফলাফলের জায়গাতে বিতরণ যতটা প্রশস্ত এবং সমানভাবে সমানভাবে বিতরণ করে projects

এটি কঠিন হতে পারে; উপরের এক্সওআর / বিট-শিফটিং পদ্ধতিটি সম্ভবত খারাপ শুরু নয়। কিছুটা ভাল শুরু করার জন্য, আপনি বুস্ট লাইব্রেরি থেকে hash_valueএবং hash_combineফাংশন টেম্পলেট ব্যবহার করতে পারেন । প্রাক্তনগুলি std::hashস্ট্যান্ডার্ড ধরণের জন্য একইভাবে কাজ করে (সম্প্রতি এছাড়াও টিপলস এবং অন্যান্য দরকারী স্ট্যান্ডার্ড প্রকারগুলি সহ); পরেরটি আপনাকে ব্যক্তিগত হ্যাশ মানগুলিকে একের সাথে একত্রিত করতে সহায়তা করে। এখানে বুশ সহায়ক ফাংশন ব্যবহার করে হ্যাশ ফাংশনটির একটি পুনর্লিখন:

#include <boost/functional/hash.hpp>

struct KeyHasher
{
  std::size_t operator()(const Key& k) const
  {
      using boost::hash_value;
      using boost::hash_combine;

      // Start with a hash value of 0    .
      std::size_t seed = 0;

      // Modify 'seed' by XORing and bit-shifting in
      // one member of 'Key' after the other:
      hash_combine(seed,hash_value(k.first));
      hash_combine(seed,hash_value(k.second));
      hash_combine(seed,hash_value(k.third));

      // Return the result.
      return seed;
  }
};

এবং এখানে একটি পুনর্লিখন এখানে বুস্ট ব্যবহার করে না, তবুও হ্যাশগুলি একত্রিত করার ভাল পদ্ধতি ব্যবহার করে:

namespace std
{
    template <>
    struct hash<Key>
    {
        size_t operator()( const Key& k ) const
        {
            // Compute individual hash values for first, second and third
            // http://stackoverflow.com/a/1646913/126995
            size_t res = 17;
            res = res * 31 + hash<string>()( k.first );
            res = res * 31 + hash<string>()( k.second );
            res = res * 31 + hash<int>()( k.third );
            return res;
        }
    };
}

11
আপনি দয়া করে ব্যাখ্যা করতে পারেন কেন বিটগুলি স্থানান্তর করা প্রয়োজন KeyHasher?
চানি

45
আপনি যদি বিটগুলি স্থানান্তর না করেন এবং দুটি স্ট্রিং একই থাকে, তবে জোওর তাদের একে অপরকে বাতিল করে দেবে। সুতরাং হ্যাশ ("a", "ক", 1) হ্যাশ ("বি", "বি", 1) এর সমান হবে। অর্ডারও কিছু যায় আসে না, সুতরাং হ্যাশ ("a", "খ", 1) হ্যাশ ("বি", "এ", 1) এর সমান হবে।
বুগ

1
আমি কেবল সি ++ শিখছি এবং একটি জিনিস যার সাথে আমি সর্বদা লড়াই করি: কোডটি কোথায় রাখব? std::hashআপনি যেমনটি করেছেন তেমনভাবে আমি আমার কীটির জন্য একটি বিশেষ পদ্ধতি লিখেছি । আমি আমার Key.cpp ফাইল নীচে এই করা কিন্তু আমি নিম্নলিখিত ত্রুটির পাচ্ছি: Error 57 error C2440: 'type cast' : cannot convert from 'const Key' to 'size_t' c:\program files (x86)\microsoft visual studio 10.0\vc\include\xfunctional। আমি অনুমান করছি যে সংকলকটি আমার হ্যাশ পদ্ধতিটি খুঁজে পাচ্ছে না? আমি কি আমার কী.ডি. ফাইলটিতে কিছু যুক্ত করব?
বেন

4
@ বেন এটিকে .h ফাইলে রেখে দেওয়া সঠিক। std::hashআসলে কাঠামো নয়, স্ট্রাক্টের জন্য একটি টেম্পলেট (বিশেষায়িতকরণ) । সুতরাং এটি বাস্তবায়ন নয় - সংকলকটির যখন এটি প্রয়োজন তখন এটি বাস্তবায়নে রূপান্তরিত হবে। টেমপ্লেটগুলি সর্বদা হেডার ফাইলগুলিতে চলে into আরও দেখুন stackoverflow.com/questions/495021/...
jogojapan

3
@ নাইটফুরি find()একটি পুনরুক্তি ফেরত দেয় এবং সেই পুনরাবৃত্তিটি মানচিত্রের একটি "প্রবেশ" নির্দেশ করে points একটি এন্ট্রি std::pairকী এবং মান সমন্বিত। সুতরাং আপনি যদি তা করেন তবে আপনি auto iter = m6.find({"John","Doe",12});কীটি iter->firstএবং মানটি (অর্থাত্ স্ট্রিং "example") পাবেন iter->second। আপনি যদি স্ট্রিংটি সরাসরি চান, আপনি হয় m6.at({"John","Doe",12})(চাবিটি উপস্থিত না হলে একটি ব্যতিক্রম ছুঁড়ে ফেলবে) ব্যবহার করতে পারেন , বা m6[{"John","Doe",12}](কীটি উপস্থিত না থাকলে খালি মান তৈরি করবে)।
jogojapan

16

আমার মনে হয়, জোগোজাপান একটি খুব ভাল এবং বিস্তৃত উত্তর দিয়েছে । আপনার অবশ্যই আমার পোস্টটি পড়ার আগে অবশ্যই এটি একবার নেওয়া উচিত। তবে আমি নিম্নলিখিতগুলি যুক্ত করতে চাই:

  1. unordered_mapসাম্য তুলনা অপারেটর ( operator==) ব্যবহার না করে আপনি আলাদাভাবে তুলনা ফাংশনটি সংজ্ঞায়িত করতে পারেন । এটি সহায়ক হতে পারে, উদাহরণস্বরূপ, আপনি যদি দুটি Nodeঅবজেক্টের সমস্ত সদস্যকে একে অপরের সাথে তুলনা করার জন্য উত্তরোত্তরটি ব্যবহার করতে চান তবে কেবল কিছু নির্দিষ্ট সদস্যকে একটি এর কী হিসাবে ব্যবহার করতে পারেন unordered_map
  2. আপনি হ্যাশ এবং তুলনা ফাংশনগুলি সংজ্ঞায়নের পরিবর্তে ল্যাম্বডা এক্সপ্রেশনও ব্যবহার করতে পারেন ।

সব মিলিয়ে আপনার Nodeক্লাসের জন্য কোডটি নিম্নরূপ লেখা যেতে পারে:

using h = std::hash<int>;
auto hash = [](const Node& n){return ((17 * 31 + h()(n.a)) * 31 + h()(n.b)) * 31 + h()(n.c);};
auto equal = [](const Node& l, const Node& r){return l.a == r.a && l.b == r.b && l.c == r.c;};
std::unordered_map<Node, int, decltype(hash), decltype(equal)> m(8, hash, equal);

মন্তব্য:

  • জোগোজাপানের উত্তরের শেষে আমি হ্যাশিংয়ের পদ্ধতিটি পুনরায় ব্যবহার করেছি, তবে আপনি এখানে আরও সাধারণ সমাধানের জন্য ধারণাটি পেতে পারেন (যদি আপনি বুস্ট ব্যবহার করতে না চান)।
  • আমার কোডটি সম্ভবত কিছুটা কমিয়ে দেওয়া হয়েছে। কিছুটা বেশি পঠনযোগ্য সংস্করণের জন্য দয়া করে আইডিয়নে এই কোডটি দেখুন ।

8 কোথা থেকে এসেছে এবং এর অর্থ কী?
অ্যান্ডিচিন

@WhalalalalalalaCHen: দয়া করে কটাক্ষপাত করা ডকুমেন্টেশন unordered_mapকন্সট্রাকটর8তথাকথিত "বালতি গণনা" প্রতিনিধিত্ব করে। একটি বালতি কনটেইনার অভ্যন্তরীণ হ্যাশ টেবিলের একটি স্লট, unordered_map::bucket_countআরও তথ্যের জন্য যেমন দেখুন।
হন্ক

@ ওয়ালালালালালালাচেন: আমি 8এলোমেলোভাবে বেছে নিয়েছি । আপনার যে সামগ্রী সংরক্ষণ করতে চান তার উপর নির্ভর করে unordered_mapবালতি গণনা ধারকটির কার্যকারিতা প্রভাবিত করতে পারে।
হন্ক
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.