আমি সি ++ 0 এক্সে হ্যাশ মানগুলি কীভাবে একত্র করব?


87

সি ++ 0 এক্স যোগ করে hash<...>(...)

উত্সাহhash_combine হিসাবে উপস্থাপিত হিসাবে আমি যদিও একটি ফাংশন সন্ধান করতে পারেনি । এরকম কিছু বাস্তবায়নের সবচেয়ে পরিষ্কার উপায় কী? সম্ভবত, সি ++ 0 এক্স ব্যবহার করছেন ?xor_combine

উত্তর:


94

ঠিক আছে, বুস্ট ছেলেরা যেমন করেছে তেমনই এটি করুন:

template <class T>
inline void hash_combine(std::size_t& seed, const T& v)
{
    std::hash<T> hasher;
    seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);
}

27
হ্যাঁ, এটি আমিও করতে পারি সেরা। আমি বুঝতে পারি না কীভাবে স্ট্যান্ডার্ড কমিটি এতো সুস্পষ্ট কিছু অস্বীকার করেছিল।
নীল জি

13
@ নীল: আমি সম্মত আমি মনে করি তাদের জন্য একটি সহজ সমাধান হ'ল std::pair(বা tuple, এমনকি) জন্য একটি হ্যাশ গ্রন্থাগারের প্রয়োজন । এটি প্রতিটি উপাদানের হ্যাশ গণনা করবে, তারপরে তাদের একত্রিত করবে। (এবং স্ট্যান্ডার্ড লাইব্রেরির চেতনায়, একটি বাস্তবায়নের সংজ্ঞায়িত উপায়ে))
GManNGG

4
মান থেকে বাদ দেওয়া অনেকগুলি সুস্পষ্ট জিনিস রয়েছে। নিবিড় সহকর্মী পর্যালোচনা প্রক্রিয়া সেই ছোট জিনিসগুলি দরজা থেকে সরিয়ে নেওয়া কঠিন করে তোলে।
stinky472

15
এই যাদু সংখ্যা এখানে কেন? এবং উপরের মেশিন-নির্ভর নয় (যেমন এটি x86 এবং x64 প্ল্যাটফর্মে আলাদা হবে না)?
einpoklum

4
একটা কাগজ hash_combine অন্তর্ভুক্তি পরামর্শ এর এখানে
SSJ_GZ

37

আমি এখানে এটিকে ভাগ করব যেহেতু এটি সমাধানের সন্ধানে অন্যদের পক্ষে এটি কার্যকর হতে পারে: @ কার্লভনমুআর উত্তর থেকে শুরু করে , এখানে একটি বৈকল্পিক টেম্পলেট সংস্করণ রয়েছে, যা আপনাকে বেশ কয়েকটি মানকে একত্রিত করতে হলে এর ব্যবহারের ক্ষেত্রে ক্ষুদ্রতর :

inline void hash_combine(std::size_t& seed) { }

template <typename T, typename... Rest>
inline void hash_combine(std::size_t& seed, const T& v, Rest... rest) {
    std::hash<T> hasher;
    seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);
    hash_combine(seed, rest...);
}

ব্যবহার:

std::size_t h=0;
hash_combine(h, obj1, obj2, obj3);

এটি কাস্টম প্রকারগুলি সহজেই হ্যাশেবল করে তোলার জন্য (যা আমার মনে হয় যে কোনও hash_combineফাংশনের প্রাথমিক ব্যবহারগুলির মধ্যে একটি ) এটি একটি ভ্যারিয়েডিক ম্যাক্রো বাস্তবায়নের জন্য মূলত লেখা হয়েছিল :

#define MAKE_HASHABLE(type, ...) \
    namespace std {\
        template<> struct hash<type> {\
            std::size_t operator()(const type &t) const {\
                std::size_t ret = 0;\
                hash_combine(ret, __VA_ARGS__);\
                return ret;\
            }\
        };\
    }

ব্যবহার:

struct SomeHashKey {
    std::string key1;
    std::string key2;
    bool key3;
};

MAKE_HASHABLE(SomeHashKey, t.key1, t.key2, t.key3)
// now you can use SomeHashKey as key of an std::unordered_map

বীজ সর্বদা যথাক্রমে 6 এবং 2 দ্বারা বিটশিত হয় কেন?
j00hi

5

এটি নিম্নলিখিত হিসাবে বৈকল্পিক টেম্পলেট ব্যবহার করেও সমাধান করা যেতে পারে:

#include <functional>

template <typename...> struct hash;

template<typename T> 
struct hash<T> 
    : public std::hash<T>
{
    using std::hash<T>::hash;
};


template <typename T, typename... Rest>
struct hash<T, Rest...>
{
    inline std::size_t operator()(const T& v, const Rest&... rest) {
        std::size_t seed = hash<Rest...>{}(rest...);
        seed ^= hash<T>{}(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
        return seed;
    }
};

ব্যবহার:

#include <string>

int main(int,char**)
{
    hash<int, float, double, std::string> hasher;
    std::size_t h = hasher(1, 0.2f, 2.0, "Hello World!");
}

একটি অবশ্যই একটি টেম্পলেট ফাংশন করতে পারে, তবে এটি কিছু বাজে টাইপের ছাড়ের কারণ হতে পারে যেমন hash("Hallo World!")স্ট্রিংয়ের পরিবর্তে পয়েন্টারে একটি হ্যাশ মান গণনা করবে। এটি সম্ভবত কারণ, কেন স্ট্যান্ডার্ড একটি কাঠামো ব্যবহার করে।


5

কিছু দিন আগে আমি এই উত্তরের কিছুটা উন্নত সংস্করণ নিয়ে এসেছি (সি ++ 17 সমর্থন প্রয়োজন):

template <typename T, typename... Rest>
void hashCombine(uint& seed, const T& v, Rest... rest)
{
    seed ^= ::qHash(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
    (hashCombine(seed, rest), ...);
}

উপরের কোডটি কোড তৈরির ক্ষেত্রে আরও ভাল better আমি আমার কোডে Qt থেকে qHash ফাংশনটি ব্যবহার করেছি, তবে অন্য কোনও হ্যাশার ব্যবহার করাও সম্ভব।


ভাঁজ প্রকাশটি হিসাবে লিখুন (int[]){0, (hashCombine(seed, rest), 0)...};এবং এটি সি ++ 11 এও কাজ করবে।
হেনরি মেনকে

3

আমি vt4a2h দ্বারা উত্তরটি থেকে C ++ 17 পদ্ধতির সত্যই পছন্দ করি , তবে এটি কোনও সমস্যায় ভুগছে: Restমানটি দিয়ে চলেছে তবে কনস্টেরেন্স রেফারেন্সগুলি দিয়ে তাদের পাস করা আরও পছন্দনীয় হবে (এটি যদি অবশ্যই হয় তবে এটি আবশ্যক কেবলমাত্র সরানো-প্রকারের সাথে ব্যবহারযোগ্য)।

এখানে অভিযোজিত সংস্করণটি এখনও একটি ভাঁজ এক্সপ্রেশন ব্যবহার করে (এটি কেন এটির জন্য C ++ 17 বা তার বেশি প্রয়োজন) এবং ব্যবহার করে std::hash(Qt হ্যাশ ফাংশনের পরিবর্তে):

template <typename T, typename... Rest>
void hash_combine(std::size_t& seed, const T& v, const Rest&... rest)
{
    seed ^= std::hash<T>{}(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
    (hash_combine(seed, rest), ...);
}

সম্পূর্ণতার জন্য: এই সংস্করণটির সাথে যে সমস্ত প্রকারের ব্যবহারযোগ্য হবে সেগুলি hash_combineঅবশ্যই নেমস্পেসে ইনজেকশনের জন্য একটি টেম্পলেট বিশেষীকরণ থাকতে হবে ।hashstd

উদাহরণ:

namespace std // Inject hash for B into std::
{
    template<> struct hash<B>
    {
        std::size_t operator()(B const& b) const noexcept
        {
            std::size_t h = 0;
            cgb::hash_combine(h, b.firstMember, b.secondMember, b.andSoOn);
            return h;
        }
    };
}

সুতরাং Bউপরের উদাহরণটিতে টাইপটি অন্য ধরণের ক্ষেত্রেও ব্যবহারযোগ্য A, যেমন নীচের ব্যবহারের উদাহরণ উদাহরণগুলি দেখায়:

struct A
{
    std::string mString;
    int mInt;
    B mB;
    B* mPointer;
}

namespace std // Inject hash for A into std::
{
    template<> struct hash<A>
    {
        std::size_t operator()(A const& a) const noexcept
        {
            std::size_t h = 0;
            cgb::hash_combine(h,
                a.mString,
                a.mInt,
                a.mB, // calls the template specialization from above for B
                a.mPointer // does not call the template specialization but one for pointers from the standard template library
            );
            return h;
        }
    };
}

আমার মতে Hashআপনার নিজের কাস্টম হ্যাশারটিকে stdনামস্থানে ইনজেকশন না দিয়ে নির্দিষ্ট করার জন্য স্ট্যান্ডার্ড পাত্রে টেমপ্লেট আর্গুমেন্টগুলি ব্যবহার করা ভাল ।
হেনরি মেনকে

3

Vt4a2h এর উত্তর অবশ্যই দুর্দান্ত তবে C ++ 17 ভাঁজ এক্সপ্রেশনটি ব্যবহার করে এবং প্রত্যেকেই সহজেই নতুন সরঞ্জামচঞ্চে স্যুইচ করতে সক্ষম হয় না। নীচের সংস্করণটি ভাঁজ এক্সপ্রেশন অনুকরণ করতে এক্সপেন্ডার ট্রিক ব্যবহার করে এবং সি ++ 11 এবং সি ++ 14 এও কাজ করে।

অতিরিক্তভাবে, আমি ফাংশনটি চিহ্নিত করেছি inlineএবং বৈকল্পিক টেম্পলেট আর্গুমেন্টগুলির জন্য নিখুঁত ফরোয়ার্ডিং ব্যবহার করি।

template <typename T, typename... Rest>
inline void hashCombine(std::size_t &seed, T const &v, Rest &&... rest) {
    std::hash<T> hasher;
    seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
    (int[]){0, (hashCombine(seed, std::forward<Rest>(rest)), 0)...};
}

সংকলক এক্সপ্লোরার লাইভ উদাহরণ


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