একটি অ্যারের মধ্যম গণনা করার সময় কেন শুরু + (শেষ - শুরু) / 2 ওভার (শুরু + শেষ) / 2 পছন্দ করবেন?


160

আমি প্রোগ্রামাররা সূত্রটি ব্যবহার করতে দেখেছি

mid = start + (end - start) / 2

পরিবর্তে সহজ সূত্র ব্যবহার

mid = (start + end) / 2

অ্যারে বা তালিকার মধ্যবর্তী উপাদানটি সন্ধান করার জন্য।

তারা আগেরটি কেন ব্যবহার করবে?


51
বন্য অনুমান: (start + end)ওভারফ্লো হতে পারে, যখন (end - start)পারে না।
ক্যাডানিলুক

30
কারণ পরবর্তীকালে কখন কাজ করে না startএবং endপয়েন্টার হয়।
ensc


20
start + (end - start) / 2এছাড়াও শব্দার্থিক অর্থ বহন করে: (end - start)দৈর্ঘ্য, তাই এই বলে: start + half the length
njzk2

2
@ LưuVĩnhPhúc: এই প্রশ্নের সেরা উত্তর এবং সর্বাধিক ভোট নেই? যদি তা হয় তবে অন্যান্য প্রশ্নগুলি সম্ভবত এটির একটি ডুপ হিসাবে বন্ধ করা উচিত। পদগুলির বয়স অপ্রাসঙ্গিক।
নিস এনজিস্ট্রম

উত্তর:


218

তিনটি কারণ রয়েছে।

প্রথমত, start + (end - start) / 2আপনি পয়েন্টার ব্যবহার করছেন এমনকী পর্যন্ত কাজ করে, যতক্ষণ end - startনা ওভারফ্লো 1 না ।

int *start = ..., *end = ...;
int *mid = start + (end - start) / 2; // works as expected
int *mid = (start + end) / 2;         // type error, won't compile

সব দ্বিতীয়ত, start + (end - start) / 2হবে ওভারফ্লো যদি না startএবং endবৃহৎ ইতিবাচক নম্বর আছে। স্বাক্ষরিত অপারেশনগুলির সাথে, ওভারফ্লো অপরিশোধিত:

int start = 0x7ffffffe, end = 0x7fffffff;
int mid = start + (end - start) / 2; // works as expected
int mid = (start + end) / 2;         // overflow... undefined

(দ্রষ্টব্য যে end - startউপচে পড়তে পারে তবে কেবল start < 0বা যদি end < 0।)

বা স্বাক্ষরবিহীন পাটিগণিত সহ, ওভারফ্লো সংজ্ঞায়িত করা হলেও আপনাকে ভুল উত্তর দেয়। যাইহোক, স্বাক্ষরযুক্ত অপারেটদের জন্য, start + (end - start) / 2যতক্ষণ না ওভারফ্লো হবে না end >= start

unsigned start = 0xfffffffeu, end = 0xffffffffu;
unsigned mid = start + (end - start) / 2; // works as expected
unsigned mid = (start + end) / 2;         // mid = 0x7ffffffe

অবশেষে, আপনি প্রায়শই startউপাদানটির দিকে যেতে চান ।

int start = -3, end = 0;
int mid = start + (end - start) / 2; // -2, closer to start
int mid = (start + end) / 2;         // -1, surprise!

পাদটিকা

1 সি স্ট্যান্ডার্ড অনুসারে, যদি পয়েন্টার বিয়োগের ফলাফলটি হিসাবে হিসাবে উপস্থাপনযোগ্য না হয় ptrdiff_t, তবে আচরণটি অপরিবর্তিত। যাইহোক, অনুশীলনে, এর জন্য charকমপক্ষে পুরো ঠিকানা স্পেস ব্যবহার করে একটি অ্যারে বরাদ্দ করা দরকার requires


ফল (end - start)মধ্যে signed intকেস undefined যখন এটা উপচে।
ensc

আপনি কি প্রমাণ করতে পারবেন না যে end-startওভারফ্লো? এএফআইকে আপনি যদি নেতিবাচক হন startতবে এটিকে ওভারফ্লো করা সম্ভব হওয়া উচিত। অবশ্যই, আপনি গড় গণনা করার সময় বেশিরভাগ সময় আপনি জানেন যে মানগুলি >= 0...
বাকুরিউ

12
@ বাকুরিউ: এমন কিছু প্রমাণ করা অসম্ভব যা সত্য নয়।
ডায়েটারিচ এপ্প

4
এটি সিটিতে বিশেষ আগ্রহী, যেহেতু পয়েন্টার বিয়োগ (স্ট্যান্ডার্ড অনুযায়ী) নকশার দ্বারা ভেঙে গেছে। এত বড় অ্যারে তৈরি করার বাস্তবায়নের অনুমতি রয়েছে end - startযা অপরিজ্ঞাত, কারণ বস্তুর আকারগুলি স্বাক্ষরযুক্ত নয় যেখানে পয়েন্টার পার্থক্যগুলি স্বাক্ষরিত। সুতরাং end - start, "এমনকি পয়েন্টার ব্যবহার কার্য্য সকল" এছাড়াও একরকম নিচে অ্যারের আকার রাখা আপনার দেওয়া PTRDIFF_MAX। স্ট্যান্ডার্ডের সাথে ফর্সা হওয়ার জন্য, এটি মেমরির মানচিত্রের অর্ধেক আকারের থেকে বেশিরভাগ আর্কিটেকচারে কোনও বাধা নয় size
স্টিভ জেসপ

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

18

আমরা এই সত্যটি প্রদর্শনের জন্য একটি সাধারণ উদাহরণ নিতে পারি। মনে করুন কোনও নির্দিষ্ট বড় অ্যারেতে আমরা ব্যাপ্তির মধ্যবিন্দুটি অনুসন্ধান করার চেষ্টা করছি [1000, INT_MAX]। এখন, ডাটা টাইপটি সংরক্ষণ করতে পারে INT_MAXএমন বৃহত্তম মান int। এমনকি এতে 1যুক্ত করা হলেও চূড়ান্ত মানটি নেতিবাচক হয়ে উঠবে।

এছাড়াও, start = 1000এবং end = INT_MAX

সূত্র ব্যবহার হচ্ছে: (start + end)/2,

মিড পয়েন্ট হবে

(1000 + INT_MAX)/2= -(INT_MAX+999)/2, যা নেতিবাচক এবং যদি আমরা এই মানটি ব্যবহার করে সূচী করার চেষ্টা করি তবে সেগমেন্টেশন ফল্ট দিতে পারে

তবে, সূত্রটি ব্যবহার করে (start + (end-start)/2), আমরা পাই:

(1000 + (INT_MAX-1000)/2)= (1000 + INT_MAX/2 - 500)= (INT_MAX/2 + 500) যা উপচে পড়বে না


1
আপনি যদি 1 এ যোগ করেন INT_MAXতবে ফলাফলটি নেতিবাচক হবে না, তবে অপরিবর্তিত রয়েছে।
celtschk

তাত্ত্বিকভাবে, হ্যাঁ ব্যবহারিকভাবে এটি মোড়ানো-চারপাশে অনেক সময় থেকে INT_MAXযায় -INT_MAX। যদিও এটির উপর নির্ভর করা একটি খারাপ অভ্যাস।
মাস্ত

17

অন্যরা ইতিমধ্যে যা বলেছে তার সাথে যুক্ত হওয়ার জন্য, প্রথমটি এর অর্থ পরিষ্কারভাবে ব্যাখ্যা করে যারা কম গাণিতিক বিবেচিত তাদের কাছে:

mid = start + (end - start) / 2

হিসাবে পড়া:

মাঝের সমান দৈর্ঘ্যের অর্ধেক আরম্ভ।

যেহেতু:

mid = (start + end) / 2

হিসাবে পড়া:

মাঝের সমাপ্তি সমাপ্তির শেষের অর্ধেক সমান

যা প্রথমটির মতো পরিষ্কার বলে মনে হয় না, অন্তত এমনটি প্রকাশ করার সময়।

কোস যেমন উল্লেখ করেছেন এটি পড়তে পারে:

মিড শুরু এবং শেষের সমান

যা পরিষ্কার তবে এখনও কমপক্ষে আমার মতে, প্রথমটির মতো পরিষ্কার।


3
আমি আপনার পয়েন্টটি দেখতে পাচ্ছি, তবে এটি সত্যিই প্রসারিত। আপনি যদি "ই - এস" দেখেন এবং "দৈর্ঘ্য" মনে করেন তবে আপনি অবশ্যই "(গুলি + ই) / 2" দেখতে পাবেন এবং "গড়" বা "মধ্য" ভাবেন।
djechlin

2
@ ডিজেচলিন প্রোগ্রামারগণ গণিতে দুর্বল। তারা তাদের কাজ করতে ব্যস্ত। তাদের গণিত ক্লাসে অংশ নেওয়ার সময় নেই।
ছোট এলিয়েন

1

শুরু + (শেষ-শুরু) / 2 সম্ভাব্য ওভারফ্লো এড়াতে পারে, উদাহরণস্বরূপ শুরু = 2 ^ 20 এবং শেষ = 2 ^ 30

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