সংলগ্ন অঞ্চলে থাকার জন্য কোনও পূর্ণসংখ্যার 1-বিটগুলির জন্য পরীক্ষা করার কি সুন্দর এবং দ্রুত উপায় আছে?


84

বিট ভ্যালু 1 সহ পজিশনগুলি (32 বিগ পূর্ণসংখ্যার জন্য 0 থেকে 31 পর্যন্ত) একটি সুসংগত অঞ্চল গঠন করে কিনা তা পরীক্ষা করে দেখার দরকার। উদাহরণ স্বরূপ:

00111111000000000000000000000000      is contiguous
00111111000000000000000011000000      is not contiguous

আমি এই পরীক্ষাটি অর্থাৎ কিছু ফাংশন has_contiguous_one_bits(int)পোর্টেবল হতে চাই।

একটি সুস্পষ্ট উপায় হ'ল প্রথম সেট বিটটি খুঁজে পেতে পজিশনের উপর লুপ করা, তারপরে প্রথম অ-সেট বিট এবং আরও কোনও সেট বিট পরীক্ষা করা।

আমি অবাক হই যে এখানে কোনও দ্রুততর পথ আছে কি না? সর্বাধিক এবং সর্বনিম্ন সেট বিটগুলি খুঁজে পাওয়ার জন্য যদি দ্রুত পদ্ধতিগুলি থাকে (তবে এই প্রশ্ন থেকে এটি কোনও বহনযোগ্যযোগ্য নেই) প্রদর্শিত হয়, তবে সম্ভাব্য বাস্তবায়নটি হ'ল

bool has_contiguous_one_bits(int val)
{
    auto h = highest_set_bit(val);
    auto l = lowest_set_bit(val);
    return val == (((1 << (h-l+1))-1)<<l);
}

কেবল মজাদার জন্য, এখানে প্রথম 100 টি সংক্ষিপ্ত বিট সহ পূর্ণসংখ্যা রয়েছে:

0 1 2 3 4 6 7 8 12 14 15 16 24 28 30 31 32 48 56 60 62 63 64 96 112 120 124 126 127 128 192 224 240 248 252 254 255 256 384 448 480 496 504 508 510 511 512 768 896 960 992 1008 1016 1020 1022 1023 1024 1536 1792 1920 1984 2016 2032 2040 2044 2046 2047 2048 3072 3584 3840 3968 4032 4064 4080 4088 4092 4094 4095 4096 6144 7168 7680 7936 8064 8128 8160 8176 8184 8188 8190 8191 8192 12288 14336 15360 15872 16128 16256 16320

এগুলি (অবশ্যই) (1<<m)*(1<<n-1)অ-নেতিবাচক mএবং ফর্মের n


4
@ আফফুলি হ্যাঁ, 0x0কমপ্যাক্ট। বিপরীতটি সংজ্ঞায়িত করা সহজ (কমপ্যাক্ট নয়): যদি ও সেট বিট থাকে তবে তাদের মধ্যে কমপক্ষে একটি আনসেট বিট থাকে।
ওয়াল্টার

4
@ কামিলকুক h>=l(অন্তর্ভুক্ত) কার্যকারিতা দ্বারা highest_set_bit()এবংlowest_set_bit()
ওয়াল্টার


6
ওআইআইএস লিঙ্কটি বলছে যে বাইনারি থাকাকালীন এই সংখ্যাগুলির সংখ্যাগুলি অ-বর্ধনশীল থাকে। তাদের উল্লেখ করার আরেকটি উপায় হ'ল এটির সংলগ্ন (বা সম্ভবত সংযুক্ত)। এই গণিতবিদের কাছে, "কমপ্যাক্ট" এর অর্থ খুব আলাদা কিছু।
টিপিমেম

4
@ টিডিপিম আমি মনে করি যে এই প্রশ্নটি হট নেটওয়ার্ক প্রশ্নে শেষ হয়েছে তার কারণ হ'ল কমপ্যাক্ট শব্দটির এই অপব্যবহারের কারণেই, কেন আমি এটি ক্লিক করেছি: আমি খুব একটা ভাবছিলাম না এবং ভাবছিলাম যে এটি কমপ্যাক্টনেসটি সংজ্ঞায়িত করার অর্থ কীভাবে তৈরি করতে পারে? ঐ দিকে. স্পষ্টতই এর কোনও অর্থ নেই।
কেউ নেই

উত্তর:


146
static _Bool IsCompact(unsigned x)
{
    return (x & x + (x & -x)) == 0;
}

সংক্ষেপে:

x & -xসর্বনিম্ন বিট সেট দেয় x(বা শূন্য যদি xশূন্য হয়)।

x + (x & -x) একটানা 1 এর সর্বনিম্ন স্ট্রিংকে একক 1 তে রূপান্তর করে (বা শূন্যে মোড়কে)।

x & x + (x & -x) এই 1 বিট সাফ করে।

(x & x + (x & -x)) == 0 অন্য 1 টি বিট রয়েছে কিনা তা পরীক্ষা করে।

দীর্ঘতর:

-xসমান ~x+1, দুটির পরিপূরক ব্যবহার করে, যা আমরা ধরে নিই। বিটগুলি ফ্লিপ হওয়ার পরে ~x, 1 টি বহন করে যাতে এটি নিম্ন 1 বিটগুলিতে ফিরে যায় ~xএবং প্রথম 0 বিট কিন্তু তারপরে থামে। সুতরাং, -xএর প্রথম 1 পর্যন্ত কম বিটগুলি নিম্ন বিটগুলির সমান x, তবে সমস্ত উচ্চতর বিটগুলি উল্টানো হয়। (উদাহরণ: ~10011100দেয় 01100011, এবং 1 যোগ করে 01100100, তাই নিম্নগুলি 100একই 10011হয় তবে উচ্চগুলি উল্টানো হয় 01100)) তারপরে x & -xআমাদের উভয়ের মধ্যে 1 বিট দেয় যা সর্বনিম্ন 1 বিট ( 00000100)। (যদি xশূন্য x & -xহয় তবে শূন্য হয়))

এটিকে যুক্ত xকরে টানা 1 সেকেন্ডে 0 কে পরিবর্তিত হয়ে সমস্ত ক্যারি করে to এটি পরের উচ্চতর 0 বিট এ 1 ছাড়বে (বা উচ্চ প্রান্তটি বহন করবে, মোট জিরো শূন্য রেখে) ( 10100000।)

যখন এটি অ্যান্ডড করা হয় x, সেখানে 1 টি স্থানগুলিতে 0s থাকে যেখানে 1 গুলি পরিবর্তিত হয় 0 সেকেন্ডে (এবং এছাড়াও যেখানে বহনটি 0 থেকে 1 তে পরিবর্তিত হয়েছিল)। সুতরাং ফলাফলটি আরও 1 বিট উপরে থাকলে কেবল শূন্য হয় না।


23
হ্যাকারস ডিলাইট বইটি কমপক্ষে কেউ জানেন knows উত্তরের জন্য দয়া করে 2-1 অধ্যায়টি দেখুন। তবে এটি আবারও এসও-তে বেশ কয়েকবার উত্তর দেওয়া হয়েছে। যাইহোক: +1
আর্মিন মন্টিগনি

33
আমি আশা করি আপনি যদি প্রযোজনায় এ জাতীয় কোড লিখেন, আপনি মন্তব্যে ব্যাখ্যাটি অন্তর্ভুক্ত করবেন;)
বহুবৃত্ত

14
এটি x & -xএকক blsiনির্দেশিকায় x86 বিএমআই 1 থেকে দুর্দান্ত উপকার পেয়েছে যা ইন্টেলের 1 আপ এবং এএমডি জেনের 2 টি উওপ। Godbolt.org/z/5zBx-A । তবে BMI1 ছাড়াই @ কেভিনজেডের সংস্করণটি আরও কার্যকর efficient
পিটার কর্ডেস

4
@ টমমিঅ্যান্ডারসন: _Boolসি 2018 6.4.1 ১ অনুযায়ী প্রতি একটি স্ট্যান্ডার্ড কীওয়ার্ড
এরিক পোস্টপিসিল

4
@ ওয়াল্টার: হুম? এই কোড ব্যবহার করে unsigned। আপনি যদি দুজনের পরিপূরক স্বাক্ষরের জন্য পরীক্ষা করতে চান int, তবে সবচেয়ে সহজ উপায় হ'ল এই উত্তরে এটি রুটিনে পাস করা, এটি intরূপান্তরিত হয়ে unsigned। এটি পছন্দসই ফলাফল দেবে। intওভারফ্লো / ক্যারি সমস্যার কারণে সরাসরি স্বাক্ষরিত অবস্থায় অপারেশন শো প্রয়োগ করা সমস্যাযুক্ত হতে পারে। (আপনি যদি কোনও ব্যক্তির পরিপূরক বা সাইন-এন্ড- int
গৌনত্ব

29

আসলে কোনও অভ্যন্তরীণ ব্যবহার করার প্রয়োজন নেই।

প্রথম 0 এর আগে সমস্ত 0 টি ফ্লিপ করুন 1 তারপরে পরীক্ষা করুন যদি নতুন মানটি মের্সেনিন নম্বর হয়। এই আলগোতে, শূন্যটি সত্যতে ম্যাপ করা হয়।

bool has_compact_bits( unsigned const x )
{
    // fill up the low order zeroes
    unsigned const y = x | ( x - 1 );
    // test if the 1's is one solid block
    return not ( y & ( y + 1 ) );
}

অবশ্যই আপনি যদি আন্তঃব্যক্তি ব্যবহার করতে চান তবে এখানে পপকাউন্ট পদ্ধতি রয়েছে:

bool has_compact_bits( unsigned const x )
{
    size_t const num_bits = CHAR_BIT * sizeof(unsigned);
    size_t const sum = __builtin_ctz(x) + __builtin_popcount(x) + __builtin_clz(z);
    return sum == num_bits;
}

4
শোষণ / নির্দেশাবলীর সংকলিত হলে প্রথম সংস্করণটি কেবল 4 টি নির্দেশকে হ্রাস করে । এটি এখন পর্যন্ত প্রস্তাবিত সংক্ষিপ্ততম সংস্করণ হবে। দুর্ভাগ্যক্রমে, প্রায় কোনও প্রসেসর সেই নির্দেশ সেট এক্সটেনশানটিকে সমর্থন করে না-mtbmblsfillblcfill
জিওভান্নি সেরেরতানি

18

আসলে আপনার নেতৃস্থানীয় শূন্যগুলি গণনা করার দরকার নেই। মন্তব্যগুলিতে পিএমজি দ্বারা প্রস্তাবিত হিসাবে, আপনি যে নম্বরগুলি সন্ধান করছেন সেটি হ'ল ক্রম OEIS A023758 , অর্থাত্ i> = j সহ ফর্মের সংখ্যা 2 ^ i - 2 ^ j , আপনি কেবল অনুসরণীয় শূন্যগুলি গণনা করতে পারেন ( অর্থাত্ জে - ১ ), সেই বিটগুলি মূল মানের ( 2 ^ j - 1 যোগ করার সমতুল্য) টগল করুন এবং তারপরে সেই মানটি 2 ^ i - 1 রূপের কিনা তা পরীক্ষা করুন । জিসিসি / ঝনঝন অভ্যন্তরীণ সাথে,

bool has_compact_bits(int val) {
    if (val == 0) return true; // __builtin_ctz undefined if argument is zero
    int j = __builtin_ctz(val) + 1;
    val |= (1 << j) - 1; // add 2^j - 1
    val &= (val + 1); // val set to zero if of the form (2^i - 1)
    return val == 0;
}

এই সংস্করণটি সামান্য দ্রুত আপনার এবং আপনার একটি কামিলকুক প্রস্তাবিত এবং একটি ইউরি ফিল্ডম্যান কেবল পপকাউন্ট সহ with

আপনি সি ++ 20 ব্যবহার করে থাকেন, আপনি প্রতিস্থাপন পোর্টেবল ফাংশন পেতে পারেন __builtin_ctzসঙ্গে std::countr_zero:

#include <bit>

bool has_compact_bits(int val) {
    int j = std::countr_zero(static_cast<unsigned>(val)) + 1; // ugly cast
    val |= (1 << j) - 1; // add 2^j - 1
    val &= (val + 1); // val set to zero if of the form (2^i - 1)
    return val == 0;
}

Castালাই কুরুচিপূর্ণ তবে এটি আপনাকে সতর্ক করে দিচ্ছে যে বিটগুলি পরিচালনা করার সময় স্বাক্ষরবিহীন প্রকারের সাথে কাজ করা ভাল। প্রাক-সি ++ 20 বিকল্পগুলি boost::multiprecision::lsb

সম্পাদনা করুন:

স্ট্রাইকথ্রু লিঙ্কের মানদণ্ডটি এই কারণে সীমাবদ্ধ ছিল যে ইউরি ফিল্ডম্যান সংস্করণের জন্য কোনও পপকাউন্টের নির্দেশিকা নির্গত হয়নি। আমার পিসিতে এগুলি সংকলনের চেষ্টা করে -march=westmere, আমি নিম্নলিখিত সময়টি 1 বিলিয়ন পুনরাবৃত্তির জন্য অভিন্ন ক্রমগুলি সহ পরিমাপ করেছি std::mt19937:

  • আপনার সংস্করণ: 5.7 এস
  • কামিলকুকের দ্বিতীয় সংস্করণ: 4.7 এস
  • আমার সংস্করণ: 4.7 এস
  • এরিক পোস্টপিসিলের প্রথম সংস্করণ: 4.3 এস
  • ইউরি ফিল্ডম্যানের সংস্করণ (স্পষ্টভাবে ব্যবহার করা __builtin_popcount): ৪.১ এস

সুতরাং, কমপক্ষে আমার আর্কিটেকচারে, দ্রুততমটি পপকাউন্ট সহ এক হিসাবে মনে হচ্ছে।

সম্পাদনা 2:

আমি নতুন এরিক পোস্টপিসিল সংস্করণ দিয়ে আমার মানদণ্ড আপডেট করেছি। মন্তব্যে অনুরোধ করা হয়েছে, আমার পরীক্ষার কোড এখানে পাওয়া যাবে । আমি পিআরএনজি দ্বারা প্রয়োজনীয় সময়টি অনুমান করার জন্য কোনও অপ-লুপ যুক্ত করেছি। আমি কেভিনজেড দ্বারা দুটি সংস্করণ যুক্ত করেছি। কোডটি -O3 -msse4 -mbmiপেতে popcntএবং blsiনির্দেশ দেওয়ার জন্য ঝাঁকুনিতে সংকলিত হয়েছে (পিটার কর্ডসকে ধন্যবাদ)।

ফলাফল: কমপক্ষে আমার আর্কিটেকচারে, এরিক পোস্টপিসিলের সংস্করণটি ইউরি ফিল্ডম্যানের একটি হিসাবে ঠিক তত দ্রুত এবং এ পর্যন্ত প্রস্তাবিত অন্য সংস্করণের চেয়ে কমপক্ষে দ্বিগুণ দ্রুত faster


আমি একটি অপারেশন সরানো হয়েছে: return (x & x + (x & -x)) == 0;
এরিক পোস্টপিসিল

4
এটি @ এরিকের সংস্করণটির একটি পুরানো সংস্করণ বেঞ্চমার্ক করছে, তাই না? বর্তমান সংস্করণ সহ, এরিক gcc -O3 -march=nehalemকমপক্ষে কয়েকটি নির্দেশাবলী (পপসেন্ট সরবরাহ করতে), বা BMI1 এরblsi জন্য উপলভ্য হলে এর চেয়ে কম সংকলন করেছেন x & -x: Godbolt.org/z/zuyj_f । এবং নির্দেশাবলী হ'ল popcntইউরির সংস্করণে 3 টি চক্রের বিলম্ব রয়েছে কেবল ব্যতীত সমস্ত সাধারণ একক-উওপ । (তবে আমি ধরে নিই যে আপনি থ্রুপুট বেঞ্চ করছেন)) আমিও ধরে নিয়েছি আপনি অবশ্যই and valইউরির কাছ থেকে অপসারণ করেছেন বা এটি ধীর হবে।
পিটার কর্ডস

4
এছাড়াও, আপনি কোন হার্ডওয়্যারটি বেঞ্চমার্ক করেছিলেন? গডবোল্ট বা কোনও কিছুর উপরে আপনার পূর্ণ মানদণ্ডের কোডটি লিঙ্ক করা ভাল ধারণা হবে, তাই ভবিষ্যতের পাঠকরা সহজেই তাদের সি ++ বাস্তবায়ন পরীক্ষা করতে পারেন।
পিটার কর্ডেস

4
আপনার @ কেভিনজেডের সংস্করণও পরীক্ষা করা উচিত; এটা (অন্তত ঝনঝন সঙ্গে; জিসিসি এর অ inlined সংস্করণ বর্জ্য একটি BMI1 ছাড়া এমনকি তার চেয়ে কম নির্দেশাবলী প্রনয়ন movএবং সুবিধা গ্রহণ করতে ব্যর্থ হয় lea): godbolt.org/z/5jeQLQ । BMI1 এর সাথে এরিকের সংস্করণটি x86-64-তে আরও ভাল, কমপক্ষে ইন্টেলের যেখানে blsiএকটি একক উওপ, তবে এটি এএমডি-তে 2 উফ।
পিটার কর্ডেস

15

দ্রুত সম্পর্কে নিশ্চিত না, তবে যা val^(val>>1)যা বেশিরভাগ 2 বিট চালু আছে তা যাচাই করে একটি ওয়ান-লাইনার করতে পারে ।

এটি কেবল স্বাক্ষরবিহীন প্রকারের সাথেই কাজ করে: 0শীর্ষে একটি লজিকাল শিফট স্থানান্তর করা প্রয়োজনীয়, সাইন বিটের অনুলিপিটিতে স্থানান্তরিত কোনও গাণিতিক রাইট শিফট নয়।

#include <bitset>
bool has_compact_bits(unsigned val)
{
    return std::bitset<8*sizeof(val)>((val ^ (val>>1))).count() <= 2;
}

প্রত্যাখ্যান করতে 0(অর্থাত্ কেবলমাত্র 1 টি বিট-গ্রুপ রয়েছে এমন ইনপুটগুলি গ্রহণ করুন), যৌক্তিক- এবং valঅ-শূন্য নয়। এই প্রশ্নের অন্যান্য উত্তরগুলি 0কমপ্যাক্ট হিসাবে স্বীকার করে ।

bool has_compact_bits(unsigned val)
{
    return std::bitset<8*sizeof(val)>((val ^ (val>>1))).count() <= 2 and val;
}

সি ++ বহনযোগ্যভাবে পপকাউন্টের মাধ্যমে std::bitset::count()বা সি ++ 20 এর মাধ্যমে প্রকাশ করেstd::popcount । সি এর কাছে এখনও একটি বহনযোগ্য উপায় নেই যা লক্ষ্যমাত্রায় যেখানে কোনও উপলব্ধ সেখানে নির্ভরযোগ্যভাবে পপকন্ট বা অনুরূপ নির্দেশের সাথে সংকলন করে।


4
এছাড়াও এখন পর্যন্ত সবচেয়ে দ্রুত।
জিওভান্নি সেরেরতানি

4
আমি মনে করি আপনি সাইন বিটের অনুলিপি নয়, শূন্যে স্থানান্তরিত হবেন তা নিশ্চিত করার জন্য আপনাকে একটি স্বাক্ষরবিহীন প্রকারের প্রয়োজন। বিবেচনা করুন 11011111। পাটিগণিত ডান স্থানান্তরিত, এটি হয়ে যায় 11101111, এবং এক্সওআর হয় 00110000। লজিকাল ডান শিফট ( 0শীর্ষে একটি স্থানান্তরিত ) এর সাহায্যে আপনি 10110000একাধিক বিট-গ্রুপ পেয়েছেন এবং সঠিকভাবে সনাক্ত করতে পারেন । এটি ঠিক করার জন্য সম্পাদনা করা হচ্ছে।
পিটার কর্ডেস

4
এটা সত্যিই চালাক। আমি যতটা স্টাইল পছন্দ করি না (আইএমও কেবল ব্যবহার করি __builtin_popcount(), প্রতিটি সংকলকের আজকের মতো আদিম রয়েছে), এটি এখন পর্যন্ত দ্রুততম (আধুনিক সিপিইউতে)। আসলে, আমি যুক্তি দিতে যাচ্ছি যে উপস্থাপনাটি গুরুত্ব সহকারে গুরুত্বপূর্ণ, কারণ একটি সিপিইউতে যে একক নির্দেশ হিসাবে পিওপিসিএনটি নেই, আমার বাস্তবায়ন এটিকে পরাস্ত করতে পারে। অতএব, আপনি যদি এই বাস্তবায়ন ব্যবহার করতে চলেছেন তবে আপনার কেবল অভ্যন্তরীণ ব্যবহার করা উচিত। std::bitsetএকটি ভয়ঙ্কর ইন্টারফেস আছে।
কেভিনজেড

9

সিপিইউগুলির পক্ষে এটির জন্য নিবেদিত নির্দেশ রয়েছে fast পিসিতে তারা বিএসআর / বিএসএফ (1985 সালে 80386 এ প্রবর্তিত), এআরএম এ তারা সিএলজেড / সিটিজেড

কমপক্ষে উল্লেখযোগ্য সেট বিটের সূচি খুঁজে পেতে একটি ব্যবহার করুন, সেই পরিমাণ দিয়ে ডানদিকে পূর্ণ শিখুন। সর্বাধিক উল্লেখযোগ্য সেট বিটের একটি সূচক খুঁজতে অন্যটি ব্যবহার করুন, আপনার পূর্ণসংখ্যার (1u << (বিএসআর + 1)) - 1 এর সাথে তুলনা করুন।

দুর্ভাগ্যক্রমে, 35 বছর হার্ডওয়ারের সাথে মেলে সি ++ ভাষা আপডেট করার পক্ষে পর্যাপ্ত ছিল না। সি ++ থেকে এই নির্দেশাবলী ব্যবহার করতে আপনার অভ্যন্তরীণ প্রয়োজন, এগুলি পোর্টেবল নয়, এবং ফলাফলটি কিছুটা আলাদা ফর্ম্যাটে ফিরে আসে। সংকলকটি #ifdefসনাক্ত করতে প্রিপ্রসেসর ইত্যাদি ব্যবহার করুন এবং তারপরে যথাযথ ইন্টার্নিক্স ব্যবহার করুন। MSVC তারা হয় _BitScanForward, _BitScanForward64, _BitScanReverse, _BitScanReverse64। জিসিসি এবং ঝনঝনিতে তারা __builtin_clzএবং __builtin_ctz


4
@ e2-e4 ভিজুয়াল স্টুডিও এএমডি 64 এর জন্য সংকলন করার সময় ইনলাইন অ্যাসেমব্লিকে সমর্থন করে না। এজন্যই আমি আন্তঃনীতি প্রস্তাব করি।
সূচনা

4
যেহেতু সি ++ 20 রয়েছে std::countr_zeroএবং আছে std::countl_zero। আপনি বুস্ট ব্যবহার করছেন এমন ক্ষেত্রে এটিতে পোর্টেবল র‍্যাপারস কল রয়েছে boost::multiprecision::lsbএবং boost::multiprecision::msb
জিওভান্নি সেরের্তানি

8
এটি আমার প্রশ্নের মোটেও উত্তর দেয় না - আমি কেন ভাবছি কেন এটির কোনও অগ্রগতি হয়েছিল
ওয়াল্টার

4
@ ওয়াল্টার আপনার কি অর্থ "উত্তর দেয় না"? আপনার কী করা উচিত তা আমি স্পষ্টভাবে উত্তর দিয়েছি, প্রিপ্রসেসর ব্যবহার করুন এবং তারপরে অভ্যন্তরীণ জিনিসগুলি।
শীঘ্রই

4
দৃশ্যত সি ++ 20 অবশেষে বিট-স্ক্যান, পপকাউন্ট, এবং ঘোরানোর সাথে #include <bit> en.cppreferences.com/w/cpp/header/bit যুক্ত করছে । এটি অত্যন্ত করুণ বিষয় যে এটি বিট-স্ক্যানের বহনযোগ্যভাবে বহন করতে দীর্ঘ সময় নিয়েছিল, তবে এখন আর আগের চেয়ে ভাল। (পোর্টেবল popcnt প্রাপ্তিসাধ্য মাধ্যমে হয়েছে std::bitset::count()।) সি ++ 20 এখনও কিছু বিষয় আছে যা মরচে প্রদান করে (অনুপস্থিত doc.rust-lang.org/std/primitive.i32.html ), যেমন বিট-বিপরীত এবং endian যা কিছু সিপিইউ দক্ষতার প্রদান কিন্তু সব না. কোনও সিপিইউর কিছু অপারেশন করার জন্য একটি বহনযোগ্য বিল্টিন অন্তর্ভুক্ত যা ব্যবহারকারীদের দ্রুত কী তা জানতে হবে।
পিটার কর্ডেস

7

এর পরিবর্তে শূন্যগুলির সাথে তুলনা করা কিছু ক্রিয়াকলাপ সংরক্ষণ করবে:

bool has_compact_bits2(int val) {
    if (val == 0) return true;
    int h = __builtin_clz(val);
    // Clear bits to the left
    val = (unsigned)val << h;
    int l = __builtin_ctz(val);
    // Invert
    // >>l - Clear bits to the right
    return (~(unsigned)val)>>l == 0;
}

উপরের gcc10 -O3x86_64 এ সাইন এক্সটেনশনে ব্যবহার করে উপরের নির্দেশাবলীতে নিম্নলিখিত ফলাফলগুলি কম :

bool has_compact_bits3(int val) {
    if (val == 0) return true;
    int h = __builtin_clz(val);
    val <<= h;
    int l = __builtin_ctz(val);
    return ~(val>>l) == 0;
}

গডবোল্টে পরীক্ষিত ।


দুর্ভাগ্যক্রমে, এটি কোনও বহনযোগ্য নয়। আমি সর্বদা ভয় করি যে আমি এই শিফট অপারেটরগুলির সাথে অপারেটরটির নজরে ভুল পেয়েছি - আপনি কি নিশ্চিত ~val<<h>>h>>l == 0যে এটি যা করেন তা করেন?
ওয়াল্টার

4
হ্যাঁ, আমি নিশ্চিত, সম্পাদিত এবং যাইহোক বন্ধনীগুলি যুক্ত। ওহ, সুতরাং আপনি একটি পোর্টেবল সমাধান আগ্রহী? কারণ আমি কিছু দেখেছি there exists a faster way?এবং ধরেই নিয়েছি ।
কামিলকুক

5

আপনি প্রয়োজনীয়তা পুনঃব্যবস্থা করতে পারেন:

  • আগের বিটের চেয়ে বিটের সংখ্যা এন সেট করুন (বিটের মাধ্যমে পুনরাবৃত্তি করে)
  • যদি এন = 2 এবং প্রথম বা শেষ বিট 0 হয় তবে উত্তর হ্যাঁ
  • যদি এন = 1 হয় তবে উত্তর হ্যাঁ (কারণ সমস্ত 1 টি একদিকে রয়েছে)
  • যদি N = 0 হয় এবং যদি কোনও বিট 0 হয় তবে আপনার কোনও উত্তর নেই 1 যদি আপনি উত্তরটি হ্যাঁ বা না বলে বিবেচনা করেন
  • অন্য কিছু: উত্তর না হয়

সমস্ত বিট দিয়ে যেতে এই দেখতে পারে:

unsigned int count_bit_changes (uint32_t value) {
  unsigned int bit;
  unsigned int changes = 0;
  uint32_t last_bit = value & 1;
  for (bit = 1; bit < 32; bit++) {
    value = value >> 1;
    if (value & 1 != last_bit  {
      changes++;
      last_bit = value & 1;
    }
  }
  return changes;
}

কিন্তু এই নিশ্চয় অপ্টিমাইজ করা যেতে পারে (গর্ভপাত দ্বারা যেমন forলুপ যখন valueপৌঁছে 0যা মান আর উল্লেখযোগ্য বিট মানে 1 উপস্থিত)।


3

আপনি গণনার এই ক্রমটি করতে পারেন ( valএকটি ইনপুট হিসাবে ধরে নেওয়া ):

uint32_t x = val;
x |= x >>  1;
x |= x >>  2;
x |= x >>  4;
x |= x >>  8;
x |= x >> 16;

সবচেয়ে উল্লেখযোগ্য 1ভরাট নীচে সমস্ত শূন্যের সাথে একটি নম্বর পেতে ।

আপনি y = val & -valঅন্তত উল্লেখযোগ্য 1 বিট ইন val(উদাহরণস্বরূপ, 7 & -7 == 1এবং 12 & -12 == 4) বাদে সমস্ত স্ট্রিপ করতে গণনা করতে পারেন ।
সতর্কতা: এটি ব্যর্থ হবে val == INT_MIN, সুতরাং আপনাকে এই কেসটি আলাদাভাবে পরিচালনা করতে হবে, তবে এটি তাত্ক্ষণিক।

তারপরে yআসল এলএসবি থেকে কিছুটা নিচে নামার জন্য valএবং এক অবস্থানের সাহায্যে ডান-শিফট করুন x:

uint32_t y = (val & -val) >> 1;
y |= y >>  1;
y |= y >>  2;
y |= y >>  4;
y |= y >>  8;
y |= y >> 16;

তারপর x - yবা x & ~yবা x ^ y'কম্প্যাক্ট' বিট মাস্ক পুরো দৈর্ঘ্য spanning উত্পাদন করে val। 'কমপ্যাক্ট' valকিনা valতা দেখতে কেবল এটির সাথে তুলনা করুন ।


2

আমরা জিসিসি অন্তর্নির্মিত নির্দেশাবলীর সাহায্যে তা পরীক্ষা করে দেখতে পারি কিনা:

সেট বিটের গণনা

int __builtin_popcount (স্বাক্ষরযুক্ত স্বাক্ষরিত x) x- এ
1-বিটের সংখ্যা প্রদান করে।

এর সমান (ক - খ):

a : সর্বাধিক সেট বিটের সূচি (32 - সিটিজেড) (32 কারণ স্বাক্ষরবিহীন পূর্ণসংখ্যায় 32 বিট)।

int __builtin_clz (স্বাক্ষরযুক্ত স্বাক্ষরিত x)
x সর্বাধিক তাৎপর্যপূর্ণ বিট অবস্থানে শুরু করে এক্স-এর শীর্ষস্থানীয় 0-বিটের সংখ্যা প্রদান করে Return X যদি 0 হয় তবে ফলাফলটি অপরিজ্ঞাত।

বি : সর্বনিম্ন সেট বিটের সূচি (সিএলজেড):

int __builtin_clz (স্বাক্ষরযুক্ত স্বাক্ষরিত x)
x সর্বাধিক তাৎপর্যপূর্ণ বিট অবস্থানে শুরু করে এক্স-এর শীর্ষস্থানীয় 0-বিটের সংখ্যা প্রদান করে Return X যদি 0 হয় তবে ফলাফলটি অপরিজ্ঞাত।

উদাহরণস্বরূপ যদি n = 0b0001100110; আমরা পপকাউন্ট সহ 4 পাব তবে সূচকের পার্থক্য (ক - খ) 6 এ ফিরে আসবে।

যা এ হিসাবে লেখা যেতে পারে:

আমি মনে করি না যে এটি বর্তমানের সর্বাধিক উত্সাহিত উত্তরের চেয়ে আরও মার্জিত বা দক্ষ:

নিম্নলিখিত সমাবেশ সহ:

mov     eax, edi
neg     eax
and     eax, edi
add     eax, edi
test    eax, edi
sete    al

তবে এটি সম্ভবত বোঝা সহজ।


1

ঠিক আছে, এখানে এমন একটি সংস্করণ যা বিটগুলির উপরে আছড়ে পড়ে

template<typename Integer>
inline constexpr bool has_compact_bits(Integer val) noexcept
{
    Integer test = 1;
    while(!(test & val) && test) test<<=1; // skip unset bits to find first set bit
    while( (test & val) && test) test<<=1; // skip set bits to find next unset bit
    while(!(test & val) && test) test<<=1; // skip unset bits to find an offending set bit
    return !test;
}

প্রথম দুটি লুপগুলি প্রথম কমপ্যাক্ট অঞ্চলটি খুঁজে পেয়েছিল। চূড়ান্ত লুপটি এই অঞ্চলটির বাইরে অন্য কোনও সেট বিট আছে কিনা তা পরীক্ষা করে।

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