একটি একক চরিত্রের জন্য কি কোনও ভাল অনুসন্ধানের অ্যালগরিদম আছে?


23

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


13
আপনি এটিতে সিমডি নির্দেশাবলী নিক্ষেপ করতে পারেন তবে আপনি ও (এন) এর চেয়ে ভাল কিছু পাবেন না।
কোডসইনচওস

7
একক অনুসন্ধানের জন্য বা একই স্ট্রিংয়ে একাধিক অনুসন্ধানের জন্য?
ক্রিস্টোফ

কেএমপি অবশ্যই একটি "বেসিক" স্ট্রিং-ম্যাচিং অ্যালগরিদমকে কল করবে এমন কিছু নয় ... আমি এটিও নিশ্চিত নই যে এটি এত দ্রুত, তবে এটি icallyতিহাসিকভাবে গুরুত্বপূর্ণ। আপনি যদি কিছু প্রাথমিক চান তবে জেড অ্যালগরিদম চেষ্টা করুন।
মেহরদাদ

মনে করুন যে কোনও চরিত্রের অবস্থানটি অনুসন্ধানের অ্যালগরিদমটি দেখেনি। তারপরে এটি সেই অবস্থানের সুই অক্ষরের সাথে স্ট্রিং এবং সেই অবস্থানের ভিন্ন চরিত্রের সাথে স্ট্রিংয়ের মধ্যে পার্থক্য করতে সক্ষম হবে না।
ব্যবহারকারী 253751

উত্তর:


29

এটি বোঝা যাচ্ছে যে সবচেয়ে খারাপ পরিস্থিতিটি হ'ল O(N), খুব সুন্দর কিছু মাইক্রো-অপ্টিমাইজেশন রয়েছে।

নিষ্পাপ পদ্ধতিটি প্রতিটি চরিত্রের জন্য একটি অক্ষর তুলনা এবং পাঠ্য সমাপ্তির সম্পাদন করে।

একটি সেন্ডিনেল (অর্থাত্ পাঠ্যের শেষে লক্ষ্য অক্ষরের অনুলিপি) ব্যবহার করে প্রতিটি চরিত্রের তুলনা করার সংখ্যা হ্রাস করে 1।

বিট টুইডলিং স্তরে রয়েছে:

#define haszero(v)      ( ((v) - 0x01010101UL) & ~(v) & 0x80808080UL )
#define hasvalue(x, n)  ( haszero((x) ^ (~0UL / 255 * (n))) )

কোনও শব্দের কোনও বাইট ( x) এর একটি নির্দিষ্ট মান ( n) রয়েছে কিনা তা জানতে ।

স্যুবপ্রেসেশন v - 0x01010101UL, যখনই সম্পর্কিত বাইট vশূন্য বা তার চেয়ে বেশি হয় কোনও বাইটে উচ্চ বিট সেটকে মূল্যায়ন করে 0x80

উপ-এক্সপ্রেশনটি ~v & 0x80808080ULবাইটগুলিতে সেট উচ্চ বিটগুলিতে মূল্যায়ন করে যেখানে বাইটটির vউচ্চ বিট সেট থাকে না (তাই বাইটটি কম ছিল 0x80)।

এই দুটি উপ-এক্সপ্রেশন ( haszero) প্রকাশের ফলে ফলাফলটি উচ্চ বিটস সেট হয় যেখানে বাইটগুলি vশূন্য ছিল, যেহেতু 0x80প্রথম উপ-এক্সপ্রেশনটির চেয়ে বেশি মানের কারণে উচ্চ বিটগুলি দ্বিতীয় দ্বারা মুখোশযুক্ত হয় (এপ্রিল 27, 1987 দ্বারা অ্যালান মাইক্রফ্ট)।

এখন আমরা xএমন কোনও শব্দের সাথে ( ) পরীক্ষা করার মানটি XOR করতে পারি যা আমাদের আগ্রহী ( n) এর সাথে বাইট মানটি পূর্ণ হয়েছে । যেহেতু নিজের সাথে একটি মান জোর করা ফলস্বরূপ শূন্য বাইট এবং ননজারো হয় অন্যথায়, আমরা ফলাফলটি পাস করতে পারি haszero

এটি প্রায়শই একটি সাধারণ strchrবাস্তবায়নে ব্যবহৃত হয় ।

(স্টিফেন এম বেনেট 13 ডিসেম্বর, ২০০৯ এ পরামর্শ দিয়েছেন Further আরও বিশদ বিট টুইডলিং হ্যাকস-এ )।


দ্রষ্টব্য

এই কোডটি 1111a এর পাশের যেকোন সংমিশ্রণের জন্য ভেঙে গেছে0

হ্যাক নিষ্ঠুর শক্তি পরীক্ষা পাস (শুধু ধৈর্য ধরুন):

#include <iostream>
#include <limits>

bool haszero(std::uint32_t v)
{
  return (v - std::uint32_t(0x01010101)) & ~v & std::uint32_t(0x80808080);
}

bool hasvalue(std::uint32_t x, unsigned char n)
{
  return haszero(x ^ (~std::uint32_t(0) / 255 * n));
}

bool hasvalue_slow(std::uint32_t x, unsigned char n)
{
  for (unsigned i(0); i < 32; i += 8)
    if (((x >> i) & 0xFF) == n)
      return true;

  return false;
}

int main()
{
  const std::uint64_t stop(std::numeric_limits<std::uint32_t>::max());

  for (unsigned c(0); c < 256; ++c)
  {
    std::cout << "Testing " << c << std::endl;

    for (std::uint64_t w(0); w != stop; ++w)
    {
      if (w && w % 100000000 == 0)
        std::cout << w * 100 / stop << "%\r" << std::flush;

      const bool h(hasvalue(w, c));
      const bool hs(hasvalue_slow(w, c));

      if (h != hs)
        std::cerr << "hasvalue(" << w << ',' << c << ") is " << h << '\n';
    }
  }

  return 0;
}

একটি উত্তরের জন্য প্রচুর পরিমাণে অগ্রগতি যা অনুমানটিকে একটি চরারেক্টর = একটি বাইট করে তোলে যা আজকাল আর মানক নয়

মন্তব্যের জন্য আপনাকে ধন্যবাদ।

উত্তরটি হ'ল মাল্টি-বাইট / ভেরিয়েবল-প্রস্থের এনকোডিংগুলি সম্পর্কিত একটি রচনা :-) (সমস্ত ন্যায্যতার সাথে এটি আমার দক্ষতার ক্ষেত্র নয় এবং আমি নিশ্চিত নই যে এটি ওপি কী সন্ধান করছে)।

যাইহোক এটি আমার কাছে মনে হয় যে উপরের ধারণা / কৌশলগুলি কিছুটা এমবিই (বিশেষত স্ব-সিঙ্ক্রোনাইজিং এনকোডিংগুলি ) এর সাথে মানিয়ে নেওয়া যেতে পারে :

  • জোহানের মন্তব্যে উল্লিখিত হিসাবে হ্যাকটি 'সহজেই' ডাবল বাইট বা যে কোনও কিছুর জন্য কাজ করতে বাড়ানো যেতে পারে (অবশ্যই আপনি এটি খুব বেশি প্রসারিত করতে পারবেন না);
  • একটি সাধারণ ফাংশন যা একটি মাল্টিবাইট অক্ষর স্ট্রিংয়ে একটি অক্ষর সনাক্ত করে:
  • সেন্ডিনেল কৌশলটি একটু দূরদর্শিতার সাথে ব্যবহার করা যেতে পারে।

1
এটি সিমড অপারেশনের দরিদ্র লোক version
রুসলান

@ রুসলান একেবারে! এটি প্রায়শই কার্যকর বিট টুইডলিং হ্যাকের ক্ষেত্রে হয়।
মানিলিও

2
চমৎকার উত্তর. পঠনযোগ্যতার দিক থেকে, আপনি কেন 0x01010101ULএক লাইনে এবং ~0UL / 255পরের অংশে লেখেন তা আমি বুঝতে পারি না । এটি এমন ধারণা দেয় যে তাদের অবশ্যই পৃথক মান হতে হবে, অন্যথায়, কেন এটি দুটি ভিন্ন উপায়ে লিখবেন?
এইচডিভি

3
এটি দুর্দান্ত কারণ এটি একবারে 4 বাইট চেক করে, তবে এর জন্য একাধিক (8?) নির্দেশাবলীর প্রয়োজন, যেহেতু #defineএটিগুলি প্রসারিত হবে ( (((x) ^ (0x01010101UL * (n)))) - 0x01010101UL) & ~((x) ^ (0x01010101UL * (n)))) & 0x80808080UL )। একক বাইট তুলনা দ্রুত হবে না?
জেদ স্কাফ

1
@ ডকব্রাউন, কোডটি সহজেই ডাবল বাইট (অর্থাত অর্ধশব্দ) বা নিবলস বা যে কোনও কিছুর জন্য কাজ করা যায়। (আমি যে ক্যাভ্যাটটি উল্লেখ করেছি তা বিবেচনা করে))
জোহান - মনিকা

20

যে কোনও পাঠ্য অনুসন্ধানের অ্যালগরিদম যা প্রদত্ত পাঠ্যের একক অক্ষরের প্রতিটি ঘটনার জন্য অনুসন্ধান করে সেগুলি পাঠ্যের প্রতিটি অক্ষর কমপক্ষে একবার পড়তে হবে, এটি স্পষ্ট হওয়া উচিত। এবং যেহেতু এটি এককালীন অনুসন্ধানের জন্য যথেষ্ট, এর চেয়ে ভাল আর কোনও অ্যালগরিদম (রান টাইম অর্ডার হিসাবে বিবেচনা করার সময় হতে পারে না, যাকে এই ক্ষেত্রে "রৈখিক" বা ও (এন) বলা হয়, যেখানে এন অক্ষরের সংখ্যা মাধ্যমে অনুসন্ধান করা)।

তবে বাস্তব বাস্তবায়নের জন্য অবশ্যই প্রচুর পরিমাণে মাইক্রো-অপ্টিমাইজেশন সম্ভব, যা রান টাইম অর্ডারকে পুরোপুরি পরিবর্তন করে না, তবে আসল রান সময়কে কমিয়ে দেয়। এবং যদি লক্ষ্যটি কোনও একক চরিত্রের প্রতিটি ঘটনা খুঁজে না পাওয়া, তবে কেবলমাত্র প্রথম, আপনি অবশ্যই প্রথম ঘটনাটি থামাতে পারেন, অবশ্যই। তবুও, সেই ক্ষেত্রে এমনকি সবচেয়ে খারাপ পরিস্থিতিটি এখনও আপনি খুঁজে পেয়েছেন যে চরিত্রটি পাঠ্যের শেষ অক্ষর, তাই এই লক্ষ্যের জন্য সবচেয়ে খারাপ ক্ষেত্রে রান টাইম অর্ডার এখনও ও (এন)।


8

যদি আপনার "খড়ের কাঁটা" একাধিকবার অনুসন্ধান করা হয়, তবে একটি হিস্টগ্রাম ভিত্তিক পদ্ধতি অত্যন্ত দ্রুত গতিতে চলেছে। হিস্টগ্রাম নির্মিত হওয়ার পরে আপনার উত্তরটি খুঁজে পেতে আপনার কেবল পয়েন্টার লুকআপ দরকার।

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

string haystack = "agtuhvrth";
array<int, 256> histogram{0};
for(character: haystack)
     ++histogram[character];

if(histogram['a'])
    // a belongs to haystack

1

যদি আপনাকে এই একই স্ট্রিংয়ের একাধিকবার অক্ষর সন্ধান করতে হয়, তবে একটি সম্ভাব্য পন্থাটি হ'ল স্ট্রিংটিকে ছোট ছোট ভাগে ভাগ করা, সম্ভবত পুনরাবৃত্তভাবে এবং এই প্রতিটি অংশের জন্য ব্লুম ফিল্টার ব্যবহার করা।

যেহেতু একটি ব্লুম ফিল্টার আপনাকে নিশ্চিতভাবে বলতে পারে যে কোনও চরিত্র যদি ফিল্টারটির দ্বারা "প্রতিনিধিত্ব করা" স্ট্রিংয়ের অংশে না থাকে, আপনি অক্ষরগুলি অনুসন্ধান করার সময় কিছু অংশ ছেড়ে যেতে পারেন।

উদাহরণস্বরূপ: নিম্নলিখিত স্ট্রিংয়ের জন্য এটি এটিকে 4 টি ভাগে ভাগ করতে পারে (প্রতিটি 11 টি অক্ষর দীর্ঘ) এবং প্রতিটি অংশের জন্য একটি অংশের ব্লুম ফিল্টার (সম্ভবত 4 বাইট বৃহত্তর) এর অংশের অক্ষরগুলি পূরণ করতে পারে:

The quick brown fox jumps over the lazy dog 
          |          |          |          |

আপনি আপনার অনুসন্ধানকে ত্বরান্বিত করতে পারেন, যেমন চরিত্রটির জন্য a: ব্লুম ফিল্টারগুলির জন্য ভাল হ্যাশ ফাংশন ব্যবহার করে, তারা আপনাকে বলবে - উচ্চ সম্ভাবনার সাথে - আপনাকে প্রথম, দ্বিতীয় বা তৃতীয় অংশের মধ্যে অনুসন্ধান করতে হবে না। সুতরাং আপনি 33 টি অক্ষর পরীক্ষা থেকে নিজেকে বাঁচান এবং পরিবর্তে কেবল 16 বাইট (4 টি ব্লুম ফিল্টারগুলির জন্য) পরীক্ষা করতে হবে। এটি এখনও O(n), কেবলমাত্র একটি ধ্রুবক (ভগ্নাংশ) ফ্যাক্টর সহ (এবং এটি কার্যকর হওয়ার জন্য আপনাকে অনুসন্ধানের অক্ষরের জন্য হ্যাশ ফাংশনগুলি গণনার ওভারহেড হ্রাস করতে হবে) বড় অংশগুলি চয়ন করতে হবে।

পুনরাবৃত্ত হওয়া, গাছের মতো পদ্ধতির ব্যবহার আপনাকে নিকটস্থ করা উচিত O(log n):

The quick brown fox jumps over the lazy dog 
   |   |   |   |   |   |   |   |---|-X-|   |  (1 Byte)
       |       |       |       |---X---|----  (2 Byte)
               |               |-----X------  (3 Byte)
-------------------------------|-----X------  (4 Byte)
---------------------X---------------------|  (5 Byte)

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

5 + 2*4 + 3 + 2*2 + 2*1 bytes

চূড়ান্ত অংশে পেতে (যেখানে একজনের সন্ধানের আগ পর্যন্ত 3 টি অক্ষর পরীক্ষা করা দরকার a)।

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


প্রিয় ডাউনভোটার, দয়া করে আপনি কেন ব্যাখ্যা করেন যে আমার উত্তর সহায়ক নয়।
ড্যানিয়েল জোর

1

যদি স্ট্রিংটি একাধিকবার অনুসন্ধান করা হয় (সাধারণ "অনুসন্ধান" সমস্যা), সমাধান ও (1) হতে পারে। সমাধানটি একটি সূচক তৈরি করা।

যেমন:

মানচিত্র, যেখানে কীটি হ'ল অক্ষর এবং মান হ'ল স্ট্রিংয়ের মধ্যে অক্ষরটির সূচকগুলির একটি তালিকা।

এটির সাহায্যে একক মানচিত্রের অনুসন্ধান উত্তর সরবরাহ করতে পারে।

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