الگوریتم با (O(n برای یافتن دو عنصر با مجموع X در یک آرایه مرتب - هفت خط کد انجمن پرسش و پاسخ برنامه نویسی

الگوریتم با (O(n برای یافتن دو عنصر با مجموع X در یک آرایه مرتب

+2 امتیاز
سلام

اگر S یک مجموعه از اعداد حقیقی باشد و یک عدد حقیقی x داشته باشیم که

به دنبال 2 عدد از این مجموعه هستیم که جمع آنها دقیقا برابر x باشه

اگر فرض کنیم که آرایه از قبل مرتب هست الگوریتمی با زمان n ارائه دهید

( تمرین 4-8 کتاب Steven Skiena )
سوال شده دی 14, 1393  بوسیله ی Pakniat (امتیاز 247)   9 21 32
ویرایش شده دی 15, 1393 بوسیله ی BlueBlade
البته این سوال  یک حالت خاص برای یکی از مسایل NP_Complete یعنی Subset sum problem هستش. در sub set problem فقط مهم هست که یکسری عدد پیدا کنیم که جمع دقیقا x بشه . خیلی جالبه که یک تغییر به این سادگی  میتونه یک مساله از مرتبه n رو به یک مساله غیر قابل حل تبدیل کنه :)

1 پاسخ

+2 امتیاز
 
بهترین پاسخ

کد ++C :

std::pair<int, int> find_equal_sum(const std::vector<int>& arr,const int x)
{
    int max = arr[arr.size()-1];//O(1)
	//std::vector<int> direct_index_hash(max+1, 0);//O(n)
	std::vector<int> direct_index_hash(max+1, 0);//O(k)
	for (size_t i = 0; i < arr.size(); i++)//O(n)
		direct_index_hash[arr[i]] ++;
	for (size_t i = 0; i < arr.size(); i++)//O(n)
	{
		int diff = x - arr[i];
		if (diff <0) 
			continue;
		int diff_count = direct_index_hash[diff];
		if (diff == arr[i]){
			diff_count--;
		}
		if (diff_count >0)
			return std::make_pair(arr[i], diff);
	}
	return std::make_pair(-1, -1);//agar 2 onsoor ba majmoo x vojood nadasht
}

اجرای کد 

توضیح الگوریتم :

ورودی :

  • arr : آرایه  مرتب
  • X : جمع 2 عنصر از آرایه باید این عدد شود.

خروجی :

  • pair(a,b : دو عنصری که جمعشان X هست .

 

  1.  متغیر i را 0 می گذاریم
  2.  تفاضل عدد x با عنصر i ام arr را حساب می کنیم diff=x-arr[i
  3. مقدار diff رو درآرایه مرتب arr  جست و جو می کنیم اگر موجود بود یعنی [arr[i و diff  دو عنصر مورد نظر هستند .
  4. i را یک واحد اضافه می کنیم مرحله 2 را تا رسیدن به  انتهای آرایه یا جواب تکرار می کنیم.

 

چون قسمت 2 الگوریتم در بدترین حالت n بار تکرار میشه جست و جو باید در (1)O انجام بشه تا پیچیدگی کلی n بشه پس تنها راهی که وجود داره استفاده از hash table از نوع direct addressing هست (وکتور direct_index_hash داخل کد بالا)

میشه گفت این الگوریتم  تقریبا مشابه counting sort هست .

ویرایش:

البته پیچیدگی الگوریتم بالا (O(n+k هست که k طول بزرگترین عدد آرایست (من ساختن وکتور در خط دوم رو (O(n گرفته بودم که درست نیست ).چون سوال حرفی درباره k نزده k خودش میتونه با سرعتی بیشتر از n مثلا n^2 رشد داشته باشه پس  نمیشه گفت الگوریتم بالا در بدترین حالت (O(n هست(فقط در صورتی (O(n هست  که بازه اعداد حقیقی ورودی تابع محدود باشه) 

اگر آرایه sort شده میشه از این روش با (O(n استفاده کرد :

#include <algorithm>
#include <iostream>
#include <vector>

std::pair<int, int> find_equal_sum(const std::vector<int>& arr, const int x)
{
	if (arr.size()>1){
		int first = 0;
		int last = arr.size() - 1;
		while (first < last){//O(n)
			if (arr[first] + arr[last]>x)
				--last;
			else if (arr[first] + arr[last]<x)
				++first;
			else if (arr[first] + arr[last] == x){
				return std::make_pair(arr[first], arr[last]);
				break;
			}
		}
	}
	return std::make_pair(-1, -1);
}
int main()
{
	const int x = 19;
	std::vector<int> arr{ 5, 7, 8, 11, 12 };
	auto res = find_equal_sum(arr, x);
	std::cout << res.first << " " << res.second;
	return 0;
}

 

پاسخ داده شده دی 15, 1393 بوسیله ی BlueBlade (امتیاز 15,315)   15 18 89
انتخاب شد دی 23, 1393 بوسیله ی Pakniat
چون بازه اعداد حقیقی هست اگر در hash table در بدترین حالت hash function ما به یک درایه نگاشت کرد چی؟

در عمل اگر با ایده ی counting sort هم آرایه ای به اندازه طول اعداد حقیقی می خواهیم
این جا آرایه direct_index_hash فقط تعداد یک عنصر رو ذخیره می کنه پس اشکالی پیش نمیاد
چون سایز اعداد حقیقی ثابت هست و به طول ورودی مرتبط نیست تاثیری روی O نمی تونه بزاره .(فقط ثابت C بزرگتر میشه )
ما n عدد در دامنه ی اعداد حقیقی داریم به نظرتون میشه جدولی با این اندازه داشت؟
از کجا باید مطمئن بود که collision نداریم؟
اگر منظورتون https://www.cs.auckland.ac.nz/software/AlgAnim/hash_tables.html
هست ازجدول آدرس دهی مستقیم اونطور که در کتابها نوشته شده این روش اگر محموعه universe خیلی بزرگ باشه پیاده سازیش فقط در تئوری امکان پذیر هست
چرا فکر می کنید که داخل الگوریتم بالا وجود collision مهمه ؟
"ما n عدد در دامنه ی اعداد حقیقی داریم به نظرتون میشه جدولی با این اندازه داشت؟"
خیر الگوریتم counting sort برای دامنه های پیوسته نیست برای بازه های گسسته مثل int هست . برای بازه پیوسته  باید از ایده الگوریتم radix sort استفاده کنید .
عملی بودن یا نبودن هم یک بحث جدا هست ما فرض می کنیم که ماشین مورد نظر حافظه اش بی نهایت است. 10000000000000000n هم (O(n حساب میشه .
چون به تابع درهم سازی فکر می کنم
اگر عنصر اول ما 1 عنصر 2 ما 2 عنصر سوم 2 به توان 2 و.....عنصر n ما 2 به توان n اندازه جدول چی میشه؟
-----
اگر بحث فقط در تئوری هست پس چون ما همچین آرایه ای داریم و با o(1) درج می کنیم و با o(1)  جدول فشرده  می کنیم پس بدترین زمان مرتب سازی یک آرایه در o(1) هست  و بقیه الگوریتم ها ...چون عملی و غیر عملی بودن  بحث جدا ست.
ببینید ما اصلا از تابعی برای درهم سازی بالا استفاده نمی کنیم هر عنصر مستقیم قرار داده میشه داخل اندیس معادل .
بالا هم گفتم counting sort برای int کاربرد داره 2 به توان n از این محدوده میزنه بیرون .
و این که 2^n عدد بزرگی هست شما که بحث عملی هم براتون مهم هست به این فکر کردید که چطور باید 2 تا عدد با این اندازه رو با هم جمع کنید و با x مقایسه کنید ؟
-------
حتی در صورت فرض حافظه بی نهایت هم الگوریتم های کمی هستند که میشه این کارو انجام داد براشون چون ممکنه داده ورودی قابل تبدیل به اعداد صحیح نباشه .
2^n رو برای این گفتم که همچین آرایه قابل تصور نیست ، (مثلا به شما بگن اینو با زمان خطی برای ما بنویس شاید بگی تو این بازه ، خطی هست) مشکل پیاده سازی این مسئله با hash هست
یه چیزی اون بالا گفتید : "ما فرض می کنیم که ماشین مورد نظر حافظه اش بی نهایت است"
مرجعتون چی هست؟ این مطلب  تو نظریه محاسبات شنیدم درسته داریم ماشین تورینگ طراحی می کنیم اما  الگوریتم هایی که دیدم این فرض رو نکردند
"2^n رو برای این گفتم که همچین آرایه قابل تصور نیست  "
قابل تصور بودن تعریف دقیقی نداره . چند بار هم  گفتم  این الگوریتم  در عمل فقط برای بازه های کوچک جواب میده  کسی counting sort رو برای  2^200 انجام نمیده .(هر چند که در تئوری  (O(n هست)
"مرجعتون چی هست؟ این مطلب  تو نظریه محاسبات شنیدم درسته داریم ماشین تورینگ طراحی می کنیم اما  الگوریتم هایی که دیدم این فرض رو نکردند"
فکر کنیم برگردیم به اول بهتر باشه ! ببینید سوال این بود الگوریتمی طراجی کنید که (O(n باشه حالا شما می گی اگر عدد بزرگ باشه  فقط در تئوری قابل انجامه .
آیا این که فقط در تئوری قابل انجامه دلیل درستیه که بگیم (O(n نیست ؟! (به همین دلیل هم گفتم فرض می کنیم  حافظه بینهایت باشه ) الگوریتم های زیادی هستند که این طوری هستند  وفقط جنبه  تئوری  دارند نمونش الگوریتم ضرب ماتریس Coppersmith

در مورد مرجع هم ما  در تعریف  اوی بزرگ فرض می کنیم که n به سمت بی نهایت میره آیا واقعا در دنیای واقعی n می تونه به سمت بی نهایت بره ؟ (پس بشکل ضمنی فرض شده که ماشین حافظش بی نهایت هستش)
مرتب بودن شرط لازم و کافی این الگوریتم هست؟
نه نیازی به مرتب بودن نیست
این k که در ویرایش اشاره کردید  بحث بالا رو نقض نمی کنه؟اگر k وn^2باشه وکتور در این زمان ساخته میشه؟
کتاب زمان n رو درنظر گرفته
آره زمان ساخت وکتور این هست . کدوم کتاب ؟
بحث بالا در مورد زمان جست و جو بود که تغییری نمی کنه .
کتابی که در صورت سوال گفتم
طبق قضیه ماکزیمم گیری زمان اون الگوریتم k میشه نه n !؟ در حالت میانگین n اما Worst case اون k میشه

آره ا worst case میشه k .

best case میشه n (حالتی که k از n سرعت رشد کمتری داره)

,ولی میانگین هم  k میشه چون باید از همه حالت هایی که هم k و هم  n میتونن داشته باشن میانگین بگیریم ! که همچین چیزی میشه :( این میانگین پیچیدگی ساختن وکتور کد بالا هست. همین طور که مشخصه چند جمله ای هم نیست و بر حسب تعداد ارقام به شکل نمایی رشد می کنه ! پس طبیعتا سرعت رشد ساخت وکتور از میانگین جست و جو که n هست بیشتر هستش.)

 

ممنون
پس الگوریتم نمایی هست ,
با جستجوی دودویی حداقل nlgn هست که اما چجوری میشه با n پیاده سازی کرد
پیاده سازی با (O(n رو با فرضی که کتاب کرده (مرتب بودن آرایه ) داخل کد بالا گذاشتم . بعید میدونم در صورت مرتب نبودن راهی با پیچیدگی کمتر از nlogn وجود داشته باشه (احتمالا  قابل اثبات هم هست ( با روشی مثل اثبات upper bound  ا nlong الگوریتم های sort با مقایسه ))
...