সি-তে পূর্ণসংখ্যার সর্বোচ্চ সেট বিট (এমএসবি) সন্ধান করার দ্রুত / সবচেয়ে কার্যকর উপায় কী?


119

যদি আমার কিছু পূর্ণসংখ্যা এন থাকে এবং আমি সর্বাধিক তাৎপর্যপূর্ণ বিটের অবস্থান জানতে চাই (অর্থাৎ যদি কমপক্ষে উল্লেখযোগ্য বিটটি ডানদিকে থাকে তবে আমি দীর্ঘতম বাম বিটের অবস্থানটি জানতে চাই যে এটি 1), দ্রুত / সবচেয়ে কার্যকর পদ্ধতিটি খুঁজে বের করার উপায় কী?

আমি জানি যে পসিক্স ffs()প্রথম সেট বিটটি সন্ধান করার জন্য স্ট্রিংস.-তে একটি পদ্ধতি সমর্থন করে তবে এর সাথে সম্পর্কিত fls()পদ্ধতি বলে মনে হয় না ।

আমি অনুপস্থিত এটি করার কিছু সত্যই উপায় আছে?

আপনি যখন পোর্টিবিলিটির জন্য পসিক্স ফাংশন ব্যবহার করতে পারবেন না এমন ক্ষেত্রে কী হবে?

সম্পাদনা করুন: 32 এবং 64 বিট উভয় আর্কিটেকচারের ক্ষেত্রে যে সমাধানটি কার্যকর হয় সে সম্পর্কে কী (কোড লিস্টের অনেকগুলি মনে হয় যে তারা কেবল 32 বিট ইন্টে কাজ করবে)।


এখানে কয়েকটি বাস্তবায়ন রয়েছে: গ্রাফিক্স.স্যানফোর্ড.এডু / সিসেন্ডার / বিট্যাকস.ইচটিএমএল_জিরোসনরাইটলাইনার (সম্পাদনা: আপনার প্রশ্নটি পুনরায় পড়ার পরে, আমি বুঝতে পেরেছি যে উপরের লিঙ্কটি আপনার প্রয়োজন মতো ডানদিকের সেট বিটটি অনুসন্ধানের জন্য, যদিও ছাড়াই শব্দের আকারের অনুভূতি, এটি উত্তর দেওয়া খুব কঠিন)
ব্যয়কারী


এটি ডানদিকে শূন্যগুলি গণনা করে ; প্রশ্নটি ছিল বামে শূন্য সম্পর্কে। কমপক্ষে, দ্রুত স্কিমে আমি এটি সেখানে দেখছি না।
দারিয়াস বেকন

2
আপনি কি বিশেষত বিট নম্বর 'এন' চান, বা 2 ^ n যথেষ্ট হবে?
Alnitak

1
"লগ বেস 2" অ্যালগরিদমগুলি দেখুন - যেমন অ্যান্ডারসন নিবন্ধে বলেছেন: "একটি পূর্ণসংখ্যার লগ বেস 2 সর্বোচ্চ বিট সেট (বা সবচেয়ে গুরুত্বপূর্ণ বিট সেট, এমএসবি) এর অবস্থানের মতো"
মাইকেল বারার

উত্তর:


64

জিসিসির রয়েছে :

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

 - অন্তর্নির্মিত ফাংশন: ইন __ বিল্টিন_ক্লিজএল (স্বাক্ষরযুক্ত দীর্ঘ নয়)
     আর্গুমেন্ট প্রকারটি `স্বাক্ষরবিহীন বাদে` __builtin_clz 'এর মতো
     দীর্ঘ।

 - অন্তর্নির্মিত ফাংশন: ইন __ বিল্টিন_ক্লজল (স্বাক্ষরবিহীন দীর্ঘ দীর্ঘ)
     আর্গুমেন্ট প্রকারটি `স্বাক্ষরবিহীন বাদে` __builtin_clz 'এর মতো
     দীর্ঘ দীর্ঘ'.

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


একটি দরকারী কৌতুক আপনার ইনপুট যদি পারেন হতে শূন্য হয় __builtin_clz(x | 1): নিঃশর্তভাবে কোনো অন্যদের পরিবর্তন ছাড়া কম বিট সেট আউটপুট তোলে 31জন্য x=0, অন্য কোন ইনপুট জন্য আউটপুট পরিবর্তন না করে।

এটি করার প্রয়োজন এড়াতে আপনার অন্য বিকল্পটি প্ল্যাটফর্ম-নির্দিষ্ট অন্তর্নিহিতগুলি যেমন এআরএম জিসিসির __clz(কোনও শিরোনামের প্রয়োজন নেই), বা _lzcnt_u32নির্দেশকে সমর্থনকারী সিপিইউগুলিতে x86 এর lzcnt। (সাবধান যে lzcntযেমন decodes bsrযা নন-জিরো ইনপুট জন্য 31 lzcnt দেয় পরিবর্তে ফল্টিং পুরোনো সিপিইউ, উপর।)

দুর্ভাগ্যক্রমে x-non86 প্ল্যাটফর্মগুলিতে বিবিধভাবে বিভিন্ন সিএলজেড নির্দেশনার সুবিধা নেওয়ার কোনও উপায় নেই যা ইনপুট = 0 বা 32 বা 64 হিসাবে অপারেন্ডের প্রস্থ অনুযায়ী ফলাফল নির্ধারণ করে। x86 গুলি lzcntএটিও করে, যখন bsrএকটি বিট-ইনডেক্স তৈরি করে যা আপনি ব্যবহার না করে কম্পাইলারটি ফ্লিপ করতে হয় 31-__builtin_clz(x)

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



1
অপরিজ্ঞাত-অন-শূন্য আচরণ এলএজডিসিএনটি উপলভ্য না হওয়া সত্ত্বেও, তাদের x86-এ একটি বিএসআর নির্দেশিকায় সংকলন করতে দেয়। এটি __builtin_ctzওভারের জন্য একটি বড় সুবিধা ffs, যা একটি বিএসএফ এবং সিএমওভকে ইনপুট-শূন্যের কেসটি পরিচালনা করতে সংকলিত করে। সংক্ষিপ্ত-পর্যাপ্ত বাস্তবায়ন ছাড়াই আর্কিটেকচারে (উদাহরণস্বরূপ clzনির্দেশ ব্যতীত পুরানো এআরএম ), জিসিসি একটি libgcc সহায়ক ফাংশনে একটি কল প্রেরণ করে।
পিটার কর্ডস

41

ধরে নিই যে আপনি x86 এবং গেমটিতে কিছুটা ইনলাইন এসেম্বেবলারের জন্য, ইনটেল একটি BSRনির্দেশনা সরবরাহ করে ("বিট স্ক্যান রিভার্স")। এটি কয়েকটি এক্স 86 এর উপর দ্রুত (অন্যদের উপর মাইক্রোকডযুক্ত)। ম্যানুয়াল থেকে:

সর্বাধিক তাৎপর্যপূর্ণ সেট বিট (1 বিট) এর জন্য উত্স অপারেন্ড অনুসন্ধান করে। যদি কোনও উল্লেখযোগ্য 1 বিট পাওয়া যায়, তবে এর বিট সূচকটি গন্তব্য অপারেণ্ডে সঞ্চিত থাকে। উত্স অপারেন্ড একটি রেজিস্টার বা একটি মেমরি অবস্থান হতে পারে; গন্তব্য অপারেন্ড একটি রেজিস্টার। বিট সূচকটি সোর্স অপারেন্ডের বিট 0 থেকে স্বাক্ষরবিহীন অফসেট। যদি সামগ্রীর উত্স অপারেন্ড 0 হয় তবে গন্তব্য অপারেন্ডের সামগ্রী অপরিজ্ঞাত।

(আপনি যদি পাওয়ারপিসিতে থাকেন তবে অনুরূপ একটি নির্দেশ রয়েছে cntlz("শীর্ষস্থানীয় জিরো গণনা করুন") inst

জিসিসির উদাহরণ কোড:

#include <iostream>

int main (int,char**)
{
  int n=1;
  for (;;++n) {
    int msb;
    asm("bsrl %1,%0" : "=r"(msb) : "r"(n));
    std::cout << n << " : " << msb << std::endl;
  }
  return 0;
}

এই ইনলাইন এসেম্বলারের টিউটোরিয়ালটিও দেখুন , যা দেখায় (বিভাগ 9.4) এটি লুপিং কোডের চেয়ে বেশ দ্রুত গতিযুক্ত।


4
আসলে এই নির্দেশটি সাধারণত একটি লুপে মাইক্রোকোড হয় এবং এটি ধীর হয় slow
rlbond

2
কোনটি ? বিএসআর নাকি সিএনটিএলজেড? উপরে উল্লিখিত x86- টাইমিং.পিডিএফ পড়ার পরে, বিএসআর নেটবার্স্ট পেন্টিয়ামগুলিতে কেবল ধীর গতিতে রয়েছে। আমি পাওয়ারপিসি সম্পর্কে কিছুই জানি না।
টিমডে

5
... ঠিক আছে, কাছাকাছি পরিদর্শন করার পরে নিশ্চিত করুন যে "BSR কেবলমাত্র P3 / পেন্টিয়াম-এম / কোর 2 x86 এর উপর দ্রুত"। নেটবার্স্ট এবং এএমডি-তে ধীরে ধীরে।
তমদিন

1
মাত্র একটি মাথা আপ: আপনার শেষ দুটি লিঙ্ক মারা গেছে।
বাউম মিট অউজেন

2
@ আরলবন্ড: হু, পি 4 প্রেসকটের বিএসআর প্রতি 4 গ থ্রুটপুট এক জনের সাথে 16 টি চক্র ল্যাটেন্সি (!) সহ 2 টি উওপ। তবে আগের নেটবার্স্টে এটি কেবল 4 টি চক্রের বিলম্ব (এখনও 2 উপ), এবং প্রতি 2 গ থ্রুটপুট প্রতি এক one (উত্স: agner.org/optimize )। বেশিরভাগ সিপিইউতে এটির আউটপুটটির উপরও নির্ভরতা থাকে যা জিসিসি অ্যাকাউন্ট দেয় না (যখন ইনপুট শূন্য হয়, আসল আচরণটি গন্তব্যটি অপরিবর্তিত রেখে দেয়)। এটি স্ট্যাকওভারফ্লো :: প্রশ্নগুলি / 25050285/ … এর মতো সমস্যার সৃষ্টি করতে পারে । আইডিকে ঠিক করে দেওয়ার সময় কেন জিসিসি বিএসআর মিস করল।
পিটার কর্ডেস

38

যেহেতু 2 ^ N কেবলমাত্র Nth বিট সেট (1 << এন) সহ একটি পূর্ণসংখ্যা, তাই সর্বোচ্চ সেট বিটের অবস্থান (N) সন্ধান করা হল সেই পূর্ণসংখ্যার পূর্ণসংখ্যার লগ বেস 2।

http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogObvious

unsigned int v;
unsigned r = 0;

while (v >>= 1) {
    r++;
}

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

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

দ্রষ্টব্য: -৪-বিট মান ব্যবহার করার সময় অতিরিক্ত চৌকস অ্যালগরিদম ব্যবহার সম্পর্কে অত্যন্ত সতর্ক থাকুন; তাদের মধ্যে অনেকগুলি কেবল 32-বিট মানগুলির জন্য সঠিকভাবে কাজ করে।


2
@ জোহান একটি ডিবাগারের সাথে পদক্ষেপ নেওয়া লুপটি কেন প্রস্থান করে তা ব্যাখ্যা করতে সহায়তা করতে পারে। মূলত, এর 'কারণ শর্তে প্রকাশটি 0-এ পর্যালোচনা করে (যা মিথ্যা হিসাবে বিবেচিত হয়) একবার একবার 1 বিটটি ডানদিকে সরিয়ে নিয়ে যাওয়ার পরে।
কুইন টেলর

2
শেষ ফলাফলটি এর মতো ব্যবহার করার জন্য দুর্দান্ত ধারণা :)
জোহান

6
দ্রষ্টব্য: স্বাক্ষরিত অবশ্যই হবে, স্বাক্ষরিত পূর্ণসংখ্যার জন্য ডান শিফট নেতিবাচক সংখ্যার জন্য ব্যর্থ হয়।
Xantix

2
জ্যান্টিক্স: সি / সি ++ এ স্থানান্তরটি একটি লজিকাল শিফট, তাই এটি দুর্দান্ত কাজ করে। জাভা, জাভাস্ক্রিপ্ট বা ডি এর জন্য আপনার লজিকাল শিফট অপারেটরটি ব্যবহার করতে হবে >>>। প্লাস সম্ভবত তুলনাকারী != 0, এবং কিছু বন্ধুত্বপূর্ণ সংখ্যা।
চেজ

8
@ ফেজ: না তা নয়। এটি স্বাক্ষরবিহীন জন্য একটি লজিকাল শিফট । জন্য সাইন ইন , এটা বা না পারে একটি লজিক্যাল শিফট হতে (এবং এটি সাধারণত গাণিতিক আছে, আসলে)।
টিম Čas

17

এটি দ্রুত বজ্রপাত হওয়া উচিত:

int msb(unsigned int v) {
  static const int pos[32] = {0, 1, 28, 2, 29, 14, 24, 3,
    30, 22, 20, 15, 25, 17, 4, 8, 31, 27, 13, 23, 21, 19,
    16, 7, 26, 12, 18, 6, 11, 5, 10, 9};
  v |= v >> 1;
  v |= v >> 2;
  v |= v >> 4;
  v |= v >> 8;
  v |= v >> 16;
  v = (v >> 1) + 1;
  return pos[(v * 0x077CB531UL) >> 27];
}

25
7 বিট শিফট, 5 বা নির্দেশাবলী, একটি বহুগুণ এবং একটি সম্ভাব্য ক্যাশে মিস। :) আপনি কি এটি বেঞ্চমার্ক করেছেন, বা উত্পন্ন এসেমব্লারের দিকে চেয়েছেন? এটা তোলে পারে বেশ ধীর শেষ, কিভাবে এটা অনেক কম্পাইলার বাদ দিতে পারে উপর নির্ভর করে।
জাল্ফ

5
আমি এখানে নতুন. আমি নেতিবাচক ভোট বলছি না। আমি উত্স কোড সহ একমাত্র উত্তর প্রদান করেছি যা আসলে কাজ করে।
নায়ক

9
"সম্ভাব্য ক্যাশে মিস" সম্ভবত এই কোডটির জন্য তার সন্ধানের টেবিলটিতে অ্যাক্সেসের প্রয়োজন রয়েছে due এটি বলা হলে সেই টেবিলটি যদি ক্যাশে না করা হয় তবে এটি আনার সময় একটি স্টল থাকবে। এটি LUT ব্যবহার না করে সমাধানগুলির চেয়ে সবচেয়ে খারাপের পারফরম্যান্সকে আরও খারাপ করতে পারে।
বিনোদন

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

6
সারণীতে 32 টি প্রবেশ রয়েছে এবং প্রতিটি মান <255 (127), সুতরাং সারণিটি স্বাক্ষরবিহীন চর হিসাবে টাইপ করুন এবং এটি একটি 32 বাইট এল 1 ক্যাশে লাইনে ফিট হবে। এবং পুরো জিনিসটি দুটি ক্যাশে লাইনে ফিট করে।
চককট্রিল

16

এটি এক ধরণের পূর্ণসংখ্যার লগ অনুসন্ধান করার মতো। বিট-টুইডলিং কৌশল আছে তবে আমি এটির জন্য আমার নিজের সরঞ্জাম তৈরি করেছি। অবশ্যই লক্ষ্য গতি জন্য।

আমার উপলব্ধিটি হ'ল সিপিইউতে ইতিমধ্যে একটি স্বয়ংক্রিয় বিট-ডিটেক্টর রয়েছে, এটি রূপান্তর করতে ভাসা সংখ্যার জন্য ব্যবহৃত হয়! সুতরাং যে ব্যবহার।

double ff=(double)(v|1);
return ((*(1+(uint32_t *)&ff))>>20)-1023;  // assumes x86 endianness

এই সংস্করণটি মানটিকে দ্বিগুণ করে তোলে, তারপরে খুনিটি পাঠ করে, যা আপনাকে জানায় যে বিটটি ছিল। অভিনব শিফট এবং বিয়োগফলটি আইইইই মান থেকে সঠিক অংশগুলি বের করা ract

এটি ফ্লোটগুলি ব্যবহার করতে কিছুটা দ্রুত, তবে একটি ছোট ফ্ল্যাট তার ছোট যথাযথতার কারণে আপনাকে কেবল প্রথম 24 বিট পজিশন দিতে পারে।


এটি নিরাপদে করতে, সি ++ বা সি তে অপরিজ্ঞাত আচরণ ছাড়াই memcpyটাইপ-পেনিংয়ের জন্য পয়েন্টার castালাইয়ের পরিবর্তে ব্যবহার করুন। সংকলকরা কীভাবে এটি দক্ষতার সাথে ইনলাইন করতে হয় তা জানেন।

// static_assert(sizeof(double) == 2 * sizeof(uint32_t), "double isn't 8-byte IEEE binary64");
// and also static_assert something about FLT_ENDIAN?

double ff=(double)(v|1);

uint32_t tmp;
memcpy(&tmp, ((const char*)&ff)+sizeof(uint32_t), sizeof(uint32_t));
return (tmp>>20)-1023;

অথবা C99 এবং তারপরে, এ ব্যবহার করুন union {double d; uint32_t u[2];};। তবে মনে রাখবেন যে সি ++ তে ইউনিয়ন টাইপ পেনিং কেবলমাত্র কয়েকটি সংকলককে এক্সটেনশন হিসাবে সমর্থিত, আইএসও সি ++ তে নয়।


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

এই অ্যালগরিদমটি সিমডি বাস্তবায়নের জন্য কার্যকরভাবে কার্যকর হতে পারে, কারণ কম সিপিইউতে সিমডি থাকে lzcnt। x86 কেবল এভিএক্স 512 সিসি সহ এ জাতীয় নির্দেশ পেয়েছে


2
হ্যাঁ. টাইপ-এলিয়াসিং অপটিমাইজেশনের কারণে জিসিসি -O2 এর সাথে কোড সহ এই দুষ্টু জিনিসগুলি করবে।
এমএসএন

4
পূর্ণসংখ্যার এবং ভাসমান পয়েন্টের মধ্যে
ingালাই

1
হ্যাঁ, এফপিইউ ব্যয় বেশি। তবে প্রকৃত সময় পরিমাপগুলি অল-বিট অপস বা বিশেষত কোনও লুপগুলির চেয়ে দ্রুত ছিল। এটি ব্যবহার করে দেখুন এবং দ্রুত গ্রহণ করা সর্বদা সেরা পরামর্শ। আমি যদিও এটি নিয়ে জিসিসি এবং -ও 2 নিয়ে সমস্যা করি না।
এসপিওয়ার্লি

1
এই অনির্ধারিত আচরণ নয় (একটি বেমানান ধরণের পয়েন্টারের মাধ্যমে মান পড়া)?
ড্রিমলাক্স

3
হ্যাকারস ডিলাইট 5.3 কাউন্টিং লিডিং 0 এর মধ্যে 32-বিট ফ্লোটে ত্রুটির জন্য কীভাবে সংশোধন করবেন তা ব্যাখ্যা করে। এখানে তাদের কোড, যা ফ্ল্যাট এবং asInt হিসাবে ওভারল্যাপ করতে একটি বেনামী ইউনিয়ন ব্যবহার করে: কে = কে & ~ (কে >> 1); asFloat = (ভাসা) k + 0.5f; n = 158 - (asInt >> 23); (এবং হ্যাঁ, এটি বাস্তবায়ন-সংজ্ঞায়িত আচরণের উপর নির্ভর করে)
ডি কোয়েজি

11

কাজ কিলহেখু এখানে

আমি সাইন বিট থেকে দূরে থাকায় আমি 63৩ টিরও বেশি বিট সংখ্যার (জিসিসি x86_64-তে দীর্ঘ দীর্ঘ টাইপ) জন্য দুটি পদ্ধতির বেঞ্চমার্ক করেছি।

(কোনও কিছুর জন্য আমার এই "সর্বাধিক বিট" দরকার হবে, আপনি দেখুন))

আমি ডেটা-চালিত বাইনারি অনুসন্ধান (উপরের উত্তরগুলির সাথে ঘনিষ্ঠভাবে ভিত্তিক) প্রয়োগ করেছি। আমি হাতে হাতে একটি সম্পূর্ণ অনিবন্ধিত সিদ্ধান্ত গাছ বাস্তবায়ন করেছি, যা তাত্ক্ষণিক ক্রিয়াকলাপগুলির সাথে স্রেফ কোড। লুপ নেই, টেবিল নেই।

সিদ্ধান্ত গাছ (সর্বোচ্চ_বিট_অনরোলल्ड) বেঞ্চমার্কটি =৯% দ্রুত হতে পারে, এন = 0 কেস ব্যতীত, বাইনারি অনুসন্ধানের স্পষ্ট পরীক্ষা রয়েছে।

বাইনারি-অনুসন্ধানের 0 টি ক্ষেত্রে বিশেষ পরীক্ষা সিদ্ধান্ত গাছের তুলনায় মাত্র 48% দ্রুত, যার কোনও বিশেষ পরীক্ষা নেই।

সংকলক, মেশিন: (জিসিসি 4.5.2, -O3, x86-64, 2867 মেগাহার্টজ ইন্টেল কোর আই 5)।

int highest_bit_unrolled(long long n)
{
  if (n & 0x7FFFFFFF00000000) {
    if (n & 0x7FFF000000000000) {
      if (n & 0x7F00000000000000) {
        if (n & 0x7000000000000000) {
          if (n & 0x4000000000000000)
            return 63;
          else
            return (n & 0x2000000000000000) ? 62 : 61;
        } else {
          if (n & 0x0C00000000000000)
            return (n & 0x0800000000000000) ? 60 : 59;
          else
            return (n & 0x0200000000000000) ? 58 : 57;
        }
      } else {
        if (n & 0x00F0000000000000) {
          if (n & 0x00C0000000000000)
            return (n & 0x0080000000000000) ? 56 : 55;
          else
            return (n & 0x0020000000000000) ? 54 : 53;
        } else {
          if (n & 0x000C000000000000)
            return (n & 0x0008000000000000) ? 52 : 51;
          else
            return (n & 0x0002000000000000) ? 50 : 49;
        }
      }
    } else {
      if (n & 0x0000FF0000000000) {
        if (n & 0x0000F00000000000) {
          if (n & 0x0000C00000000000)
            return (n & 0x0000800000000000) ? 48 : 47;
          else
            return (n & 0x0000200000000000) ? 46 : 45;
        } else {
          if (n & 0x00000C0000000000)
            return (n & 0x0000080000000000) ? 44 : 43;
          else
            return (n & 0x0000020000000000) ? 42 : 41;
        }
      } else {
        if (n & 0x000000F000000000) {
          if (n & 0x000000C000000000)
            return (n & 0x0000008000000000) ? 40 : 39;
          else
            return (n & 0x0000002000000000) ? 38 : 37;
        } else {
          if (n & 0x0000000C00000000)
            return (n & 0x0000000800000000) ? 36 : 35;
          else
            return (n & 0x0000000200000000) ? 34 : 33;
        }
      }
    }
  } else {
    if (n & 0x00000000FFFF0000) {
      if (n & 0x00000000FF000000) {
        if (n & 0x00000000F0000000) {
          if (n & 0x00000000C0000000)
            return (n & 0x0000000080000000) ? 32 : 31;
          else
            return (n & 0x0000000020000000) ? 30 : 29;
        } else {
          if (n & 0x000000000C000000)
            return (n & 0x0000000008000000) ? 28 : 27;
          else
            return (n & 0x0000000002000000) ? 26 : 25;
        }
      } else {
        if (n & 0x0000000000F00000) {
          if (n & 0x0000000000C00000)
            return (n & 0x0000000000800000) ? 24 : 23;
          else
            return (n & 0x0000000000200000) ? 22 : 21;
        } else {
          if (n & 0x00000000000C0000)
            return (n & 0x0000000000080000) ? 20 : 19;
          else
            return (n & 0x0000000000020000) ? 18 : 17;
        }
      }
    } else {
      if (n & 0x000000000000FF00) {
        if (n & 0x000000000000F000) {
          if (n & 0x000000000000C000)
            return (n & 0x0000000000008000) ? 16 : 15;
          else
            return (n & 0x0000000000002000) ? 14 : 13;
        } else {
          if (n & 0x0000000000000C00)
            return (n & 0x0000000000000800) ? 12 : 11;
          else
            return (n & 0x0000000000000200) ? 10 : 9;
        }
      } else {
        if (n & 0x00000000000000F0) {
          if (n & 0x00000000000000C0)
            return (n & 0x0000000000000080) ? 8 : 7;
          else
            return (n & 0x0000000000000020) ? 6 : 5;
        } else {
          if (n & 0x000000000000000C)
            return (n & 0x0000000000000008) ? 4 : 3;
          else
            return (n & 0x0000000000000002) ? 2 : (n ? 1 : 0);
        }
      }
    }
  }
}

int highest_bit(long long n)
{
  const long long mask[] = {
    0x000000007FFFFFFF,
    0x000000000000FFFF,
    0x00000000000000FF,
    0x000000000000000F,
    0x0000000000000003,
    0x0000000000000001
  };
  int hi = 64;
  int lo = 0;
  int i = 0;

  if (n == 0)
    return 0;

  for (i = 0; i < sizeof mask / sizeof mask[0]; i++) {
    int mi = lo + (hi - lo) / 2;

    if ((n >> mi) != 0)
      lo = mi;
    else if ((n & (mask[i] << lo)) != 0)
      hi = mi;
  }

  return lo + 1;
}

দ্রুত এবং নোংরা পরীক্ষা প্রোগ্রাম:

#include <stdio.h>
#include <time.h>
#include <stdlib.h>

int highest_bit_unrolled(long long n);
int highest_bit(long long n);

main(int argc, char **argv)
{
  long long n = strtoull(argv[1], NULL, 0);
  int b1, b2;
  long i;
  clock_t start = clock(), mid, end;

  for (i = 0; i < 1000000000; i++)
    b1 = highest_bit_unrolled(n);

  mid = clock();

  for (i = 0; i < 1000000000; i++)
    b2 = highest_bit(n);

  end = clock();

  printf("highest bit of 0x%llx/%lld = %d, %d\n", n, n, b1, b2);

  printf("time1 = %d\n", (int) (mid - start));
  printf("time2 = %d\n", (int) (end - mid));
  return 0;
}

কেবল -O2 ব্যবহার করে পার্থক্য আরও বেশি হয়। সিদ্ধান্ত গাছ প্রায় চারগুণ দ্রুত is

আমি নিষ্পাপ বিট শিফটিং কোডটির বিরুদ্ধেও বেঞ্চমার্ক করেছি:

int highest_bit_shift(long long n)
{
  int i = 0;
  for (; n; n >>= 1, i++)
    ; /* empty */
  return i;
}

এটি কেবলমাত্র অল্প সংখ্যক ক্ষেত্রেই দ্রুত, যেমনটি কেউ আশা করে। সর্বোচ্চ বিটটি এন == 1 এর জন্য 1 হবে তা নির্ধারণের ক্ষেত্রে এটি ৮০% এরও বেশি দ্রুত বেঞ্চমার্ক করেছে। তবে, bit৩ বিট স্পেসে এলোমেলোভাবে নির্বাচিত সংখ্যাগুলির অর্ধেকের মধ্যে 63৩ তম বিট সেট রয়েছে!

ইনপুট 0x3FFFFFFFFFFFFFFFFF ইনপুটটিতে, সিদ্ধান্ত গাছের সংস্করণটি 1-এর তুলনায় কিছুটা দ্রুত এবং বিট শিফটারের তুলনায় 1120% (12.2 গুণ) দ্রুত গতিতে দেখায়।

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


9
আমি বলছি না এটি ভাল নয়, তবে আপনার পরীক্ষার প্রোগ্রামটি এখানে কেবল একই সংখ্যায় পরীক্ষা করে, যা 2-3 বারবারের পরে শাখার ভবিষ্যদ্বাণীকারীদের তাদের চূড়ান্ত অবস্থানে স্থাপন করে এবং তারপরে তারা নিখুঁত শাখার পূর্বাভাস দেয়। ভাল কথা হ'ল সম্পূর্ণরূপে এলোমেলো বিতরণ দিয়ে অর্ধেক সংখ্যার কাছে বিট 63 নামের নিখুঁত পূর্বাভাসের কাছাকাছি থাকবে।
অ্যাশ Shāţi '


6
unsigned int
msb32(register unsigned int x)
{
        x |= (x >> 1);
        x |= (x >> 2);
        x |= (x >> 4);
        x |= (x >> 8);
        x |= (x >> 16);
        return(x & ~(x >> 1));
}

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

Http://aggregate.org/MAGIC/#Most%20Significant%201%20 বিট থেকে


7
উপরের কোড প্রশ্নের উত্তর দেয় না। এটি একটি স্বাক্ষরবিহীন পূর্ণসংখ্যার ফেরত দেয় যেখানে এক্স বিটে সর্বাধিক তাৎপর্যপূর্ণ থাকে এবং অন্যান্য সমস্ত বিট বন্ধ থাকে off বিটটিতে সবচেয়ে উল্লেখযোগ্য অবস্থানে ফিরে আসার প্রশ্নটি ছিল return
নায়িকা

3
তারপরে আপনি সেট করা বিটের সূচি খুঁজে পেতে কোনও ডি ব্রুইজন সিকোয়েন্স পদ্ধতির ব্যবহার করতে পারেন। :-)
আর .. গীটহাব বন্ধ করুন ICE

5
@ নায়ক, তিনি একটি মন্তব্যে বলেছেন যে হয় যথেষ্ট হয়।
rlbond

এইটি (সেই একই পৃষ্ঠা থেকে) আপনার যা প্রয়োজন তা করবে তবে এটির জন্য অতিরিক্ত ফাংশন প্রয়োজন। aggregate.org/MAGIC/#Log2%20of%20an%20Intteger
কুইন টেলর

1
কমপক্ষে কোর 2 থেকে বিএসআর ইন্টেল সিপিইউগুলিতে দ্রুত। এলজেডিসিএনটি এএমডি সিপিইউতে দ্রুত এবং জিসিসি এটি ব্যবহার করে বা কোনও কিছুর জন্য __builtin_clzসক্ষম হয়েছে -march=native(যেহেতু এটি প্রতিটি সিপিইউ যা এটি সমর্থন করে এটি দ্রুত) it এমনকি এএমডি বুলডোজার-পরিবারের মতো সিপিইউগুলিতে যেখানে বিএসআর "স্লো", এটি এত ধীর নয়: 4 টি চক্রের বিলম্ব এবং 7 সি থ্রুটপুট সহ একটির 7 এম-অপস। পরমাণুতে, বিএসআর সত্যিই ধীর: 16 টি চক্র। সিলভারমন্টে এটি 10 ​​চক্রের বিলম্বের সাথে 10 উওস। এটি সিলভারমন্টে বিএসআরের তুলনায় কিছুটা কম বিলম্ব হতে পারে তবে IDK।
পিটার কর্ডেস

6

এই পৃষ্ঠায় বর্তমানে দেওয়া অ্যালগরিদমের কয়েকটি (সাধারণ) মাপদণ্ড এখানে ...

অ্যালগোরিদমগুলি স্বাক্ষরযুক্ত স্বাক্ষরের সমস্ত ইনপুটগুলির উপরে পরীক্ষা করা হয়নি; অন্ধভাবে কিছু ব্যবহার করার আগে প্রথমে এটি পরীক্ষা করে দেখুন;)

আমার মেশিনে clz (__ বিল্টিন_ক্লিজ) এবং asm কাজ সেরা work asm আরও দ্রুত মনে হচ্ছে clz ... তবে এটি সাধারণ বেনমার্কের কারণে হতে পারে ...

//////// go.c ///////////////////////////////
// compile with:  gcc go.c -o go -lm
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

/***************** math ********************/

#define POS_OF_HIGHESTBITmath(a) /* 0th position is the Least-Signif-Bit */    \
  ((unsigned) log2(a))         /* thus: do not use if a <= 0 */  

#define NUM_OF_HIGHESTBITmath(a) ((a)               \
                  ? (1U << POS_OF_HIGHESTBITmath(a))    \
                  : 0)



/***************** clz ********************/

unsigned NUM_BITS_U = ((sizeof(unsigned) << 3) - 1);
#define POS_OF_HIGHESTBITclz(a) (NUM_BITS_U - __builtin_clz(a)) /* only works for a != 0 */

#define NUM_OF_HIGHESTBITclz(a) ((a)                    \
                 ? (1U << POS_OF_HIGHESTBITclz(a))  \
                 : 0)


/***************** i2f ********************/

double FF;
#define POS_OF_HIGHESTBITi2f(a) (FF = (double)(ui|1), ((*(1+(unsigned*)&FF))>>20)-1023)


#define NUM_OF_HIGHESTBITi2f(a) ((a)                    \
                 ? (1U << POS_OF_HIGHESTBITi2f(a))  \
                 : 0)




/***************** asm ********************/

unsigned OUT;
#define POS_OF_HIGHESTBITasm(a) (({asm("bsrl %1,%0" : "=r"(OUT) : "r"(a));}), OUT)

#define NUM_OF_HIGHESTBITasm(a) ((a)                    \
                 ? (1U << POS_OF_HIGHESTBITasm(a))  \
                 : 0)




/***************** bitshift1 ********************/

#define NUM_OF_HIGHESTBITbitshift1(a) (({   \
  OUT = a;                  \
  OUT |= (OUT >> 1);                \
  OUT |= (OUT >> 2);                \
  OUT |= (OUT >> 4);                \
  OUT |= (OUT >> 8);                \
  OUT |= (OUT >> 16);               \
      }), (OUT & ~(OUT >> 1)))          \



/***************** bitshift2 ********************/
int POS[32] = {0, 1, 28, 2, 29, 14, 24, 3,
             30, 22, 20, 15, 25, 17, 4, 8, 31, 27, 13, 23, 21, 19,
             16, 7, 26, 12, 18, 6, 11, 5, 10, 9};

#define POS_OF_HIGHESTBITbitshift2(a) (({   \
  OUT = a;                  \
  OUT |= OUT >> 1;              \
  OUT |= OUT >> 2;              \
  OUT |= OUT >> 4;              \
  OUT |= OUT >> 8;              \
  OUT |= OUT >> 16;             \
  OUT = (OUT >> 1) + 1;             \
      }), POS[(OUT * 0x077CB531UL) >> 27])

#define NUM_OF_HIGHESTBITbitshift2(a) ((a)              \
                       ? (1U << POS_OF_HIGHESTBITbitshift2(a)) \
                       : 0)



#define LOOPS 100000000U

int main()
{
  time_t start, end;
  unsigned ui;
  unsigned n;

  /********* Checking the first few unsigned values (you'll need to check all if you want to use an algorithm here) **************/
  printf("math\n");
  for (ui = 0U; ui < 18; ++ui)
    printf("%i\t%i\n", ui, NUM_OF_HIGHESTBITmath(ui));

  printf("\n\n");

  printf("clz\n");
  for (ui = 0U; ui < 18U; ++ui)
    printf("%i\t%i\n", ui, NUM_OF_HIGHESTBITclz(ui));

  printf("\n\n");

  printf("i2f\n");
  for (ui = 0U; ui < 18U; ++ui)
    printf("%i\t%i\n", ui, NUM_OF_HIGHESTBITi2f(ui));

  printf("\n\n");

  printf("asm\n");
  for (ui = 0U; ui < 18U; ++ui) {
    printf("%i\t%i\n", ui, NUM_OF_HIGHESTBITasm(ui));
  }

  printf("\n\n");

  printf("bitshift1\n");
  for (ui = 0U; ui < 18U; ++ui) {
    printf("%i\t%i\n", ui, NUM_OF_HIGHESTBITbitshift1(ui));
  }

  printf("\n\n");

  printf("bitshift2\n");
  for (ui = 0U; ui < 18U; ++ui) {
    printf("%i\t%i\n", ui, NUM_OF_HIGHESTBITbitshift2(ui));
  }

  printf("\n\nPlease wait...\n\n");


  /************************* Simple clock() benchmark ******************/
  start = clock();
  for (ui = 0; ui < LOOPS; ++ui)
    n = NUM_OF_HIGHESTBITmath(ui);
  end = clock();
  printf("math:\t%e\n", (double)(end-start)/CLOCKS_PER_SEC);

  start = clock();
  for (ui = 0; ui < LOOPS; ++ui)
    n = NUM_OF_HIGHESTBITclz(ui);
  end = clock();
  printf("clz:\t%e\n", (double)(end-start)/CLOCKS_PER_SEC);

  start = clock();
  for (ui = 0; ui < LOOPS; ++ui)
    n = NUM_OF_HIGHESTBITi2f(ui);
  end = clock();
  printf("i2f:\t%e\n", (double)(end-start)/CLOCKS_PER_SEC);

  start = clock();
  for (ui = 0; ui < LOOPS; ++ui)
    n = NUM_OF_HIGHESTBITasm(ui);
  end = clock();
  printf("asm:\t%e\n", (double)(end-start)/CLOCKS_PER_SEC);

  start = clock();
  for (ui = 0; ui < LOOPS; ++ui)
    n = NUM_OF_HIGHESTBITbitshift1(ui);
  end = clock();
  printf("bitshift1:\t%e\n", (double)(end-start)/CLOCKS_PER_SEC);

  start = clock();
  for (ui = 0; ui < LOOPS; ++ui)
    n = NUM_OF_HIGHESTBITbitshift2(ui);
  end = clock();
  printf("bitshift2\t%e\n", (double)(end-start)/CLOCKS_PER_SEC);

  printf("\nThe lower, the better. Take note that a negative exponent is good! ;)\n");

  return EXIT_SUCCESS;
}

6

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

[...], bsrlসমাবেশ নির্দেশ সর্বাধিক উল্লেখযোগ্য বিটের অবস্থান গণনা করে। সুতরাং, আমরা এই asmবিবৃতি ব্যবহার করতে পারে :

asm ("bsrl %1, %0" 
     : "=r" (position) 
     : "r" (number));

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

আমি বলব লগারিদম গ্রহণ করা একটি নিখুঁতভাবে পঠনযোগ্য সমাধান হতে পারে (
সংকলকটি

কখনও কখনও সিপিইউ মাইক্রোকোডে প্রয়োগের উপর নির্ভর করে ইনলাইন এএসএম সমাধান ধীর হয়।
rlbond

5
@ আরলবাউন্ড: আমি বিশ্বাস করতে পারি না, যদিও আমার ভুল হতে পারে। যে কোনও আধুনিক সিপিইউতে যে কেউ এটি ভাবেন যে এটি কোনও একক নির্দেশায় অনুবাদিত হবে ....
নলডোরিন

3
@ নলডোরিন এটি কিছুটা দেরি করেছে তবে .. এটি সংজ্ঞা অনুসারে একটি একক নির্দেশনা, তবে এটি যদি আরএলবন্ডের পরামর্শ অনুসারে মাইক্রোকোডযুক্ত থাকে তবে সেই একক নির্দেশটি অভ্যন্তরীণভাবে পুরো একগুচ্ছ toষধগুলিকে ডিকোড করতে পারে। এটি এএমডির মাইক্রোআরকিটেক্টচার এবং ইন্টেল অ্যাটমের ক্ষেত্রে হতে পারে, তবে সাধারণ ইনটেল মাইক্রোআরকিটেকচারগুলিতে এটি পুরোপুরি একক অপারেশন।
26:51

4

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

int highest_bit(unsigned int a) {
  static const unsigned int maskv[] = { 0xffff, 0xff, 0xf, 0x3, 0x1 };
  const unsigned int *mask = maskv;
  int l, h;

  if (a == 0) return -1;

  l = 0;
  h = 32;

  do {
    int m = l + (h - l) / 2;

    if ((a >> m) != 0) l = m;
    else if ((a & (*mask << l)) != 0) h = m;

    mask++;
  } while (l < h - 1);

  return l;
}

4

কোনও ধরণের বাইনারি অনুসন্ধান চালায়, এটি সমস্ত ধরণের (স্বাক্ষরযুক্ত!) পূর্ণসংখ্যার ধরণের সাথে কাজ করে

#include <climits>
#define UINT (unsigned int)
#define UINT_BIT (CHAR_BIT*sizeof(UINT))

int msb(UINT x)
{
    if(0 == x)
        return -1;

    int c = 0;

    for(UINT i=UINT_BIT>>1; 0<i; i>>=1)
    if(static_cast<UINT>(x >> i))
    {
        x >>= i;
        c |= i;
    }

    return c;
}

সম্পূর্ণ করতে:

#include <climits>
#define UINT unsigned int
#define UINT_BIT (CHAR_BIT*sizeof(UINT))

int lsb(UINT x)
{
    if(0 == x)
        return -1;

    int c = UINT_BIT-1;

    for(UINT i=UINT_BIT>>1; 0<i; i>>=1)
    if(static_cast<UINT>(x << i))
    {
        x <<= i;
        c ^= i;
    }

    return c;
}

4
দয়া করে ALL_CAPS এর জন্য typedefবা প্রিপ্রোসেসর ম্যাক্রো ব্যতীত অন্য কোনও কিছুর জন্য ব্যবহার না করার বিষয়টি বিবেচনা করুন । এটি একটি বহুল স্বীকৃত সম্মেলন।
আন্ডারস্কোর_১১

4

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

যদি অভ্যন্তরীণ ফাংশন কোনও বিকল্প না হয় তবে সাধারণ ইনপুটগুলি প্রক্রিয়াকরণের জন্য এখানে একটি অনুকূল সফ্টওয়্যার সমাধান রয়েছে।

u8  inline log2 (u32 val)  {
    u8  k = 0;
    if (val > 0x0000FFFFu) { val >>= 16; k  = 16; }
    if (val > 0x000000FFu) { val >>= 8;  k |= 8;  }
    if (val > 0x0000000Fu) { val >>= 4;  k |= 4;  }
    if (val > 0x00000003u) { val >>= 2;  k |= 2;  }
    k |= (val & 2) >> 1;
    return k;
}

মনে রাখবেন যে এই সংস্করণটির শেষে অন্যান্য উত্তরগুলির চেয়ে পৃথকভাবে কোনও দেবুউইন লুকের প্রয়োজন নেই। এটি জায়গায় অবস্থান গণনা করে।

টেবিলগুলি বেশি পছন্দনীয় হতে পারে, আপনি যদি এটিকে বারবার পর্যাপ্ত পরিমাণে কল করেন তবে কোনও টেবিলের গতিবেগের ফলে ক্যাশে মিসের ঝুঁকি গ্রহন হয়ে যায়।

u8 kTableLog2[256] = {
0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7
};

u8 log2_table(u32 val)  {
    u8  k = 0;
    if (val > 0x0000FFFFuL) { val >>= 16; k  = 16; }
    if (val > 0x000000FFuL) { val >>=  8; k |=  8; }
    k |= kTableLog2[val]; // precompute the Log2 of the low byte

    return k;
}

এটি এখানে প্রদত্ত যে কোনও সফ্টওয়্যার উত্তরের সর্বাধিক থ্রুপুট উত্পাদন করতে হবে, তবে আপনি যদি মাঝে মাঝে এটি কল করেন তবে আমার প্রথম স্নিপেটের মতো একটি টেবিল-মুক্ত সমাধান পছন্দ করুন।


1
উত্তরগুলির কয়েকটি শাখাবিহীন, তবে এটি সম্ভবত শর্তাধীন শাখাগুলি সংকলন করবে। আপনি কি একই বারবার একই মান সহ একটি সাধারণ প্যাটার্ন বা কিছু দিয়েছিলেন? শাখার ভুল ধারণাটি পারফরম্যান্সের জন্য হত্যাকারী। stackoverflow.com/questions/11227809/…
পিটার

3

উপরের উত্তরগুলি যেমন উল্লেখ করেছে, সর্বাধিক উল্লেখযোগ্য বিট নির্ধারণের জন্য বেশ কয়েকটি উপায় রয়েছে। তবে, যেমনটি উল্লেখ করা হয়েছিল, পদ্ধতিগুলি 32 বিট বা bit৪ বিট নিবন্ধকের মধ্যে অনন্য হতে পারে। Stanford.edu bithacks পৃষ্ঠা উভয় 32bit এবং 64bit জন্য কাজ কম্পিউটিং সমাধান প্রদান করে। সামান্য কাজ করার সাথে, তাদেরকে এমএসবি প্রাপ্ত করার জন্য একটি দৃ cross় ক্রস-আর্কিটেকচার পদ্ধতির সরবরাহ করতে একত্রিত করা যেতে পারে। আমি যে সমাধানটিতে পৌঁছেছি তা 64৪ এবং ৩২ বিট কম্পিউটারের মধ্যে সংকলিত / কাজ করা হয়েছে:

#if defined(__LP64__) || defined(_LP64)
# define BUILD_64   1
#endif

#include <stdio.h>
#include <stdint.h>  /* for uint32_t */

/* CHAR_BIT  (or include limits.h) */
#ifndef CHAR_BIT
#define CHAR_BIT  8
#endif  /* CHAR_BIT */

/* 
 * Find the log base 2 of an integer with the MSB N set in O(N)
 * operations. (on 64bit & 32bit architectures)
 */
int
getmsb (uint32_t word)
{
    int r = 0;
    if (word < 1)
        return 0;
#ifdef BUILD_64
    union { uint32_t u[2]; double d; } t;  // temp
    t.u[__FLOAT_WORD_ORDER==LITTLE_ENDIAN] = 0x43300000;
    t.u[__FLOAT_WORD_ORDER!=LITTLE_ENDIAN] = word;
    t.d -= 4503599627370496.0;
    r = (t.u[__FLOAT_WORD_ORDER==LITTLE_ENDIAN] >> 20) - 0x3FF;
#else
    while (word >>= 1)
    {
        r++;
    }
#endif  /* BUILD_64 */
    return r;
}

ইন্ট আর ছিল না; মূলত #ifdef BUILD_64পতাকার উপরে সংজ্ঞায়িত ? সেক্ষেত্রে শর্তসাপেক্ষে এটির পুনঃনির্ধারণের প্রয়োজন হবে না।
ডেভিড সি র্যাঙ্কিন

3

ক্রমাগত আনুমানিক ব্যবহার করে সি-র একটি সংস্করণ:

unsigned int getMsb(unsigned int n)
{
  unsigned int msb  = sizeof(n) * 4;
  unsigned int step = msb;
  while (step > 1)
 {
    step /=2;
    if (n>>msb)
     msb += step;
   else
     msb -= step;
 }
  if (n>>msb)
    msb++;
  return (msb - 1);
}

সুবিধা: চলমান সময় প্রদত্ত সংখ্যা নির্বিশেষে ধ্রুবক হয়, কারণ লুপের সংখ্যা সর্বদা একই থাকে। ("স্বাক্ষরবিহীন ইনট" ব্যবহার করার সময় 4 টি লুপ)


আপনি যদি এটি কোনও টার্নারি অপারেটর ( msb += (n>>msb) ? step : -step;) দিয়ে লিখেন তবে আরও সংকলক প্রতিটি শাখায় স্ট্যাকওভারফ্লো / প্রশ্নগুলি / 12128080 // ) শাখার ভুলগুলি এড়িয়ে গিয়ে শাখাবিহীন asm তৈরি করে ।
পিটার কর্ডেস

3

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

বেশিরভাগ সমাধান (বিশেষত যাঁরা বাইনারি অনুসন্ধানের স্কিম বা স্নিগ্ধ দৃষ্টিভঙ্গি ব্যবহার করেন যা ডান থেকে বামে রৈখিক স্ক্যান করে) এ বিষয়টি অবহেলা করে বলে মনে হয় যে স্বেচ্ছাসেবী বাইনারি সংখ্যার জন্য, এমন অনেকগুলি নেই যা খুব দীর্ঘ ক্রম দিয়ে শুরু হয় শূন্য। আসলে, কোনও বিট-প্রস্থের জন্য, সমস্ত পূর্ণসংখ্যার অর্ধেক 1 দিয়ে শুরু হয় এবং তাদের চতুর্থাংশ 01 দিয়ে শুরু হয় । আমি কোথায় যাচ্ছি দেখুন? আমার যুক্তিটি হ'ল যে এক লিনিয়ার স্ক্যানটি সর্বাধিক তাৎপর্যপূর্ণ বিট অবস্থান থেকে শুরু করে কমপক্ষে তাৎক্ষণিক (বাম থেকে ডান) অবধি "লিনিয়ার" নয় কারণ এটি প্রথম নজরে দেখা যায়।

এটি 1 টি দেখানো যেতে পারে যে কোনও বিট-প্রস্থের জন্য, বিটগুলির গড় সংখ্যার গড় সংখ্যা সর্বোচ্চ 2 হয় This এটি বিটের সংখ্যার (1) সংখ্যার সাথে ও (1) এর একটি মোড়িত সময় জটিলতায় অনুবাদ করে ।

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

এখানে আমি যে "নির্দোষ" দৃষ্টিভঙ্গি নিয়ে এসেছি, যা কমপক্ষে আমার মেশিনে অন্যান্য বেশিরভাগ পদ্ধতিকে মারধর করে (32-বিট ইন্টের জন্য বাইনারি অনুসন্ধানের স্কিমগুলির জন্য সর্বদা লগ 2 (32) = 5 পদক্ষেপ প্রয়োজন, অন্যদিকে এই নির্বোধ অ্যালগরিদম কম প্রয়োজন গড়ে 2 এর চেয়ে বেশি) - এটি সি ++ হওয়ার জন্য এবং খাঁটি সি নয় বলে দুঃখিত:

template <typename T>
auto msb(T n) -> int
{
    static_assert(std::is_integral<T>::value && !std::is_signed<T>::value,
        "msb<T>(): T must be an unsigned integral type.");

    for (T i = std::numeric_limits<T>::digits - 1, mask = 1 << i; i >= 0; --i, mask >>= 1)
    {
        if ((n & mask) != 0)
            return i;
    }

    return 0;
}

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

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

যদি আমরা সমস্ত সম্ভাব্য পূর্ণসংখ্যার জন্য প্রয়োজনীয় সমস্ত পুনরাবৃত্তিগুলি যোগ করি এবং তাদের 2 এন , মোট পূর্ণসংখ্যার সংখ্যা দ্বারা বিভক্ত করি , আমরা এন- বিট পূর্ণসংখ্যার জন্য এমএসবি নির্ধারণের জন্য প্রয়োজনীয় পুনরাবৃত্তির গড় সংখ্যা পাই :

(1 * 2 এন - 1 + 2 * 2 এন - 2 + 3 * 2 এন - 3 + ... + এন) / 2 এন

এই গড় পুনরাবৃত্তির এই সিরিজটি আসলে অভিভাবক এবং অনন্তের দিকে n এর সীমা 2 থাকে

সুতরাং, সাদাসিধা বাম-থেকে-ডান অ্যালগরিদম আসলে একটি আছে amortized লাগাতার সময় জটিলতা হে (1) বিট যে কোন সংখ্যার জন্য।


2
আমার মনে হয় না যে এটি এমএসবি ফাংশনগুলির ইনপুটগুলি সমানভাবে বিতরণ করার প্রবণতা একটি ন্যায্য ধারণা। অনুশীলনে, এই ইনপুটগুলি অসম্পূর্ণ বিতরণকৃত মানগুলির সাথে বাধা নিবন্ধক বা বিটবোর্ড বা অন্য কোনও ডেটা স্ট্রাকচার হতে পারে tend সুষ্ঠু মানদণ্ডের জন্য আমি মনে করি যে আউটপুটগুলি (ইনপুটগুলি নয়) সমানভাবে বিতরণ করা হবে তা ধরে নেওয়া নিরাপদ।
johnwbyrd

3

আমাদের দিয়েছে log2। এটি log2এই পৃষ্ঠায় আপনি যে সমস্ত বিশেষ সস প্রয়োগের প্রয়োজন তা সরিয়ে দেয় । আপনি স্ট্যান্ডার্ডের log2প্রয়োগটি এভাবে ব্যবহার করতে পারেন :

const auto n = 13UL;
const auto Index = (unsigned long)log2(n);

printf("MSB is: %u\n", Index); // Prints 3 (zero offset)

একটি nএর 0ULচাহিদা পাশাপাশি বিরুদ্ধে কারণ পাহারায় করা:

-∞ ফিরিয়ে দেওয়া হয় এবং FE_DIVBYZERO উত্থাপিত হয়

আমি সেই চেকটির সাথে একটি উদাহরণ লিখেছি যা এখানে নির্বিচারে সেট Indexকরে ULONG_MAX: https://ideone.com/u26vsi


দ্য থেকে সম্পুরক ephemient এর জিসিসি শুধুমাত্র উত্তর হল:

const auto n = 13UL;
unsigned long Index;

_BitScanReverse(&Index, n);
printf("MSB is: %u\n", Index); // Prints 3 (zero offset)

_BitScanReverseরাষ্ট্রগুলির জন্য ডকুমেন্টেশন যা Indexহ'ল:

প্রথম সেট বিটের বিট অবস্থানের সাথে লোড (1) পাওয়া গেছে

বাস্তবে আমি দেখেছি যে, যদি nহয় 0ULযে Indexসেট করা হয়0UL , যেমনটা এটি একটি জন্য হবে nএর 1UL। কিন্তু শুধু একটি ক্ষেত্রে ডকুমেন্টেশনে নিশ্চিত nএর 0ULযে ফিরতি নন:

0 যদি সেট বিট পাওয়া যায় নি

সুতরাং, একইভাবে log2রিটার্নের ওপরে পছন্দসই প্রয়োগের Indexক্ষেত্রে এই ক্ষেত্রে একটি পতাকাযুক্ত মান হিসাবে সেটিংস পরীক্ষা করা উচিত । আমি আবার ULONG_MAXএই পতাকাটির মানটির জন্য ব্যবহারের উদাহরণ লিখেছি : http://rextester.com/GCU61409


না, কেবল ইনপুট থাকলে _BitScanReverse0 প্রদান করে । এটি x86 এর নির্দেশের মতো , যা আউটপুট নয়, কেবল ইনপুটের ভিত্তিতে জেডএফ সেট করে। মজার বিষয় হল যে কোনও বিএসটি পাওয়া না গেলে এমএস ডক্সকে আনসেট ছাড়ার কথা বলে; এটি x86 এর asm আচরণের সাথেও মেলে । (এএমডি এটিকে src = 0 এ অশোধিত গন্তব্য রেজিস্ট্রেশন হিসাবে রেখেছিল, তবে ইন্টেল কেবল অপরিজ্ঞাত আউটপুট বলেছে যদিও তাদের সিপিইউগুলি ছুটি-মোড়কবিহীন আচরণ বাস্তবায়িত করে।) এটি x86 এর বিপরীতে , যা খুঁজে পাওয়া যায় না। 0BSRindex1bsrlzcnt32
পিটার কর্ডেস

@ পিটারকর্ডস _BitScanReverseশূন্য-ভিত্তিক সূচক ব্যবহার করে, সুতরাং যদি n1 হয় তবে সেট বিটের সূচকটি আসলে 0 হয়। দুর্ভাগ্যক্রমে, আপনি যেমনটি n0 বলে থাকেন তবে আউটপুটটিও 0 :( এর অর্থ ফেরতটি ব্যবহার করার কোনও উপায় নেই n1 বা 0 এর মধ্যে পার্থক্য করুন That's এটিই আমি যোগাযোগের চেষ্টা করছিলাম youআপনি কি বলছেন এর থেকে আরও ভাল উপায় আছে?
জোনাথন মে

আমি মনে করি আপনি এটি কীভাবে সেট করবেন সে সম্পর্কে কথা বলছেন Index। এটি রিটার্নের মান নয়। এটি ইনফুটটি শূন্য হলে এই মিথ্যাটি মিথ্যা বলে প্রত্যাবর্তন করে (এবং এজন্য সূচকগুলি সাধারণত ফিরে আসার পরিবর্তে রেফারেন্স দিয়ে পাস করা হয়)। Godbolt.org/g/gQKJdE । এবং আমি পরীক্ষা করেছিলাম: এমএসের ডক্সের শব্দ থাকা সত্ত্বেও, _BitScanReverseসূচকটি সেট না করে ছাড়াই n==0: আপনি যে রেজিস্টারটি ব্যবহার করতে এসেছিলেন তাতে আপনি যে মানটি পেয়েছিলেন তা পেয়ে যান। (আপনার ক্ষেত্রে এটি সম্ভবত একই নিবন্ধ হিসাবে এটি Indexপরে ব্যবহার করা হয়েছিল, এটি আপনাকে দেখার জন্য পরিচালিত হয়েছিল 0)।
পিটার কর্ডেস

এই প্রশ্নটি সি ++ ট্যাগ করা হয়নি।
টেকনোসরাস

@ টেকনোসরাস, ধন্যবাদ, আমি নিজেকে ভুলে গেছি। প্রদত্ত যে প্রশ্নটি সি আমরা আমাদের আসলে log2C99 সাল থেকেই পেয়েছি ।
জোনাথন মি

2

বিটওয়াইজ অপারেটরদের চিন্তা করুন।

আমি প্রথমবার প্রশ্নটি ভুল বুঝেছি। আপনার বামতম বিট সেট (অন্যদের শূন্য) এর সাথে একটি আন্ত উত্পাদন করা উচিত। ধরে নিচ্ছি সিএমপি সেই মানটিতে সেট করা আছে:

position = sizeof(int)*8
while(!(n & cmp)){ 
   n <<=1;
   position--;
}

আপনি একটি স্ট্রিং রূপান্তর মানে কি? Ffs এর সংজ্ঞাটি একটি int নেয় এবং একটি int দেয়। রূপান্তর কোথায় হবে? এবং যদি আমরা একটি শব্দে বিটস খুঁজছি তবে রূপান্তরটি কী উদ্দেশ্যে কাজ করবে?
ড্রিমলাক্স

আমি এই ফাংশন সম্পর্কে জানতাম না।
ভাসিল

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

2

জোশের মানদণ্ডে প্রসারিত করা হচ্ছে ... নিম্নরূপে কেউ ক্লিজটি উন্নত করতে পারে

/***************** clz2 ********************/

#define NUM_OF_HIGHESTBITclz2(a) ((a)                              \
                  ? (((1U) << (sizeof(unsigned)*8-1)) >> __builtin_clz(a)) \
                  : 0)

Asm সম্পর্কে: নোট করুন যে এখানে বিএসআর এবং বিএসআরএল (এটি "দীর্ঘ" সংস্করণ)। স্বাভাবিকটি কিছুটা দ্রুত হতে পারে।


1

নোট করুন আপনি যা করতে চেষ্টা করছেন তা হল একটি পূর্ণসংখ্যার পূর্ণসংখ্যার লগ 2 গণনা করা,

#include <stdio.h>
#include <stdlib.h>

unsigned int
Log2(unsigned long x)
{
    unsigned long n = x;
    int bits = sizeof(x)*8;
    int step = 1; int k=0;
    for( step = 1; step < bits; ) {
        n |= (n >> step);
        step *= 2; ++k;
    }
    //printf("%ld %ld\n",x, (x - (n >> 1)) );
    return(x - (n >> 1));
}

লক্ষ্য করুন যে আপনি একবারে 1 বিটের বেশি অনুসন্ধান করার চেষ্টা করতে পারেন।

unsigned int
Log2_a(unsigned long x)
{
    unsigned long n = x;
    int bits = sizeof(x)*8;
    int step = 1;
    int step2 = 0;
    //observe that you can move 8 bits at a time, and there is a pattern...
    //if( x>1<<step2+8 ) { step2+=8;
        //if( x>1<<step2+8 ) { step2+=8;
            //if( x>1<<step2+8 ) { step2+=8;
            //}
        //}
    //}
    for( step2=0; x>1L<<step2+8; ) {
        step2+=8;
    }
    //printf("step2 %d\n",step2);
    for( step = 0; x>1L<<(step+step2); ) {
        step+=1;
        //printf("step %d\n",step+step2);
    }
    printf("log2(%ld) %d\n",x,step+step2);
    return(step+step2);
}

এই পদ্ধতির বাইনারি অনুসন্ধান ব্যবহার করে

unsigned int
Log2_b(unsigned long x)
{
    unsigned long n = x;
    unsigned int bits = sizeof(x)*8;
    unsigned int hbit = bits-1;
    unsigned int lbit = 0;
    unsigned long guess = bits/2;
    int found = 0;

    while ( hbit-lbit>1 ) {
        //printf("log2(%ld) %d<%d<%d\n",x,lbit,guess,hbit);
        //when value between guess..lbit
        if( (x<=(1L<<guess)) ) {
           //printf("%ld < 1<<%d %ld\n",x,guess,1L<<guess);
            hbit=guess;
            guess=(hbit+lbit)/2;
            //printf("log2(%ld) %d<%d<%d\n",x,lbit,guess,hbit);
        }
        //when value between hbit..guess
        //else
        if( (x>(1L<<guess)) ) {
            //printf("%ld > 1<<%d %ld\n",x,guess,1L<<guess);
            lbit=guess;
            guess=(hbit+lbit)/2;
            //printf("log2(%ld) %d<%d<%d\n",x,lbit,guess,hbit);
        }
    }
    if( (x>(1L<<guess)) ) ++guess;
    printf("log2(x%ld)=r%d\n",x,guess);
    return(guess);
}

আর একটি বাইনারি অনুসন্ধান পদ্ধতি, সম্ভবত আরও পাঠযোগ্য,

unsigned int
Log2_c(unsigned long x)
{
    unsigned long v = x;
    unsigned int bits = sizeof(x)*8;
    unsigned int step = bits;
    unsigned int res = 0;
    for( step = bits/2; step>0; )
    {
        //printf("log2(%ld) v %d >> step %d = %ld\n",x,v,step,v>>step);
        while ( v>>step ) {
            v>>=step;
            res+=step;
            //printf("log2(%ld) step %d res %d v>>step %ld\n",x,step,res,v);
        }
        step /= 2;
    }
    if( (x>(1L<<res)) ) ++res;
    printf("log2(x%ld)=r%ld\n",x,res);
    return(res);
}

এবং কারণ আপনি এগুলি পরীক্ষা করতে চান,

int main()
{
    unsigned long int x = 3;
    for( x=2; x<1000000000; x*=2 ) {
        //printf("x %ld, x+1 %ld, log2(x+1) %d\n",x,x+1,Log2(x+1));
        printf("x %ld, x+1 %ld, log2_a(x+1) %d\n",x,x+1,Log2_a(x+1));
        printf("x %ld, x+1 %ld, log2_b(x+1) %d\n",x,x+1,Log2_b(x+1));
        printf("x %ld, x+1 %ld, log2_c(x+1) %d\n",x,x+1,Log2_c(x+1));
    }
    return(0);
}

1

এটি 'এখনও অন্য' পদ্ধতির কারণে এটি রাখা, ইতিমধ্যে প্রদত্ত অন্যদের থেকে আলাদা বলে মনে হচ্ছে।

আয় -1যদি x==0, অন্যথায় floor( log2(x)) (সর্বোচ্চ ফলাফলের 31)

32 থেকে 4 বিট সমস্যা হ্রাস করুন, তারপরে একটি টেবিল ব্যবহার করুন। সম্ভবত অবাস্তব, তবে বাস্তববাদী।

__builtin_clzপোর্টেবিলিটি সমস্যার কারণে যখন আমি ব্যবহার করতে চাই না তখন এটিই আমি ব্যবহার করি ।

এটি আরও কমপ্যাক্ট করতে, পরিবর্তে হ্রাস করার জন্য একটি লুপ ব্যবহার করতে পারে, প্রতিবার 4 থেকে আর যোগ করে, সর্বোচ্চ 7 পুনরাবৃত্তি। বা কিছু সংকর, যেমন (b৪ বিটের জন্য): লুপ 8 এ কমানোর জন্য, 4 টি হ্রাস করার পরীক্ষা।

int log2floor( unsigned x ){
   static const signed char wtab[16] = {-1,0,1,1, 2,2,2,2, 3,3,3,3,3,3,3,3};
   int r = 0;
   unsigned xk = x >> 16;
   if( xk != 0 ){
       r = 16;
       x = xk;
   }
   // x is 0 .. 0xFFFF
   xk = x >> 8;
   if( xk != 0){
       r += 8;
       x = xk;
   }
   // x is 0 .. 0xFF
   xk = x >> 4;
   if( xk != 0){
       r += 4;
       x = xk;
   }
   // now x is 0..15; x=0 only if originally zero.
   return r + wtab[x];
}

1

ওও, অনেক উত্তর ছিল। একটি পুরানো প্রশ্নের উত্তর দেওয়ার জন্য আমি দুঃখিত নই।

int result = 0;//could be a char or int8_t instead
if(value){//this assumes the value is 64bit
    if(0xFFFFFFFF00000000&value){  value>>=(1<<5); result|=(1<<5);  }//if it is 32bit then remove this line
    if(0x00000000FFFF0000&value){  value>>=(1<<4); result|=(1<<4);  }//and remove the 32msb
    if(0x000000000000FF00&value){  value>>=(1<<3); result|=(1<<3);  }
    if(0x00000000000000F0&value){  value>>=(1<<2); result|=(1<<2);  }
    if(0x000000000000000C&value){  value>>=(1<<1); result|=(1<<1);  }
    if(0x0000000000000002&value){  result|=(1<<0);  }
}else{
  result=-1;
}

এই উত্তরটি অন্য উত্তরের সাথে বেশ মিল oh ওহ ভাল।


শিফট পরিমাণ হিসাবে লিখতে 1<<kএকটি দুর্দান্ত স্পর্শ। মুখোশগুলির কী হবে? (1 << (1<<k-1)-1<< (1<<k-1)? ( most optimal? আপনি একটি চমকপ্রদ তুলনা করুন?)
গ্রেইবার্ড

@ গ্রেইবার্ড আপনি যদি এই প্রশ্নের সম্পাদনাগুলি লক্ষ্য করেন তবে আপনি দেখতে পাবেন যখন আমি "অনুকূল" অংশটি যুক্ত করব। আমি আমার উত্তর পরিবর্তন করার সাথে সাথে এটি সরিয়ে দিতে ভুলে গেছি। এছাড়াও আমি নিশ্চিত করুন যে আপনি কেন বলছ হয় নই মুখোশ? (কি মুখোশগুলি? আমি আপনাকে অনুসরণ করছি না)
হ্যারি সোভেনসন

( (বিআইটি) মাস্ক নির্বাচন করতে / স্পষ্ট বিট বেছে বেছে / ব্যবহৃত ব্যবহৃত মান &এবং &~।) আপনি পছন্দ দ্বারা হেক্স ধ্রুবক প্রতিস্থাপন করতে পারে ((type)1<<(1<<k))-1<<(1<<k)
গ্রেইবার্ড

ওহ ঠিক আছে, আমি মুখোশ ব্যবহার করছি, আমি এটি সম্পর্কে পুরোপুরি ভুলে গেছি। আমি কয়েক মাস আগে এর উত্তর দিয়েছি ... - হুমমম, ঠিক আছে যেহেতু এটি সংকলনের সময় মূল্যায়ন করা হয়েছে আমি বলি এটি হেক্স মানের সাথে সমান । তবে একটি ক্রিপ্টিক এবং একটি হেক্সাডেসিমাল।
হ্যারি স্পেনসন 21

0

কোড:

    // x>=1;
    unsigned func(unsigned x) {
    double d = x ;
    int p= (*reinterpret_cast<long long*>(&d) >> 52) - 1023;
    printf( "The left-most non zero bit of %d is bit %d\n", x, p);
    }

অথবা Y = 1 সেট করে FPU নির্দেশ FYL2X (Y * লগ 2 এক্স) এর পূর্ণসংখ্যার অংশটি পান


uhhhhh। কি? কিভাবে এই কাজ করে? এটি কোনও উপায়ে বহনযোগ্য?
আন্ডারস্কোর_১১

উইন্ডোতে কোডগুলি পোর্টেবল। FYL2X () ফাংশনটি একটি fpu নির্দেশনা, তবে পোর্ট করা হতে পারে এবং কিছু এফপিইউ / গণিতের লাইব্রেরিতে পাওয়া যেতে পারে।
জেমিন

@ আসর_কেন_ডি এটি কাজ করে কারণ ভাসমান পয়েন্ট সংখ্যাগুলি স্বাভাবিক করা হয় ... মান্টিসা বিটগুলিকে ডাবল শিফটে রূপান্তর করা নেতৃস্থানীয় শূন্যগুলি নির্মূল করার জন্য এবং এই কোডটি এক্সপোনেন্টটি বের করে এবং বিট স্থানান্তরিত বিটের সংখ্যা নির্ধারণ করতে এটি সামঞ্জস্য করে। এটি অবশ্যই আর্কিটেকচার-স্বতন্ত্র নয়, তবে এটি সম্ভবত আপনি যে কোনও মেশিনে এসে পৌঁছেছেন on
জিম বাল্টার

এটি এই উত্তরের একটি বিকল্প সংস্করণ , কর্মক্ষমতা এবং বহনযোগ্যতার বিষয়ে মন্তব্যের জন্য সেখানে দেখুন। (বিশেষত টাইপ-পানিংয়ের জন্য পয়েন্টার ingালাইয়ের অ-বহনযোগ্যতা)) এটি কেবলমাত্র উচ্চ 32 বিটগুলি পুনরায় লোড করতে অ্যাড্রেস গণিত ব্যবহার করে double, এটি সম্ভবত ভাল যদি এটি টাইপ-পানের পরিবর্তে অন্য কোনও উপায়ে স্টোর / পুনরায় লোড করে, যেমন movqআপনি এখানে পেতে পারেন মত একটি নির্দেশের সাথে x86 এ।
পিটার কর্ডস

এছাড়াও আমার [সেই উত্তরের মন্তব্যে] নোট করুন, যেখানে আমি কঠোর সতর্কতা দিয়ে বলছি যে এই পদ্ধতিটি (কমপক্ষে) পরিসরের মানগুলির জন্য ভুল উত্তর দেয় [7FFFFFFFFFFFFE00 - 7FFFFFFFFFFFFFFF]
গ্লেন স্লেডেন

0

অন্য একটি পোস্টার একটি বাইট-ওয়াইড লুকআপ ব্যবহার করে একটি লুক- টেবিল সরবরাহ করেছে । যদি আপনি একটি বিট আরো কর্মক্ষমতা সংযোজিত আউট (ঠিক 256 লুকআপ এন্ট্রি পরিবর্তে মেমরি 32K খরচে) এখানে একটি সমাধান একটি ব্যবহার করছে চান 15-বিট লুকআপ টেবিল , এ সি # 7 জন্য .NET

আকর্ষণীয় অংশটি টেবিলটি শুরু করে দিচ্ছে। যেহেতু এটি অপেক্ষাকৃত ছোট একটি ব্লক যা আমরা প্রক্রিয়াটির আজীবন চাই, তাই আমি এটি ব্যবহার করে পরিচালনা না করা মেমরি বরাদ্দ করি Marshal.AllocHGlobal। আপনি দেখতে পাচ্ছেন, সর্বাধিক পারফরম্যান্সের জন্য পুরো উদাহরণটি স্থানীয় হিসাবে লেখা হয়েছে:

readonly static byte[] msb_tab_15;

// Initialize a table of 32768 bytes with the bit position (counting from LSB=0)
// of the highest 'set' (non-zero) bit of its corresponding 16-bit index value.
// The table is compressed by half, so use (value >> 1) for indexing.
static MyStaticInit()
{
    var p = new byte[0x8000];

    for (byte n = 0; n < 16; n++)
        for (int c = (1 << n) >> 1, i = 0; i < c; i++)
            p[c + i] = n;

    msb_tab_15 = p;
}

উপরের কোডের মাধ্যমে টেবিলটির এককালীন সূচনা প্রয়োজন। এটি কেবল পঠনযোগ্য তাই একক গ্লোবাল অনুলিপি সমবর্তী অ্যাক্সেসের জন্য ভাগ করা যায়। এই টেবিলটির সাহায্যে আপনি দ্রুত পূর্ণসংখ্যার লগ 2 সন্ধান করতে পারেন , যা আমরা এখানে যা খুঁজছি তা সমস্ত বিভিন্ন পূর্ণসংখ্যার প্রস্থের জন্য (8, 16, 32 এবং 64 বিট)।

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

উলং (-৪-বিট) সংস্করণ

/// <summary> Index of the highest set bit in 'v', or -1 for value '0' </summary>
public static int HighestOne(this ulong v)
{
    if ((long)v <= 0)
        return (int)((v >> 57) & 0x40) - 1;      // handles cases v==0 and MSB==63

    int j = /**/ (int)((0xFFFFFFFFU - v /****/) >> 58) & 0x20;
    j |= /*****/ (int)((0x0000FFFFU - (v >> j)) >> 59) & 0x10;
    return j + msb_tab_15[v >> (j + 1)];
}

uint (32-বিট) সংস্করণ

/// <summary> Index of the highest set bit in 'v', or -1 for value '0' </summary>
public static int HighestOne(uint v)
{
    if ((int)v <= 0)
        return (int)((v >> 26) & 0x20) - 1;     // handles cases v==0 and MSB==31

    int j = (int)((0x0000FFFFU - v) >> 27) & 0x10;
    return j + msb_tab_15[v >> (j + 1)];
}

উপরের জন্য বিভিন্ন ওভারলোড

public static int HighestOne(long v) => HighestOne((ulong)v);
public static int HighestOne(int v) => HighestOne((uint)v);
public static int HighestOne(ushort v) => msb_tab_15[v >> 1];
public static int HighestOne(short v) => msb_tab_15[(ushort)v >> 1];
public static int HighestOne(char ch) => msb_tab_15[ch >> 1];
public static int HighestOne(sbyte v) => msb_tab_15[(byte)v >> 1];
public static int HighestOne(byte v) => msb_tab_15[v >> 1];

এটি একটি সম্পূর্ণ, কার্যক্ষম সমাধান যা আমি বিশেষায়িত পারফরম্যান্স পরীক্ষার জোয়ারের সাথে তুলনা করে এমন অনেক বিকল্পের জন্য নেট। নেট 4.7.2 এ সেরা পারফরম্যান্সের প্রতিনিধিত্ব করে। এর কয়েকটি নীচে উল্লেখ করা হয়েছে। পরীক্ষার প্যারামিটারগুলি সমস্ত 65 বিট পজিশনের সমান ঘনত্ব ছিল, যেমন, 0 ... 31/63 প্লাস মান 0(যা ফলাফল -1 প্রদান করে)। লক্ষ্য সূচকের অবস্থানের নীচে বিটগুলি এলোমেলোভাবে পূরণ করা হয়েছিল। পরীক্ষাগুলি কেবল এক্স 64 ছিল , রিলিজ মোডে, জেআইটি-অপ্টিমাইজেশান সক্ষম হয়েছিল।




এখানেই আমার আনুষ্ঠানিক উত্তরের সমাপ্তি; নিম্নলিখিতগুলির সাথে সম্পর্কিত কিছু পরীক্ষামূলক নোট এবং উত্স কোডের লিঙ্কগুলি পরীক্ষার সাথে সম্পর্কিত বিকল্প পরীক্ষার্থীদের জন্য আমি উপরের কোডটির কার্য সম্পাদন এবং যথার্থতা যাচাই করতে দৌড়ে এসেছি।


উপরের সরবরাহিত সংস্করণটি, টব 16 এ কোড করে অনেক রান করার ক্ষেত্রে নিয়মিত বিজয়ী ছিল। সক্রিয় কর্মরত / স্ক্র্যাচ ফর্মে এই বিভিন্ন প্রার্থীকে এখানে , এখানে এবং এখানে পাওয়া যাবে

 1 জন প্রার্থী। হাইস্টেস্টন_টব 16 এ 622,496
 2 জন প্রার্থী .উচ্চেস্টে_নটব 16 সি 628,234
 3 জন পরীক্ষার্থী igউচ্চেস্টে abণ_ট্যাব 8 এ 649,146
 4 জন প্রার্থী igউচ্চেস্টে_ট্যাব 8 বি 656,847
 5 জন প্রার্থী .উচ্চেস্টে_নটব 16 বি 657,147
 6 জন পরীক্ষার্থী। হাইস্টেস্ট ওয়ান_ট্যাব 16 ডি 659,650
 7 _ হাইস্টেস্ট_নে_বিট_ ইউএনএএনএএনএইজেডি.হিগস্টেস্টে_উ 702,900
 8 ডি_বুরুজান.আইডেক্সঅফএমএসবি 709,672
 9 _લ્ડ_2.উচ্চতম এক_ ওল্ড 715,810
10 _তম_এ.হিগাস্টেস্টে 757,188
11 _old_1.HighestOne_Old1 757,925
12 _তম_এ.হিগাস্টেস্টন 5 (অনিরাপদ) 760,387
13 _টেষ্ট_বি.হিগস্টেস্টন 8 (অনিরাপদ) 763,904
14 _তম_এ.হিগাস্টেস্টন 3 (অনিরাপদ) 766,433
15 _তম_এ.হিগস্টেস্টন 1 (অনিরাপদ) 767,321
16 _তম_এ.হিগস্টেস্টন 4 (অনিরাপদ) 771,702
17 _তম_বি.হিগস্টেস্টন 2 (অনিরাপদ) 772,136
18 _তম_বি.হিগাস্টেস্টন 1 (অনিরাপদ) 772,527
19 _তম_বি.হিগস্টেস্টন 3 (অনিরাপদ) 774,140
20 _তম_এ.হিগস্টেস্টন 7 (অনিরাপদ) 774,581
21 _তম_বি.হিগাস্টনওন 7 (অনিরাপদ) 775,463
22 _তম_এ.হিগাস্টেস্টন 2 (অনিরাপদ) 776,865
23 জন প্রার্থী igউচ্চেস্টে_নোটাব 777,698
24 _তম_বি.হিগস্টেস্টন 6 (অনিরাপদ) 779,481
25 _তম_এ.হিগাস্টেস্টন 6 (অনিরাপদ) 781,553
26 _ সেরা_বি.হিগস্টেস্টন 4 (অনিরাপদ) 785,504
27 _তম_বি.হিগস্টেস্টন 5 (অনিরাপদ) 789,797
28 _তম_এ.হিগস্টেস্টন0 (অনিরাপদ) 809,566
29 _তম_বি.হিগাস্টেস্টন 0 (অনিরাপদ) 814,990
30 _ হাইস্টেস্ট_ওন_বিট.হিগস্টেস্ট 824,345
30 _বিটারে_একটি.আর্টলিফাইন্ডমোস্টসিগনিফিক্যান্ট বিট 894,069
31 জন প্রার্থী igউচ্চেস্টে_নায়েভ 898,865

উল্লেখযোগ্য হ'ল ntdll.dll!RtlFindMostSignificantBitপি / ইনভোকের মাধ্যমে ভয়াবহ অভিনয় :

[DllImport("ntdll.dll"), SuppressUnmanagedCodeSecurity, SecuritySafeCritical]
public static extern int RtlFindMostSignificantBit(ulong ul);

এটি সত্যিই খুব খারাপ, কারণ এখানে পুরো আসল কাজটি রয়েছে:

    RtlFindMostSignificantBit:
        bsr rdx, rcx  
        mov eax,0FFFFFFFFh  
        movzx ecx, dl  
        cmovne      eax,ecx  
        ret

এই পাঁচটি লাইনের মধ্য দিয়ে উত্সাহিত দুর্বল অভিনয় আমি কল্পনা করতে পারি না, সুতরাং পরিচালিত / নেটিভ ট্রানজিশন জরিমানার জন্য দোষ চাপতে হবে। আমি আরও অবাক হয়ে গিয়েছিলাম যে পরীক্ষাটি সত্যই short128-বাইট (এবং 256-বাইট) byte(8-বিট) দেখার সারণীতে 32KB (এবং 64KB) (16-বিট) সরাসরি-অনুসন্ধান সারণীগুলির পক্ষে হয়েছিল । আমি ভেবেছিলাম নিম্নলিখিতটি 16-বিট লুকআপের সাথে আরও প্রতিযোগিতামূলক হবে তবে পরবর্তীকালে ধারাবাহিকভাবে এটিকে ছাপিয়ে দেওয়া হয়েছে:

public static int HighestOne_Tab8A(ulong v)
{
    if ((long)v <= 0)
        return (int)((v >> 57) & 64) - 1;

    int j;
    j =  /**/ (int)((0xFFFFFFFFU - v) >> 58) & 32;
    j += /**/ (int)((0x0000FFFFU - (v >> j)) >> 59) & 16;
    j += /**/ (int)((0x000000FFU - (v >> j)) >> 60) & 8;
    return j + msb_tab_8[v >> j];
}

শেষ কথাটি আমি উল্লেখ করব যে আমার ডিব্রুজন পদ্ধতিটি এর চেয়ে ভাল নয় didn't এই পদ্ধতিটি আমি আগে ব্যাপকভাবে ব্যবহার করেছি:

const ulong N_bsf64 = 0x07EDD5E59A4E28C2,
            N_bsr64 = 0x03F79D71B4CB0A89;

readonly public static sbyte[]
bsf64 =
{
    63,  0, 58,  1, 59, 47, 53,  2, 60, 39, 48, 27, 54, 33, 42,  3,
    61, 51, 37, 40, 49, 18, 28, 20, 55, 30, 34, 11, 43, 14, 22,  4,
    62, 57, 46, 52, 38, 26, 32, 41, 50, 36, 17, 19, 29, 10, 13, 21,
    56, 45, 25, 31, 35, 16,  9, 12, 44, 24, 15,  8, 23,  7,  6,  5,
},
bsr64 =
{
     0, 47,  1, 56, 48, 27,  2, 60, 57, 49, 41, 37, 28, 16,  3, 61,
    54, 58, 35, 52, 50, 42, 21, 44, 38, 32, 29, 23, 17, 11,  4, 62,
    46, 55, 26, 59, 40, 36, 15, 53, 34, 51, 20, 43, 31, 22, 10, 45,
    25, 39, 14, 33, 19, 30,  9, 24, 13, 18,  8, 12,  7,  6,  5, 63,
};

public static int IndexOfLSB(ulong v) =>
    v != 0 ? bsf64[((v & (ulong)-(long)v) * N_bsf64) >> 58] : -1;

public static int IndexOfMSB(ulong v)
{
    if ((long)v <= 0)
        return (int)((v >> 57) & 64) - 1;

    v |= v >> 1; v |= v >> 2;  v |= v >> 4;   // does anybody know a better
    v |= v >> 8; v |= v >> 16; v |= v >> 32;  // way than these 12 ops?
    return bsr64[(v * N_bsr64) >> 58];
}

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


1
আধুনিক x86 সিপিইউতে এল 1 ডি ক্যাশেটি কেবলমাত্র 32KB। যদি আপনি একই মানগুলি বারবার ব্যবহার না করেন তবে একটি বড় LUT ছোট LUT এর চেয়ে খারাপ হতে পারে। আপনি যদি না হন তবে আপনি ঘন ঘন ক্যাশে মিস করবেন।
পিটার কর্ডেস

0

আমার নম্র পদ্ধতিটি খুব সহজ:

এমএসবি (এক্স) = আইএনটি [লগ (এক্স) / লগ (2)]

অনুবাদ: x এর এমএসবি হ'ল লগ অফ বেস 2 দ্বারা বিভক্ত বেসের লগের পূর্ণসংখ্যা মান।

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


আপনার আগ্রহী সমস্ত যদি বিকাশকারীর দক্ষতা হয় তবে তা কাজ করে। আপনি যদি রানটাইম দক্ষতা চান তবে আপনার বিকল্প অ্যালগরিদম প্রয়োজন।
মিক্কো রেন্টালাইনেন

রাউন্ডঅফ ত্রুটির কারণে এটি ব্যর্থ হতে পারে। উদাহরণস্বরূপ, CPython 2 এবং 3, int(math.log((1 << 48) - 1) / math.log(2))48. হয়
benrg

0

এখানে সি এর জন্য একটি দ্রুত সমাধান রয়েছে যা জিসিসি এবং কলঙ্কে কাজ করে ; অনুলিপি এবং আটকানো প্রস্তুত।

#include <limits.h>

unsigned int fls(const unsigned int value)
{
    return (unsigned int)1 << ((sizeof(unsigned int) * CHAR_BIT) - __builtin_clz(value) - 1);
}

unsigned long flsl(const unsigned long value)
{
    return (unsigned long)1 << ((sizeof(unsigned long) * CHAR_BIT) - __builtin_clzl(value) - 1);
}

unsigned long long flsll(const unsigned long long value)
{
    return (unsigned long long)1 << ((sizeof(unsigned long long) * CHAR_BIT) - __builtin_clzll(value) - 1);
}

এবং সি ++ এর জন্য কিছুটা উন্নত সংস্করণ ।

#include <climits>

constexpr unsigned int fls(const unsigned int value)
{
    return (unsigned int)1 << ((sizeof(unsigned int) * CHAR_BIT) - __builtin_clz(value) - 1);
}

constexpr unsigned long fls(const unsigned long value)
{
    return (unsigned long)1 << ((sizeof(unsigned long) * CHAR_BIT) - __builtin_clzl(value) - 1);
}

constexpr unsigned long long fls(const unsigned long long value)
{
    return (unsigned long long)1 << ((sizeof(unsigned long long) * CHAR_BIT) - __builtin_clzll(value) - 1);
}

কোড ধরে নেয় যে তা valueহবে না 0। আপনি যদি 0 এর অনুমতি দিতে চান তবে আপনাকে এটি সংশোধন করতে হবে।


0

আমি ধরে নিলাম আপনার প্রশ্নটি একটি পূর্ণসংখ্যার জন্য (নীচে v নামে পরিচিত) এবং স্বাক্ষরবিহীন পূর্ণসংখ্যার জন্য নয়।

int v = 612635685; // whatever value you wish

unsigned int get_msb(int v)
{
    int r = 31;                         // maximum number of iteration until integer has been totally left shifted out, considering that first bit is index 0. Also we could use (sizeof(int)) << 3 - 1 instead of 31 to make it work on any platform.

    while (!(v & 0x80000000) && r--) {   // mask of the highest bit
        v <<= 1;                        // multiply integer by 2.
    }
    return r;                           // will even return -1 if no bit was set, allowing error catch
}

আপনি যদি সাইনটি আমলে না নিয়ে এটিকে কাজ করতে চান তবে আপনি একটি অতিরিক্ত 'ভি << = 1' যোগ করতে পারেন; লুপের আগে (এবং r মানটি 30 অনুযায়ী পরিবর্তন করুন)। আমি কিছু ভুলে গেছি দয়া করে আমাকে জানান। আমি এটি পরীক্ষা করেছি না তবে এটি ঠিক কাজ করা উচিত।


v <<= 1হয় অনির্ধারিত আচরণ (UB) যখন v < 0
chux - মনিকা

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