স্ট্রিং জন্য হ্যাশ ফাংশন


124

আমি সি ভাষায় হ্যাশ টেবিলের উপর কাজ করছি এবং আমি স্ট্রিংয়ের জন্য হ্যাশ ফাংশনটি পরীক্ষা করছি।

আমি চেষ্টা করেছি প্রথম ফাংশনটি হচ্ছে এসকি কোড যুক্ত করা এবং মডুলো (% 100) ব্যবহার করা কিন্তু আমি ডেটার প্রথম পরীক্ষার সাথে খারাপ ফলাফল পেয়েছি: 130 শব্দের জন্য 40 সংঘর্ষ।

চূড়ান্ত ইনপুট ডেটাতে 8,000 শব্দ থাকবে (এটি একটি ফাইলে একটি কাল্পনিক স্টোর)। হ্যাশ টেবিলটি ইন্টি টেবিল হিসাবে ঘোষণা করা হয়েছে [10000] এবং এটি একটি টেক্সট ফাইলে শব্দটির অবস্থান ধারণ করে।

প্রথম প্রশ্নটি হ্যাশিং স্ট্রিংয়ের জন্য সেরা অ্যালগরিদম কোনটি? এবং কিভাবে হ্যাশ টেবিলের আকার নির্ধারণ করবেন?

আগাম ধন্যবাদ !

:-)


11
যদি আপনার হ্যাশ টেবিলটিতে 10 কে প্রবেশ থাকে তবে আপনি মডুলো 100 ব্যবহার করবেন কেন? 130 শব্দের মধ্যে 40 টি সংঘর্ষ পাওয়া এত ছোট মডুলাসের সাথে আশ্চর্যজনক নয়।
কেরি গ্রেগরি

13
Burtleburtle.net/bob/hash/evahash.html এবং partow.net/programming/hashfunction দেখুন যার জন্য বিভিন্ন হ্যাশিং সম্পর্কিত সংস্থান (সাধারণ থেকে স্ট্রিং থেকে ক্রিপ্টো পর্যন্ত) resources

3
@ কেরি গ্রেগরিটি স্পষ্ট করার জন্য: আপনি বুঝতে পেরেছেন যে একটি মৌলিক গাণিতিক সত্য হিসাবে, 100 বালতিতে 130 টি আইটেম (অর্থাত্ Mod 100) অবশ্যই 30 টি সংঘর্ষ তৈরি করতে পারে (যেখানে সংঘর্ষ প্রতিবার দ্বিতীয়, তৃতীয় ইত্যাদি হিসাবে গণনা করা হয়) একটি বালতি), সঠিক? সুতরাং আপনি যে কিছুটা উপরে আছেন।
ডারোবার্ট

4
@ লীলাউড: ঠিক আছে, আমি এটাই বুঝতে পেরেছি, তবে আরও ভাল পরীক্ষা হওয়ার জন্য আপনার 100 টি প্রবেশের একটি হ্যাশ টেবিল সহ 80 টি শব্দ ব্যবহার করা উচিত। এটি আপনাকে আপনার সরাসরি ডেটা হিসাবে একই অনুপাত দেবে এবং সংঘর্ষের জন্য বাধ্য করবে না।
কেরি গ্রেগরি

উত্তর:


185

djb2ড্যান বার্নস্টেইনের সাথে আমার ভাল ফলাফল হয়েছে ।

unsigned long
hash(unsigned char *str)
{
    unsigned long hash = 5381;
    int c;

    while (c = *str++)
        hash = ((hash << 5) + hash) + c; /* hash * 33 + c */

    return hash;
}

37
উত্তরের সাথে লিঙ্ক করা পৃষ্ঠাটি খুব আকর্ষণীয়।
অ্যাড্রিয়েন প্লিসন

2
প্রোগ্রামটি কীভাবে লুপটি শেষ করে ?? = এস
ড্যানিয়েল এন।

1
@ danfly09 যখন সি শূন্য হয়। (সি = * আরআর ++) এর সমতুল্য হবে (0! = (সি = *
টিআর

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

6
আশ্চর্যজনক। এই অ্যালগরিদম মারমুর হ্যাশ, এফএনভি ভেরিয়েন্টস হ্যাশ এবং আরও অনেকগুলি থেকে নরকে পরাজিত করেছে! +1
ডেভিড হাইম

24

প্রথমত, আপনি সাধারণত হ্যাশ টেবিলের জন্য কোনও ক্রিপ্টোগ্রাফিক হ্যাশ ব্যবহার করতে চান না । একটি অ্যালগরিদম যা ক্রিপ্টোগ্রাফিক মানদণ্ডের দ্বারা খুব দ্রুত হয় এখনও হ্যাশ টেবিলের মান দ্বারা উদ্দীপনাজনকভাবে ধীরে ধীরে।

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

উদাহরণস্বরূপ, 8 বিট বাইটের সাধারণ ক্ষেত্রে ধরে নেওয়া, আপনি 5 বিট দ্বারা ঘোরতে পারেন:

int hash(char const *input) { 
    int result = 0x55555555;

    while (*input) { 
        result ^= *input++;
        result = rol(result, 5);
    }
}

সম্পাদনা: এছাড়াও নোট করুন যে 10000 স্লট হ্যাশ টেবিল আকারের জন্য খুব কমই ভাল পছন্দ is আপনি সাধারণত দুটি জিনিসের একটি চান: আপনি হয় আকার হিসাবে একটি প্রাথমিক সংখ্যা চান (কিছু ধরণের হ্যাশ রেজোলিউশনের সাথে নির্ভুলতা নিশ্চিত করা প্রয়োজন) অথবা 2 এর শক্তি (যাতে সঠিক পরিসরে মান হ্রাস করা সহজ একটি সাধারণ পদ্ধতিতে করা যেতে পারে) বিট-মাস্ক)।


এই সি নয়, কিন্তু আমি এই সংশ্লিষ্ট উত্তর আপনার চিন্তা আগ্রহী হতে হবে: stackoverflow.com/a/31440118/3681880
Suragch

1
@ সুরগাচ: যেহেতু আমি এটি লিখেছি, বেশ কয়েকজন প্রসেসর এসএএএর গণনা ত্বরান্বিত করার জন্য বিশেষভাবে হার্ডওয়্যারকে অন্তর্ভুক্ত করা শুরু করেছেন, যা এটি আরও বেশি প্রতিযোগিতামূলক করে তুলেছে। এটি বলেছিল, আমি সন্দেহ করি আপনার কোডটি আপনার ধারণা মতই নিরাপদ - উদাহরণস্বরূপ, আইইইই ভাসমান পয়েন্ট সংখ্যাগুলির দুটি পৃথক বিট নিদর্শন রয়েছে (0 এবং -0) যা একই হ্যাশগুলি উত্পাদন করতে পারে (তারা একে অপরের সমান হিসাবে তুলনা করবে) )।
জেরি কফিন

@ জেরি কফিন আমাকে রোল () ফাংশনের জন্য কোন লাইব্রেরিটির প্রয়োজন?
Thanos.a

@ থানোস.এ: আমি এটি কোনও গ্রন্থাগারে রয়েছি সম্পর্কে অবগত নই, তবে আপনার নিজের ঘূর্ণায়মান হতে কেবল কোডের একটি লাইন বা দুটি লাগে। এক অংশ বামে, অন্য অংশটি ডানদিকে এবং এগুলি একসাথে শিফট করুন।
জেরি কফিন

8

উইকিপিডিয়ায় জেনকিন্স ওয়ান এ টাইম হ্যাশ নামে একটি দুর্দান্ত স্ট্রিং হ্যাশ ফাংশন দেখায় । এটি এই হ্যাশের উন্নত সংস্করণগুলি উদ্ধৃত করে।

uint32_t jenkins_one_at_a_time_hash(char *key, size_t len)
{
    uint32_t hash, i;
    for(hash = i = 0; i < len; ++i)
    {
        hash += key[i];
        hash += (hash << 10);
        hash ^= (hash >> 6);
    }
    hash += (hash << 3);
    hash ^= (hash >> 11);
    hash += (hash << 15);
    return hash;
}

8

সি জন্য বিদ্যমান hashtable বাস্তবায়নের একটি নম্বর সি মান গ্রন্থাগার hcreate থেকে আছে / hdestroy / hsearch, যারা এপিআর এবং সাবলীল , যা prebuilt হ্যাশ ফাংশন প্রদান। আমি আপনার নিজস্ব হ্যাশ টেবিল বা হ্যাশ ফাংশন আবিষ্কার করার চেয়ে এগুলি ব্যবহার করার সুপারিশ করব; তারা সাধারণ ব্যবহারের ক্ষেত্রে ভারীভাবে অনুকূলিত হয়েছে।

যদি আপনার ডেটাসেট স্থিতিশীল হয় তবে আপনার সেরা সমাধানটি সম্ভবত একটি নিখুঁত হ্যাশ ব্যবহার করা । gperf একটি প্রদত্ত ডেটাসেটের জন্য আপনার জন্য একটি নিখুঁত হ্যাশ উত্পন্ন করবে।


স্ট্রিং বা স্ট্রিং পিটিআর ঠিকানার সাথে তুলনা করে অনুসন্ধানের অনুসন্ধানগুলি? আমি মনে করি এটি কেবল পিটিআর ঠিকানা পরীক্ষা করে দেখছে? আমি বিভিন্ন পয়েন্টার ব্যবহার করার চেষ্টা করেছি কিন্তু একই স্ট্রিং ক্যু ব্যবহার করেছি। hsearch কোনও উপাদান খুঁজে পাওয়া যায় নি বলে ব্যর্থ হয়েছে
এম কে ..

3

ডিজেবি 2 এর এই 466 ক ইংলিশ অভিধানের জন্য 317 টি সংঘর্ষ রয়েছে যখন মার্মুরহ্যাশ 64৪ বিট হ্যাশের জন্য আর 21 টি 32 বিট হ্যাশের জন্য নেই (প্রায় 256 466k এলোমেলো 32 বিট হ্যাশের জন্য প্রত্যাশিত)। আমার সুপারিশটি যদি পাওয়া যায় তবে মর্মুরহ্যাশ ব্যবহার করছে এটি খুব দ্রুত, কারণ এটি একবারে বেশ কয়েকটি বাইট নেয়। তবে আপনার প্রকল্পটিতে অনুলিপি করতে এবং কাস্ট করার জন্য যদি আপনার একটি সহজ এবং সংক্ষিপ্ত হ্যাশ ফাংশন প্রয়োজন হয় তবে আমি বয়মারগুলি এক-সময়ে-সময়ে-সংস্করণ ব্যবহার করার পরামর্শ দেব:

uint32_t inline MurmurOAAT32 ( const char * key)
{
  uint32_t h(3323198485ul);
  for (;*key;++key) {
    h ^= *key;
    h *= 0x5bd1e995;
    h ^= h >> 15;
  }
  return h;
}

uint64_t inline MurmurOAAT64 ( const char * key)
{
  uint64_t h(525201411107845655ull);
  for (;*key;++key) {
    h ^= *key;
    h *= 0x5bd1e9955bd1e995;
    h ^= h >> 47;
  }
  return h;
}

হ্যাশ টেবিলের সর্বোত্তম আকারটি হ'ল - সংক্ষেপে - মেমরিটিতে ফিট করার সময় যতটা সম্ভব বড়। যেহেতু আমরা সাধারণত জানি না বা আমাদের কাছে কত স্মৃতি উপলব্ধ রয়েছে তা সন্ধান করতে চাই না এবং এটি এমনকি পরিবর্তিত হতে পারে, সর্বোত্তম হ্যাশ টেবিলের আকারটি প্রায় টেবিলের মধ্যে থাকা উপাদানগুলির প্রত্যাশিত সংখ্যার 2x হয় is এর চেয়ে অনেক বেশি বরাদ্দ দেওয়া আপনার হ্যাশ টেবিলটিকে দ্রুততর করে তুলবে তবে দ্রুত হ্রাস হওয়া রিটার্নগুলিতে, আপনার হ্যাশ টেবিলটিকে তার চেয়ে ছোট করে তুলবে এটি তাত্পর্যপূর্ণভাবে ধীর করে দেবে। এটি কারণ হ্যাশ টেবিলগুলির জন্য স্থান এবং সময় জটিলতার মধ্যে একটি অ-রৈখিক বাণিজ্য বন্ধ রয়েছে , এটি 2-স্কয়ার্ট (2) = 0.58 এর সর্বোত্তম লোড ফ্যাক্টর সহ ... দৃশ্যত।


2

প্রথমত, 130 টি শব্দের জন্য 40 সংঘর্ষগুলি 0..99 এ খারাপ হয়েছে? আপনি যদি এটির জন্য বিশেষভাবে পদক্ষেপ না নিচ্ছেন তবে আপনি নিখুঁত হ্যাশিং আশা করতে পারবেন না। একটি সাধারণ হ্যাশ ফাংশনটির বেশিরভাগ সময় এলোমেলো জেনারেটরের চেয়ে কম সংঘর্ষ হয় না।

একটি ভাল খ্যাতি সহ একটি হ্যাশ ফাংশন হ'ল মার্মুরহ্যাশ 3

অবশেষে, হ্যাশ টেবিলের আকার সম্পর্কে, এটি আপনার মস্তিষ্কে কী ধরণের হ্যাশ টেবিলের উপর নির্ভর করে তা বিশেষত: বালতিগুলি এক্সটেনসিবল বা এক-স্লট কিনা depends যদি বালতিগুলি এক্সটেনসিবল হয় তবে আবার একটি পছন্দ আছে: আপনি যে স্মৃতি / গতির সীমাবদ্ধতা আছে তার জন্য গড় বালতির দৈর্ঘ্য বেছে নিন।


1
হ্যাশ সংঘর্ষের প্রত্যাশিত সংখ্যাটি n - m * (1 - ((m-1)/m)^n) = 57.075...। 40 টি সংঘর্ষগুলি সুযোগ দ্বারা প্রত্যাশিত যা হতে পারে তার চেয়ে ভাল (0.999 এর পি স্কোরের 46 থেকে 70)। প্রশ্নটিতে থাকা হ্যাশ ফাংশনটি এলোমেলো ছিল বা আমরা খুব বিরল ঘটনা প্রত্যক্ষ করছি তার চেয়ে বেশি অভিন্ন।
ওল্ফগ্যাং ব্রাহ্ম

2

যদিও djb2, যেমন cnicutar দ্বারা Stackoverflow উপস্থাপিত , ভাল প্রায় অবশ্যই, আমি এটা দেখাচ্ছে মূল্য মনে কে & R এর খুব হ্যাশ:

1) কেএন্ডআর প্রথম সংস্করণে উত্স হিসাবে উপস্থাপিতভাবে দৃশ্যত একটি ভয়াবহ হ্যাশ অ্যালগরিদম ( উত্স )

unsigned long hash(unsigned char *str)
{
    unsigned int hash = 0;
    int c;

    while (c = *str++)
        hash += c;

    return hash;
}

2) সম্ভবত একটি চমত্কার শালীন হ্যাশ অ্যালগরিদম, কে ও আর সংস্করণ 2 এ উপস্থাপন করা হয়েছে (বইয়ের পৃষ্ঠা 144-তে আমার দ্বারা যাচাই করা হয়েছে); এনবি: % HASHSIZEআপনি যদি হ্যাশ অ্যালগরিদমের বাইরে মডিউলাস আকার-থেকে-আপনার-অ্যারে-দৈর্ঘ্য করার পরিকল্পনা করেন তবে রিটার্নের বিবৃতিটি সরিয়ে ফেলার বিষয়ে নিশ্চিত হন । এছাড়াও, আমি আপনাকে প্রস্তাব দিচ্ছি যে আপনি unsigned longসরল unsigned( ইন্ট ) এর পরিবর্তে রিটার্ন এবং "হ্যাশওয়াল" টাইপ করুন ।

unsigned hash(char *s)
{
    unsigned hashval;

    for (hashval = 0; *s != '\0'; s++)
        hashval = *s + 31*hashval;
    return hashval % HASHSIZE;
}

মনে রাখবেন যে দুই আলগোরিদিম থেকে পরিষ্কার এক কারণে 1 ম সংস্করণ হ্যাশ তাই ভয়ানক যে কারণ এটি বিবেচনা স্ট্রিং অক্ষর অন্তর্ভুক্ত নেই অর্ডার , তাই hash("ab")তাই মানের সমান ফিরে আসবে hash("ba")। এটি ২ য় সংস্করণের হ্যাশের সাথে তেমন নয় , তবে, যা এই স্ট্রিংগুলির জন্য দুটি পৃথক মান প্রদান করবে।

unordered_map(একটি হ্যাশ টেবিল টেমপ্লেট) এবং unordered_set(একটি হ্যাশ সেট টেম্পলেট) এর জন্য ব্যবহৃত জিসিসি সি ++ 11 হ্যাশিং ফাংশনগুলি নীচে প্রদর্শিত হবে।

  • এটি জিসিসি সি ++ 11 হ্যাশ ফাংশনগুলি কী কী তা ব্যবহার করার প্রশ্নের আংশিক উত্তর , উল্লেখ করে যে জিসিসি অস্টিন অ্যাপলবি ( http://murmurhash.googlepages.com/ ) দ্বারা "মুরমারহ্যাশ ইউনিলাইনড 2" প্রয়োগ করে uses
  • "Gcc / libstdc ++ - v3 / libsupc ++ / hash_bytes.cc" ফাইলটিতে, এখানে ( https://github.com/gcc-mirror/gcc/blob/master/libstdc+-v3/libsupc++/hash_bytes.cc ) পাওয়া গেছে বাস্তবায়ন। "32-বিট সাইজ_টি" রিটার্ন মানটির একটি এখানে, উদাহরণস্বরূপ (11 আগস্ট 2017 টানা):

কোড:

// Implementation of Murmur hash for 32-bit size_t.
size_t _Hash_bytes(const void* ptr, size_t len, size_t seed)
{
  const size_t m = 0x5bd1e995;
  size_t hash = seed ^ len;
  const char* buf = static_cast<const char*>(ptr);

  // Mix 4 bytes at a time into the hash.
  while (len >= 4)
  {
    size_t k = unaligned_load(buf);
    k *= m;
    k ^= k >> 24;
    k *= m;
    hash *= m;
    hash ^= k;
    buf += 4;
    len -= 4;
  }

  // Handle the last few bytes of the input array.
  switch (len)
  {
    case 3:
      hash ^= static_cast<unsigned char>(buf[2]) << 16;
      [[gnu::fallthrough]];
    case 2:
      hash ^= static_cast<unsigned char>(buf[1]) << 8;
      [[gnu::fallthrough]];
    case 1:
      hash ^= static_cast<unsigned char>(buf[0]);
      hash *= m;
  };

  // Do a few final mixes of the hash.
  hash ^= hash >> 13;
  hash *= m;
  hash ^= hash >> 15;
  return hash;
}

2

আমি এই হ্যাশ ফাংশনগুলি চেষ্টা করেছি এবং নিম্নলিখিত ফলাফল পেয়েছি। আমার প্রায় 960 ^ 3 টি এন্ট্রি রয়েছে, প্রতিটি 64 বাইট দীর্ঘ, বিভিন্ন ক্রমে 64 অক্ষর, হ্যাশ মান 32 বিট। থেকে কোড এখানে

Hash function    | collision rate | how many minutes to finish
==============================================================
MurmurHash3      |           6.?% |                      4m15s
Jenkins One..    |           6.1% |                      6m54s   
Bob, 1st in link |          6.16% |                      5m34s
SuperFastHash    |            10% |                      4m58s
bernstein        |            20% |       14s only finish 1/20
one_at_a_time    |          6.16% |                       7m5s
crc              |          6.16% |                      7m56s

একটি আশ্চর্যের বিষয় হ'ল প্রায় সমস্ত হ্যাশ ফাংশনগুলিতে আমার ডেটার জন্য 6% সংঘর্ষ হার রয়েছে।


যদিও এই লিঙ্কটি প্রশ্নের উত্তর দিতে পারে, উত্তরের প্রয়োজনীয় অংশগুলি এখানে অন্তর্ভুক্ত করা এবং রেফারেন্সের জন্য লিঙ্কটি সরবরাহ করা ভাল। লিঙ্কযুক্ত পৃষ্ঠাগুলি পরিবর্তিত হলে লিঙ্ক-শুধুমাত্র উত্তরগুলি অবৈধ হতে পারে।
ওয়েওয়েওয়েরে

একটি ভাল টেবিলের জন্য উত্সাহিত করা, আপনার উত্তরে সেই সমস্ত হ্যাশগুলির জন্য উত্স কোড পোস্ট করা খুব প্রয়োজনীয়। অন্যথায়, লিঙ্কগুলি ভেঙে যেতে পারে এবং আমরা ভাগ্যের বাইরে।
গ্যাব্রিয়েল স্ট্যাপলস

সংঘর্ষের প্রত্যাশিত সংখ্যাটি 9.112499989700318E + 7 বা 0.103 * 960³ হওয়া উচিত যদি হ্যাশগুলি সত্যই এলোমেলো ছিল তবে আমি যদি সে সমস্ত মানের কাছাকাছি হতাম তবে অবাক হতাম না, তবে 0.0616 * 960³ কিছুটা দূরে মনে হয়, প্রায় সম্ভাবনা অনুসারে প্রত্যাশার চেয়ে হ্যাশগুলি সমানভাবে বিতরণ করা হয় এবং 64৪ বাইট দৈর্ঘ্যে এই সীমাটি অবশ্যই পৌঁছাতে হবে। আপনি যে স্ট্রিংগুলি ফেলেছেন সেগুলির সেটটি কি আপনি ভাগ করে নিতে পারেন যাতে আমি এটির পুনরুত্পাদন করার চেষ্টা করতে পারি?
ওল্ফগ্যাং বেরহম

0

আমি ভাল ফলাফলের সাথে একটি জিনিস ব্যবহার করেছি তা হ'ল (আমি জানি না যে এটি ইতিমধ্যে উল্লেখ করা হয়েছে কারণ আমি এর নামটি মনে করতে পারি না)।

আপনি আপনার কী এর বর্ণমালা [0,255] এর প্রতিটি অক্ষরের জন্য এলোমেলো সংখ্যার সাথে একটি টেবিল টি সংক্ষেপণ করুন। আপনি টি [কে0] জোর টি [কে 1] এক্সওর ... xor টি [কেএন] নিয়ে আপনার কী 'কে 0 কে 1 কে 2 ... কেএন' হ্যাশ করেছেন। আপনি সহজেই দেখিয়ে দিতে পারেন যে এটি আপনার এলোমেলো নম্বর জেনারেটরের মতো এলোমেলো এবং এটির কম্পিউটেশনালি খুব সম্ভাব্য এবং আপনি যদি খুব সংঘর্ষের সাথে সত্যিই খুব খারাপ পরিস্থিতিতে চলে যান তবে আপনি এলোমেলো সংখ্যার একটি নতুন ব্যাচ ব্যবহার করে পুরো জিনিসটি পুনরাবৃত্তি করতে পারেন।


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