बिग ओ, आप इसकी गणना कैसे करते / करते हैं?


881

सीएस में एक डिग्री वाले अधिकांश लोग निश्चित रूप से जानते होंगे कि बिग ओ किस लिए खड़ा है । यह मापने में हमारी मदद करता है कि एल्गोरिथ्म कितना अच्छा है।

लेकिन मैं उत्सुक हूं, आप अपने एल्गोरिदम की जटिलता की गणना या अनुमान कैसे लगाते हैं?


4
शायद आपको वास्तव में अपने एल्गोरिथ्म की जटिलता में सुधार करने की आवश्यकता नहीं है, लेकिन आपको कम से कम यह तय करने में सक्षम होना चाहिए ...
जेवियर नोडेट

5
मुझे यह बिग ओ, बिग ओमेगा और बिग थीटा का एक बहुत स्पष्ट स्पष्टीकरण मिला: xoax.net/comp/sci/algorithms/Lesson6.php
सैम

33
-1: आह, बिगओ का एक और दुरुपयोग। बिगओह सिर्फ एक अस्वाभाविक ऊपरी सीमा है और इसे किसी भी चीज़ के लिए इस्तेमाल किया जा सकता है और यह सिर्फ सीएस से संबंधित नहीं है। BigOh के बारे में बात करना जैसे कि एक अद्वितीय अर्थहीन है (एक रैखिक समय एल्गोरिथ्म भी O (n ^ 2), O (n ^ 3) आदि) है। यह कहना कि हमें दक्षता मापने में मदद करता है भ्रामक भी है। इसके अलावा, जटिलता वर्गों के लिंक के साथ क्या है? यदि आप सभी में रुचि रखते हैं, एल्गोरिदम के चल रहे समय की गणना करने की तकनीक है, तो यह कैसे प्रासंगिक है?

34
बिग-ओ दक्षता को मापता नहीं है; यह मापता है कि एक एल्गोरिथ्म आकार के साथ कितना अच्छा है (यह आकार की तुलना में अन्य चीजों पर भी लागू हो सकता है, लेकिन यह है कि हम यहां क्या रुचि रखते हैं) - और वह केवल स्पर्शोन्मुख है, इसलिए यदि आप भाग्य से बाहर हैं, तो "छोटे" बड़े के साथ एक एल्गोरिथ्म जब तक आप बहुत बड़ी संख्या में नहीं पहुंचते, तब तक ओ एक अलग हो सकता है (अगर बिग-ओ साइकिल पर लागू होता है)।
ILoveFortran

4
अपने बिग-ओ जटिलता के आधार पर एक एल्गोरिथ्म चुनना आमतौर पर कार्यक्रम डिजाइन का एक अनिवार्य हिस्सा है। यह निश्चित रूप से 'समय से पहले अनुकूलन' का मामला नहीं है, जो किसी भी मामले में एक बहुत ही दुरुपयोग वाली चयनात्मक उद्धरण है।
user207421

जवाबों:


1480

मैं इसे सरल शब्दों में यहाँ समझाने की पूरी कोशिश करूँगा, लेकिन चेतावनी दी जा सकती है कि यह विषय मेरे छात्रों को कुछ महीनों के लिए अंत में पकड़ लेता है। आप जावा बुक में डेटा संरचना और एल्गोरिथम के अध्याय 2 पर अधिक जानकारी प्राप्त कर सकते हैं ।


कोई भी यांत्रिक प्रक्रिया नहीं है जिसका उपयोग बिगओह को प्राप्त करने के लिए किया जा सकता है।

एक "रसोई की किताब" के रूप में, बिगओह को कोड के एक टुकड़े से प्राप्त करने के लिए आपको पहले यह महसूस करना होगा कि आप गणना करने के लिए एक गणित सूत्र बना रहे हैं कि गणना के कितने चरणों को किसी आकार का इनपुट दिया गया है।

उद्देश्य सरल है: कोड को निष्पादित करने की आवश्यकता के बिना, सैद्धांतिक दृष्टिकोण से एल्गोरिदम की तुलना करना। चरणों की संख्या जितनी कम होगी, उतनी ही तेजी से एल्गोरिथ्म।

उदाहरण के लिए, मान लें कि आपके पास यह कोड है:

int sum(int* data, int N) {
    int result = 0;               // 1

    for (int i = 0; i < N; i++) { // 2
        result += data[i];        // 3
    }

    return result;                // 4
}

यह फ़ंक्शन सरणी के सभी तत्वों का योग लौटाता है, और हम उस फ़ंक्शन की कम्प्यूटेशनल जटिलता को गिनने के लिए एक सूत्र बनाना चाहते हैं :

Number_Of_Steps = f(N)

इसलिए हमारे पास f(N)कम्प्यूटेशनल चरणों की संख्या को गिनने का एक कार्य है। फ़ंक्शन का इनपुट प्रक्रिया के लिए संरचना का आकार है। इसका मतलब है कि इस फ़ंक्शन को इस तरह कहा जाता है:

Number_Of_Steps = f(data.length)

पैरामीटर मान Nलेता है data.length। अब हमें फ़ंक्शन की वास्तविक परिभाषा की आवश्यकता है f()। यह स्रोत कोड से किया जाता है, जिसमें प्रत्येक दिलचस्प रेखा 1 से 4 तक गिने जाती है।

बिगओ की गणना करने के कई तरीके हैं। इस बिंदु से आगे हम यह मानने जा रहे हैं कि इनपुट डेटा के आकार पर निर्भर नहीं होने वाला प्रत्येक वाक्य एक निरंतर Cसंख्या कम्प्यूटेशनल कदम उठाता है।

हम फ़ंक्शन की अलग-अलग संख्याओं को जोड़ने जा रहे हैं, और न तो स्थानीय चर घोषणा और न ही रिटर्न स्टेटमेंट dataसरणी के आकार पर निर्भर करता है ।

इसका मतलब है कि लाइनों 1 और 4 में सी की मात्रा प्रत्येक चरण में है, और फ़ंक्शन कुछ इस तरह है:

f(N) = C + ??? + C

अगला भाग forकथन के मूल्य को परिभाषित करना है । याद रखें कि हम कम्प्यूटेशनल चरणों की संख्या की गिनती कर रहे हैं, जिसका अर्थ है कि forबयान का शरीर Nबार-बार निष्पादित होता है। जोड़ने Cके Nसमय के समान ही है :

f(N) = C + (C + C + ... + C) + C = C + N * C + C

यह गणना करने के लिए कोई यांत्रिक नियम नहीं है कि निकाय को कितनी बार forनिष्पादित किया जाता है, आपको यह देखने की आवश्यकता है कि कोड क्या करता है। गणना को सरल बनाने के लिए, हम परिवर्तनशील स्थिति, forकथन के विवरण और वृद्धि भागों की अनदेखी कर रहे हैं ।

वास्तविक बिगओ को प्राप्त करने के लिए हमें फ़ंक्शन के एसिम्प्टोटिक विश्लेषण की आवश्यकता होती है। यह लगभग इसी तरह किया जाता है:

  1. सभी स्थिरांक हटा दें C
  2. से f()प्राप्त polynomium अपने में standard form
  3. बहुपद की शर्तों को विभाजित करें और विकास की दर से उन्हें क्रमबद्ध करें।
  4. Nपास आने पर बड़ा होने वाला रखें infinity

हमारी f()दो शर्तें हैं:

f(N) = 2 * C * N ^ 0 + 1 * C * N ^ 1

सभी Cस्थिरांक और निरर्थक भागों को दूर करना:

f(N) = 1 + N ^ 1

चूंकि अंतिम शब्द वह है जो f()अनंत तक पहुंचता है, जब बड़ा होता है ( सीमा पर सोचें ) यह बिगओह तर्क है, और sum()फ़ंक्शन का बिगओह है:

O(N)

कुछ मुश्किलों को हल करने के लिए कुछ ट्रिक्स हैं: जब भी आप कर सकते हैं तो योगों का उपयोग करें ।

एक उदाहरण के रूप में, इस कोड को आसानी से योगों का उपयोग करके हल किया जा सकता है:

for (i = 0; i < 2*n; i += 2) {  // 1
    for (j=n; j > i; j--) {     // 2
        foo();                  // 3
    }
}

पहली चीज़ जो आपसे पूछनी चाहिए, वह है निष्पादन का आदेश foo()। जबकि सामान्य रूप से होना चाहिए O(1), आपको अपने प्रोफेसरों से इसके बारे में पूछना होगा। O(1)का अर्थ है (लगभग, अधिकतर) स्थिर C, आकार से स्वतंत्र N

forवाक्य नंबर एक पर बयान मुश्किल है। जबकि सूचकांक समाप्त होता है 2 * N, वेतन वृद्धि दो द्वारा की जाती है। इसका मतलब है कि पहले forकेवल Nचरणों को निष्पादित किया जाता है , और हमें गिनती को दो से विभाजित करने की आवश्यकता होती है।

f(N) = Summation(i from 1 to 2 * N / 2)( ... ) = 
     = Summation(i from 1 to N)( ... )

वाक्य संख्या दो और भी पेचीदा है क्योंकि यह मूल्य पर निर्भर करता है i। एक नज़र डालें: सूचकांक मैं मान लेता है: 0, 2, 4, 6, 8, ..., 2 * एन, और दूसरा forनिष्पादित हो जाता है: एन पहले बार, एन - 2 दूसरा, एन - 4 तीसरा ... एन / 2 चरण तक, जिस पर दूसरा forकभी निष्पादित नहीं होता है।

सूत्र पर, इसका अर्थ है:

f(N) = Summation(i from 1 to N)( Summation(j = ???)(  ) )

फिर, हम चरणों की संख्या की गिनती कर रहे हैं । और परिभाषा के अनुसार, प्रत्येक योग को हमेशा एक पर शुरू करना चाहिए, और एक से अधिक बड़े-या-बराबर पर समाप्त होना चाहिए।

f(N) = Summation(i from 1 to N)( Summation(j = 1 to (N - (i - 1) * 2)( C ) )

(हम मान रहे हैं कि कदम foo()है O(1)और Cकदम उठाता है।)

हमें यहाँ एक समस्या है: जब iमूल्य N / 2 + 1ऊपर की ओर ले जाता है, तो आंतरिक योग एक नकारात्मक संख्या पर समाप्त होता है! यह असंभव और गलत है। हमें दो बार समन को विभाजित करने की आवश्यकता है, जो कि महत्वपूर्ण बिंदु iहै N / 2 + 1

f(N) = Summation(i from 1 to N / 2)( Summation(j = 1 to (N - (i - 1) * 2)) * ( C ) ) + Summation(i from 1 to N / 2) * ( C )

महत्वपूर्ण क्षण के बाद से i > N / 2, आंतरिक forनिष्पादित नहीं होगा, और हम इसके शरीर पर एक निरंतर सी निष्पादन जटिलता मान रहे हैं।

अब कुछ पहचान नियमों का उपयोग करके योगों को सरल बनाया जा सकता है:

  1. योग (1 से एन) (सी) = एन * सी
  2. योग (1 से एन) (ए (+/-) बी) = योग (1 से एन तक) (ए) (+/-) योग (1 से एन तक) (बी)
  3. योग (1 से एन) (डब्ल्यू * सी) = सी * योग (1 से एन तक) (डब्ल्यू) (सी एक स्थिर, स्वतंत्र है w)
  4. योग (1 से एन) (डब्ल्यू) = (एन * (एन + 1)) / 2

कुछ बीजगणित लागू करना:

f(N) = Summation(i from 1 to N / 2)( (N - (i - 1) * 2) * ( C ) ) + (N / 2)( C )

f(N) = C * Summation(i from 1 to N / 2)( (N - (i - 1) * 2)) + (N / 2)( C )

f(N) = C * (Summation(i from 1 to N / 2)( N ) - Summation(i from 1 to N / 2)( (i - 1) * 2)) + (N / 2)( C )

f(N) = C * (( N ^ 2 / 2 ) - 2 * Summation(i from 1 to N / 2)( i - 1 )) + (N / 2)( C )

=> Summation(i from 1 to N / 2)( i - 1 ) = Summation(i from 1 to N / 2 - 1)( i )

f(N) = C * (( N ^ 2 / 2 ) - 2 * Summation(i from 1 to N / 2 - 1)( i )) + (N / 2)( C )

f(N) = C * (( N ^ 2 / 2 ) - 2 * ( (N / 2 - 1) * (N / 2 - 1 + 1) / 2) ) + (N / 2)( C )

=> (N / 2 - 1) * (N / 2 - 1 + 1) / 2 = 

   (N / 2 - 1) * (N / 2) / 2 = 

   ((N ^ 2 / 4) - (N / 2)) / 2 = 

   (N ^ 2 / 8) - (N / 4)

f(N) = C * (( N ^ 2 / 2 ) - 2 * ( (N ^ 2 / 8) - (N / 4) )) + (N / 2)( C )

f(N) = C * (( N ^ 2 / 2 ) - ( (N ^ 2 / 4) - (N / 2) )) + (N / 2)( C )

f(N) = C * (( N ^ 2 / 2 ) - (N ^ 2 / 4) + (N / 2)) + (N / 2)( C )

f(N) = C * ( N ^ 2 / 4 ) + C * (N / 2) + C * (N / 2)

f(N) = C * ( N ^ 2 / 4 ) + 2 * C * (N / 2)

f(N) = C * ( N ^ 2 / 4 ) + C * N

f(N) = C * 1/4 * N ^ 2 + C * N

और BigOh है:

O(N²)

6
@arthur वह O (N ^ 2) होगा क्योंकि आपको सभी कॉलमों के माध्यम से पढ़ने के लिए एक लूप की आवश्यकता होगी और किसी विशेष कॉलम की सभी पंक्तियों को पढ़ने के लिए।
अभिषेक डे दास

@ आर्थर: यह निर्भर करता है। यह वह O(n)जगह nहै जहां तत्वों की संख्या है, या O(x*y)जहां xऔर yसरणी के आयाम हैं। बिग-ओह "इनपुट के सापेक्ष" है, इसलिए यह इस बात पर निर्भर करता है कि आपका इनपुट क्या है।
मूविंग डक

1
महान जवाब, लेकिन मैं वास्तव में फंस गया हूं। सारांश (i से 1 से एन / 2) (एन) में कैसे बदल जाता है (एन ^ 2/2)?
परसा

2
@ParsaAkbari एक सामान्य नियम के रूप में, (i से 1 से a) (b) एक * b है। यह कहने का एक और तरीका है b + b + ... (एक समय) + b = a * b (पूर्णांक गुणन की कुछ परिभाषाओं के लिए परिभाषा द्वारा)।
मारियो कार्नेइरो

इतना प्रासंगिक नहीं है, लेकिन सिर्फ भ्रम से बचने के लिए, इस वाक्य में एक छोटी सी गलती है: "सूचकांक I मान लेता है: 0, 2, 4, 6, 8, ..., 2 * एन"। इंडेक्स I वास्तव में 2 * N - 2 तक जाता है, लूप तब बंद हो जाएगा।
अल्बर्ट

201

बिग ओ एक एल्गोरिथ्म की समय जटिलता के लिए ऊपरी बाध्य देता है। यह आमतौर पर प्रसंस्करण डेटा सेट (सूचियों) के साथ संयोजन में उपयोग किया जाता है, लेकिन कहीं और इस्तेमाल किया जा सकता है।

C कोड में इसका उपयोग कैसे किया जाता है, इसके कुछ उदाहरण।

कहें कि हमारे पास n तत्वों की एक सरणी है

int array[n];

अगर हम सरणी के पहले तत्व को एक्सेस करना चाहते हैं तो यह O (1) होगा क्योंकि इससे कोई फर्क नहीं पड़ता कि सरणी कितनी बड़ी है, यह हमेशा पहला आइटम प्राप्त करने के लिए एक ही निरंतर समय लेता है।

x = array[0];

अगर हम सूची में एक नंबर खोजना चाहते हैं:

for(int i = 0; i < n; i++){
    if(array[i] == numToFind){ return i; }
}

यह O (n) होगा क्योंकि अधिक से अधिक हमें अपना नंबर खोजने के लिए पूरी सूची देखनी होगी। बिग-ओ अभी भी ओ (एन) है, भले ही हम अपनी संख्या को पहली कोशिश में पा सकते हैं और एक बार लूप के माध्यम से चला सकते हैं क्योंकि बिग-ओ एक एल्गोरिथ्म के लिए ऊपरी सीमा का वर्णन करता है (ओमेगा कम बाध्य के लिए है और थीटा तंग बाध्य के लिए है) ।

जब हम नेस्टेड छोरों के लिए मिलता है:

for(int i = 0; i < n; i++){
    for(int j = i; j < n; j++){
        array[j] += 2;
    }
}

यह बाहरी लूप (O (n)) के प्रत्येक पास के लिए O (n ^ 2) है क्योंकि हमें पूरी सूची से फिर से गुजरना है इसलिए n का गुणा हमें n वर्ग से छोड़ रहा है।

यह मुश्किल से सतह को खरोंच रहा है, लेकिन जब आप अधिक जटिल एल्गोरिदम का विश्लेषण करने के लिए मिलते हैं तो जटिल गणित शामिल होता है, जो सबूतों के साथ आता है। आशा है कि यह आपको मूल बातों से परिचित कराएगा हालांकि कम से कम।


महान व्याख्या! इसलिए अगर कोई कहता है कि उसके एल्गोरिथ्म में O (n ^ 2) जटिलता है, तो क्या इसका मतलब है कि वह नेस्टेड लूप का उपयोग कर रहा है?
नवनीत केएन

2
वास्तव में, कोई भी पहलू जो n स्क्वेयर्ड समय की ओर ले जाता है उसे n ^ 2 माना जाएगा
asyncwait

@NavaneethKN: आप हमेशा नेस्टेड लूप नहीं देखेंगे , क्योंकि फ़ंक्शन कॉल O(1)स्वयं काम कर सकते हैं। उदाहरण के लिए C मानक API में, bsearchस्वाभाविक रूप O(log n)से strlenहै O(n), और qsortहै O(n log n)(तकनीकी रूप से इसकी कोई गारंटी नहीं है, और quicksort में ही सबसे खराब स्थिति जटिलता है O(n²), लेकिन यह मानते हुए कि आपका libcलेखक एक मूर्ख नहीं है, इसका औसत मामला जटिलता है O(n log n)और इसका उपयोग करता है एक धुरी चयन रणनीति जो O(n²)मामले को मारने की बाधाओं को कम करती है)। और दोनों bsearchऔर qsortभी बदतर हो सकता है अगर तुलनित्र समारोह रोग है।
शैडो रेंजर

95

अपनी विशेष समस्या के लिए बिग ओ समय निकालने का तरीका जानने के लिए उपयोगी होने के साथ-साथ कुछ सामान्य मामलों को जानना आपके एल्गोरिथ्म में निर्णय लेने में मदद करने में एक लंबा रास्ता तय कर सकता है।

यहाँ http://en.wikipedia.org/wiki/Big_O_notation#Orders_of_common_functions से उठाए गए कुछ सबसे सामान्य मामले हैं :

O (1) - यदि संख्या समान या विषम है, तो यह निर्धारित करना; निरंतर-आकार लुकअप तालिका या हैश तालिका का उपयोग करना

O (logn) - बाइनरी खोज के साथ सॉर्ट की गई सरणी में एक आइटम ढूँढना

ओ (एन) - एक अनसुलझी सूची में एक आइटम ढूँढना; दो n अंकों की संख्याओं को जोड़ना

ओ (एन 2 ) - एक साधारण एल्गोरिथ्म द्वारा दो एन-अंकों की संख्या को गुणा करना; दो n × n matrices जोड़ना; बुलबुला सॉर्ट या सम्मिलन सॉर्ट

O (n 3 ) - सरल एल्गोरिथ्म द्वारा दो n × n मैट्रिसेस गुणा करना

O (c n ) - डायनामिक प्रोग्रामिंग का उपयोग करके यात्रा विक्रेता समस्या का (सटीक) समाधान खोजना; यह निर्धारित करना कि दो तार्किक कथन पाशविक बल के समतुल्य हैं

O (n!) - ब्रूट-फोर्स सर्च के माध्यम से ट्रैवलिंग सेल्समैन समस्या का समाधान

O (n n ) - प्रायः O की जगह उपयोग किया जाता है (n!) Asymptotic जटिलता के लिए सरल सूत्रों को प्राप्त करने के लिए


x&1==1विषमता की जांच करने के लिए उपयोग क्यों नहीं किया जाता है?
सामी बेनचेरी

2
@SamyBencherif: यह जांचने का एक विशिष्ट तरीका होगा (वास्तव में, बस परीक्षण x & 1पर्याप्त होगा, जांचने की कोई आवश्यकता नहीं है == 1; सी में, ऑपरेटर पूर्वता के लिए धन्यवाद केx&1==1 रूप में मूल्यांकन किया जाता x&(1==1) है , इसलिए यह वास्तव में परीक्षण के समान है x&1)। मुझे लगता है कि आप उत्तर को गलत बता रहे हैं; वहाँ एक अर्ध-उपनिवेश है, अल्पविराम नहीं। यह नहीं कह रहा है कि आपको / विषम परीक्षण के लिए एक लुकअप तालिका की आवश्यकता होगी, यह दोनों भी कह रही है / विषम परीक्षण और लुकअप तालिका की जाँच कर रहे हैं O(1)
शैडो रेंजर

मैं अंतिम वाक्य में उपयोग पर दावे के बारे में नहीं जानता, लेकिन जो कोई भी ऐसा करता है जो एक वर्ग को दूसरे द्वारा प्रतिस्थापित कर रहा है जो समकक्ष नहीं है। वर्ग O (n) में समाहित है, लेकिन O (n ^ n) से कड़ाई से बड़ा है। वास्तविक तुल्यता O (n!) = O (n ^ ne ^ {- n} sqrt (n)) होगी।
conditionalMethod

43

छोटे अनुस्मारक: big Oसंकेतन का उपयोग स्पर्शोन्मुख जटिलता (यानी, जब समस्या का आकार अनंत तक बढ़ता है) को निरूपित करने के लिए उपयोग किया जाता है , और यह एक निरंतर छुपाता है।

इसका अर्थ यह है कि O (n) और एक O (n 2 ) में एक एल्गोरिथ्म के बीच , सबसे तेज़ हमेशा पहला नहीं होता है (हालाँकि इसमें हमेशा n का मान मौजूद होता है कि आकार की समस्याओं के लिए> n, पहला एल्गोरिथम है सबसे तेज़)।

ध्यान दें कि छिपा हुआ स्थिरांक बहुत कुछ कार्यान्वयन पर निर्भर करता है!

इसके अलावा, कुछ मामलों में, रनटाइम इनपुट के आकार n का एक नियतात्मक कार्य नहीं है । उदाहरण के लिए त्वरित प्रकार का उपयोग करके छंटाई करें: एन तत्वों की एक सरणी को सॉर्ट करने के लिए आवश्यक समय एक स्थिर नहीं है, लेकिन सरणी के शुरुआती कॉन्फ़िगरेशन पर निर्भर करता है।

अलग-अलग समय जटिलताएं हैं:

  • सबसे खराब स्थिति (आमतौर पर यह पता लगाने के लिए सबसे सरल, हालांकि हमेशा बहुत सार्थक नहीं)
  • औसत मामला (आमतौर पर यह पता लगाने के लिए बहुत कठिन ...)

  • ...

एक अच्छा परिचय आर। सेडग्विक और पी। फ्लाजोलेट द्वारा एल्गोरिदम के विश्लेषण का एक परिचय है

जैसा कि आप कहते हैं, premature optimisation is the root of all evilऔर (यदि संभव हो तो) प्रोफाइल का उपयोग हमेशा कोड का अनुकूलन करते समय किया जाना चाहिए। यह आपके एल्गोरिदम की जटिलता को निर्धारित करने में आपकी मदद कर सकता है।


3
गणित में, O (।) का अर्थ है एक ऊपरी बाउंड, और थीटा (।) का मतलब है कि आपके ऊपर और नीचे एक बाउंड है। क्या सीएस में परिभाषा वास्तव में अलग है, या यह सिर्फ नोटेशन का एक सामान्य दुरुपयोग है? गणितीय परिभाषा के अनुसार, sqrt (n) O (n) और O (n ^ 2) दोनों है, इसलिए यह हमेशा ऐसा नहीं होता है कि कुछ n है जिसके बाद O (n) फ़ंक्शन छोटा होता है।
डगलस ज़ारे

28

यहाँ जवाब देखकर मुझे लगता है कि हम निष्कर्ष निकाल सकते है कि हम में से ज्यादातर वास्तव में से एल्गोरिथ्म के आदेश का अनुमान है की तलाश में उस पर और बदले के साथ की गणना के सामान्य ज्ञान का उपयोग करें, उदाहरण के लिए, मास्टर विधि के रूप में हम विश्वविद्यालय में माना जाता था। इसके साथ ही मुझे यह भी कहना चाहिए कि प्रोफेसर ने हमें (बाद में) प्रोत्साहित किया कि वास्तव में केवल गणना करने के बजाय इसके बारे में सोचें

इसके अलावा मैं यह भी जोड़ना चाहूंगा कि यह पुनरावर्ती कार्यों के लिए कैसे किया जाता है :

मान लीजिए हमारे पास एक फ़ंक्शन है ( योजना कोड) ):

(define (fac n)
    (if (= n 0)
        1
            (* n (fac (- n 1)))))

जो दिए गए संख्या के भाज्य की गणना करता है।

पहला कदम प्रदर्शन की विशेषता के लिए प्रयास करना और निर्धारित करना है केवल इस मामले में फ़ंक्शन के शरीर के , शरीर में कुछ भी विशेष नहीं किया जाता है, बस गुणा (या मूल्य 1 की वापसी)।

तो शरीर के लिए प्रदर्शन है: O (1) (स्थिर)।

अगली कोशिश करें और पुनरावर्ती कॉल की संख्या के लिए इसे निर्धारित करें । इस मामले में हमारे पास n-1 पुनरावर्ती कॉल हैं।

तो पुनरावर्ती कॉल के लिए प्रदर्शन है: O (n-1) (आदेश n है, क्योंकि हम तुच्छ भागों को फेंक देते हैं)।

फिर उन दोनों को एक साथ रखें और आपके पास पूरे पुनरावर्ती कार्य के लिए प्रदर्शन है:

1 * (n-1) = O (n)


पीटर , आपके उठाए गए मुद्दों का जवाब देने के लिए ; जिस विधि का मैं यहां वर्णन करता हूं, वह वास्तव में इसे काफी अच्छी तरह से संभालती है। लेकिन ध्यान रखें कि यह अभी भी एक सन्निकटन है और पूर्ण गणितीय रूप से सही उत्तर नहीं है। यहां वर्णित विधि भी उन तरीकों में से एक है जो हमें विश्वविद्यालय में पढ़ाए गए थे, और अगर मुझे याद है कि इस उदाहरण में उपयोग किए गए तथ्य की तुलना में कहीं अधिक उन्नत एल्गोरिदम के लिए सही ढंग से उपयोग किया गया था।
बेशक यह सब इस बात पर निर्भर करता है कि आप फ़ंक्शन के शरीर के चलने के समय और पुनरावर्ती कॉल की संख्या का अनुमान लगा सकते हैं, लेकिन यह अन्य तरीकों के लिए उतना ही सही है।


स्वेन, मुझे यकीन नहीं है कि एक पुनरावर्ती फ़ंक्शन की जटिलता को पहचानने का आपका तरीका अधिक जटिल लोगों के लिए काम करने वाला है, जैसे कि एक शीर्ष से नीचे की खोज / सारांश / एक बाइनरी ट्री में कुछ करना। निश्चित रूप से, आप एक साधारण उदाहरण के बारे में तर्क कर सकते हैं और उत्तर के साथ आ सकते हैं। लेकिन मुझे लगता है कि आप वास्तव में पुनरावर्ती लोगों के लिए कुछ गणित करना होगा?
पेटीटर

3
पुनरावृत्ति के लिए +1 ... यह भी एक सुंदर है: "... यहां तक ​​कि प्रोफेसर ने हमें सोचने के लिए प्रोत्साहित किया ..." :)
TT_

हाँ यह बहुत अच्छा है। मैं इसे इस तरह से सोचता हूं, O (..) के अंदर शब्द अधिक है, जितना अधिक काम आप कर रहे हैं / मशीन कर रही है। किसी चीज से संबंधित होने के दौरान यह सोचना एक अनुमान हो सकता है, लेकिन ये सीमाएं हैं। वे सिर्फ आपको बताते हैं कि कैसे काम किया जाता है जब इनपुट की संख्या बढ़ जाती है।
अभिनव गौनियाल

26

यदि आपकी लागत एक बहुपद है, तो उसके गुणक के बिना, उच्चतम-क्रम शब्द रखें। उदाहरण के लिए:

ओ ((एन / 2 + 1) * (एन / 2)) = O (n 2 /4 + n / 2) = O (n 2 /4) = O (n 2 )

यह अनंत श्रृंखला के लिए काम नहीं करता है, तुम मन हो। सामान्य मामले के लिए कोई एकल नुस्खा नहीं है, हालांकि कुछ सामान्य मामलों के लिए, निम्नलिखित असमानताएं लागू होती हैं:

O (log N ) <O ( N ) <O ( N log N ) <O ( N 2 ) <O ( N k ) <O (e n ) <O ( n !)


8
निश्चित रूप से O (N) <O (NlogN)
jk।

22

मैं इसके बारे में जानकारी के बारे में सोचता हूं। किसी भी समस्या में कुछ निश्चित बिट्स सीखने की होती है।

आपका मूल उपकरण निर्णय बिंदुओं की अवधारणा और उनका प्रवेश है। एक निर्णय बिंदु की एन्ट्रोपी औसत जानकारी है जो यह आपको देगी। उदाहरण के लिए, यदि किसी प्रोग्राम में दो शाखाओं के साथ एक निर्णय बिंदु होता है, तो यह एन्ट्रॉपी प्रत्येक शाखा के उस शाखा के व्युत्क्रम प्रायिकता के लॉग 2 की संभावना का योग है । उस निर्णय को निष्पादित करके आप कितना सीखते हैं।

उदाहरण के लिए, एक ifकथन जिसमें दो शाखाएं हैं, दोनों समान रूप से होने की संभावना है, जिसमें 1/2 * लॉग (2/1) + 1/2 * लॉग (2/1) = 1/2 * 1 + 1/2 * 1 है। = 1. तो इसकी एंट्रोपी 1 बिट है।

मान लीजिए कि आप N आइटम की एक तालिका खोज रहे हैं, जैसे N = 1024। यह एक 10-बिट समस्या है क्योंकि लॉग (1024) = 10 बिट्स। इसलिए यदि आप इसे IF स्टेटमेंट के साथ खोज सकते हैं, जिसके परिणाम समान रूप से होने की संभावना है, तो इसे 10 निर्णय लेने चाहिए।

यही आपको बाइनरी खोज के साथ मिलता है।

मान लीजिए आप रैखिक खोज कर रहे हैं। आप पहले तत्व को देखते हैं और पूछते हैं कि क्या यह वही है जो आप चाहते हैं। संभावनाएं 1/1024 हैं जो यह है, और 1023/1024 यह नहीं है। उस निर्णय का एन्ट्रापी 1/1024 * लॉग (1024/1) + 1023/1024 * लॉग (1024/1023) = 1/1024 * 10 + 1023/1024 * लगभग 0 = लगभग .01 बिट है। आपने बहुत कम सीखा है! दूसरा निर्णय ज्यादा बेहतर नहीं है। यही कारण है कि रैखिक खोज इतनी धीमी है। वास्तव में यह आपके सीखने के लिए आवश्यक बिट्स की संख्या में घातांक है।

मान लीजिए आप इंडेक्सिंग कर रहे हैं। मान लीजिए कि तालिका बहुत सारे डिब्बे में पूर्व-क्रमबद्ध है, और आप तालिका प्रविष्टि में सीधे अनुक्रमणिका की कुंजी में सभी बिट्स का उपयोग करते हैं। यदि 1024 डिब्बे हैं, तो प्रवेशिका सभी 1024 संभावित परिणामों के लिए 1/1024 * लॉग (1024) + 1/1024 * लॉग (1024) + ... है। यह 1/1024 * 10 गुना 1024 परिणाम है, या उस एक अनुक्रमण ऑपरेशन के लिए एन्ट्रापी के 10 बिट्स हैं। इसीलिए इंडेक्सिंग सर्च तेज है।

अब छँटाई के बारे में सोचें। आपके पास एन आइटम हैं, और आपके पास एक सूची है। प्रत्येक आइटम के लिए, आपको यह खोजना होगा कि आइटम सूची में कहां जाता है, और फिर उसे सूची में जोड़ें। इसलिए छंटनी अंतर्निहित खोज के चरणों की संख्या से लगभग N गुना अधिक होती है।

तो बाइनरी निर्णयों के आधार पर सॉर्ट किए जाने के कारण लगभग सभी ओ (एन लॉग एन) चरणों के परिणाम की संभावना होती है। यदि यह अनुक्रमण खोज पर आधारित है तो O (N) सॉर्ट एल्गोरिथ्म संभव है।

मैंने पाया है कि लगभग सभी एल्गोरिदम प्रदर्शन मुद्दों को इस तरह से देखा जा सकता है।


वाह। क्या आपके पास इस पर कोई उपयोगी संदर्भ है? मुझे लगता है कि यह सामान मेरे लिए डिजाइन / रिफ्लेक्टर / डिबग प्रोग्राम के लिए सहायक है।
जेस्विन जोस

3
@aitchnyu: इसकी कीमत क्या है, इसके लिए मैंने एक पुस्तक लिखी है जिसमें अन्य विषय शामिल हैं। यह लंबे समय से प्रिंट से बाहर है, लेकिन प्रतियां एक उचित मूल्य के लिए जा रही हैं। मैंने इसे हथियाने के लिए GoogleBooks प्राप्त करने की कोशिश की है, लेकिन फिलहाल यह पता लगाना थोड़ा कठिन है कि कॉपीराइट किसे मिला है।
माइक डनलैवी

21

हमें शुरू से करना चाहिए।

सबसे पहले, इस सिद्धांत को स्वीकार करें कि डेटा पर कुछ सरल ऑपरेशन O(1)समय में किए जा सकते हैं , अर्थात् समय में जो इनपुट के आकार से स्वतंत्र है। C में ये आदिम संचालन शामिल हैं

  1. अंकगणितीय संचालन (जैसे + या%)।
  2. तार्किक संचालन (जैसे, और&)।
  3. तुलना संचालन (जैसे, <=)।
  4. संरचना तक पहुंच संचालन (जैसे ए-आई] की तरह सरणी-अनुक्रमण, या -> ऑपरेटर के साथ पॉइंटर-लोइंग।
  5. एक चर में एक मूल्य की नकल करने के रूप में सरल काम।
  6. लाइब्रेरी फ़ंक्शंस (उदाहरण के लिए, स्कैनफ़, प्रिंटफ़) को कॉल करता है।

इस सिद्धांत के औचित्य के लिए एक विशिष्ट कंप्यूटर के मशीन निर्देशों (आदिम चरणों) का विस्तृत अध्ययन आवश्यक है। वर्णित कार्यों में से प्रत्येक को कुछ छोटी संख्या में मशीन निर्देशों के साथ किया जा सकता है; अक्सर केवल एक या दो निर्देशों की जरूरत होती है। परिणामस्वरूप, C में कई प्रकार के कथनों को समय के साथ निष्पादित किया जा सकता O(1)है, अर्थात्, इनपुट के स्वतंत्र कुछ स्थिर मात्रा में। इन सरल शामिल हैं

  1. असाइनमेंट स्टेटमेंट जिसमें उनके एक्सप्रेशन में फ़ंक्शन कॉल शामिल नहीं है।
  2. बयान पढ़े।
  3. ऐसे कथन लिखें जिन्हें तर्कों का मूल्यांकन करने के लिए फ़ंक्शन कॉल की आवश्यकता नहीं है।
  4. जम्प स्टेटमेंट्स, विमोचन, गोटो और रिटर्न एक्सप्रेशन को तोड़ते हैं, जहाँ एक्सप्रेशन में फंक्शन कॉल नहीं होती है।

C में, कई फॉर-लूप्स इंडेक्स वेरिएबल को कुछ वैल्यू में इनिशियलाइज़ करके और उस वेरिएबल को लूप के चारों ओर 1 बार बढ़ाकर बनाया जाता है। इंडेक्स कुछ सीमा तक पहुंचने पर लूप के लिए समाप्त हो जाता है। उदाहरण के लिए, लूप के लिए

for (i = 0; i < n-1; i++) 
{
    small = i;
    for (j = i+1; j < n; j++)
        if (A[j] < A[small])
            small = j;
    temp = A[small];
    A[small] = A[i];
    A[i] = temp;
}

इंडेक्स वेरिएबल i का उपयोग करता है। यह लूप के आसपास हर बार 1 से बढ़ाता है, और n - 1 तक पहुंचने पर पुनरावृत्तियाँ रुक जाती हैं।

हालांकि, फिलहाल, लूप के सरल रूप पर ध्यान केंद्रित करें, जहां अंतिम और प्रारंभिक मानों के बीच का अंतर, उस राशि से विभाजित किया जाता है जिसके द्वारा सूचकांक चर को बढ़ाया जाता है, यह बताता है कि हम लूप के आसपास कितनी बार जाते हैं । यह गणना सटीक है, जब तक कि जम्प स्टेटमेंट के माध्यम से लूप से बाहर निकलने के तरीके नहीं हैं; यह किसी भी मामले में पुनरावृत्तियों की संख्या पर एक ऊपरी बाध्य है।

उदाहरण के लिए, फॉर-लूप पुनरावृत्त करता है ((n − 1) − 0)/1 = n − 1 times, क्योंकि 0, i का प्रारंभिक मान है, n - 1, उच्चतम मूल्य है जो i तक पहुंचा है (यानी, जब मैं n, 1 तक पहुंचता हूं, तो लूप बंद हो जाता है और i = n i के साथ कोई पुनरावृत्ति नहीं होती है 1), और 1 को लूप के प्रत्येक पुनरावृत्ति पर i में जोड़ा जाता है।

सबसे सरल मामले में, जहां लूप बॉडी में बिताया गया समय प्रत्येक पुनरावृत्ति के लिए समान होता है, हम लूप के चारों ओर की संख्या से बॉडी के लिए बड़े-ओह ऊपरी को गुणा कर सकते हैं । कड़ाई से बोलते हुए, हमें लूप इंडेक्स की शुरुआत के लिए O (1) समय जोड़ना होगा और सीमा के साथ लूप इंडेक्स की पहली तुलना के लिए O (1) समय , क्योंकि हम लूप के चारों ओर जाने से एक बार और परीक्षण करते हैं। हालांकि, जब तक कि लूप शून्य बार निष्पादित करना संभव नहीं है, लूप को इनिशियलाइज़ करने और एक बार सीमा का परीक्षण करने का समय एक कम-ऑर्डर अवधि है जिसे समन नियम द्वारा गिराया जा सकता है।


अब इस उदाहरण पर विचार करें:

(1) for (j = 0; j < n; j++)
(2)   A[i][j] = 0;

हम जानते हैं कि लाइन (1) में O(1)समय लगता है। स्पष्ट रूप से, हम लूप n बार घूमते हैं, जैसा कि हम लाइन (1) पर पाई गई ऊपरी सीमा से निचली सीमा घटाकर निर्धारित कर सकते हैं और फिर जोड़ सकते हैं। 1. चूंकि शरीर, रेखा (2), O (1) समय लेता है, हम j को बढ़ाने के लिए समय की उपेक्षा कर सकते हैं और j से n की तुलना करने का समय, दोनों ही O (1) भी हैं। इस प्रकार, लाइनों का चलने का समय (1) और (2) n और O (1) का गुणन है, जो है O(n)

इसी तरह, हम बाहरी लूप के चलने के समय को रेखाओं (2) से (4) से जोड़ सकते हैं, जो है

(2) for (i = 0; i < n; i++)
(3)     for (j = 0; j < n; j++)
(4)         A[i][j] = 0;

हम पहले ही स्थापित कर चुके हैं कि लाइनों का लूप (3) और (4) O (n) समय लेता है। इस प्रकार, हम O (1) समय को बढ़ाने के लिए उपेक्षा कर सकते हैं i और परीक्षण करने के लिए कि क्या मैं प्रत्येक चलना में यह निष्कर्ष निकालता हूं कि बाहरी लूप का प्रत्येक पुनरावृत्ति O (n) समय लेता है।

बाहरी लूप का आरंभीकरण i = 0 और स्थिति का n (1 + 1) सेंट परीक्षण i <n इसी तरह ओ (1) समय लें और उपेक्षित किया जा सकता है। अंत में, हम देखते हैं कि हम बाहरी पाश के चारों ओर चलते हैं, कुल O(n^2)चलने का समय देते हुए, प्रत्येक पुनरावृत्ति के लिए O (n) समय लेते हैं ।


एक अधिक व्यावहारिक उदाहरण।

यहां छवि विवरण दर्ज करें


क्या होगा अगर किसी गोटो स्टेटमेंट में फ़ंक्शन कॉल होता है? कुछ स्टेप 3 की तरह: if (M.step == 3) {M = step3 (किया, M); } step4: if (M.step == 4) {M = step4 (M); } अगर (M.step == 5) {M = step5 (M); गोटो स्टेप 3; } अगर (M.step == 6) {M = step6 (M); गोटो चरण 4; } रिटर्न कट_मेट्रिक्स (ए, एम); तब जटिलता की गणना कैसे की जाएगी? क्या यह जोड़ या गुणा होगा? चरण 4 पर विचार करना n ^ 3 है और चरण 5 n ^ 2 है।
ताहा तारिक

14

यदि आप कोड के विश्लेषण के बजाय अपने कोड के क्रम का अनुमान लगाना चाहते हैं, तो आप अपने कोड के n और समय के बढ़ते मूल्यों की श्रृंखला में चिपक सकते हैं। एक लॉग स्केल पर अपनी टाइमिंग प्लॉट करें। यदि कोड O (x ^ n) है, तो मान ढलान n की रेखा पर गिरना चाहिए।

कोड का अध्ययन करने पर इसके कई फायदे हैं। एक बात के लिए, आप देख सकते हैं कि क्या आप उस सीमा में हैं जहाँ रन टाइम अपने अस्वाभाविक क्रम से संपर्क करता है। इसके अलावा, आप पा सकते हैं कि कुछ कोड जो आपने सोचा था कि ऑर्डर ओ (x) वास्तव में ऑर्डर O (x ^ 2) है, उदाहरण के लिए, लाइब्रेरी कॉल में समय बिताने के कारण।


बस इस उत्तर को अपडेट करने के लिए: en.wikipedia.org/wiki/Analysis_of_algorithms , इस लिंक में वह सूत्र है जिसकी आपको आवश्यकता है। कई एल्गोरिदम एक पावर नियम का पालन करते हैं, अगर आपका, मशीन पर 2 टाइमपॉइंट और 2 रनटाइम के साथ, हम लॉग-लॉग प्लॉट पर ढलान की गणना कर सकते हैं। जो a = log (t2 / t1) / log (n2 / n1) है, इसने मुझे एल्गोरिथ्म में O, (N ^ a) के लिए प्रतिपादक दिया। यह कोड का उपयोग करके मैन्युअल गणना के मुकाबले तुलना की जा सकती है।
क्रिस्टोफर जॉन

1
नमस्ते, अच्छा जवाब। मैं सोच रहा था कि क्या आप इस अनुभवजन्य विधि को सामान्य बनाने के लिए किसी भी पुस्तकालय या कार्यप्रणाली (उदाहरण के लिए python / R के साथ काम करते हैं) का अर्थ है, जो आकार के डेटासेट को बढ़ाने के लिए विभिन्न जटिलता कार्यों को फिट करने की तरह है, और यह पता करें कि कौन सा प्रासंगिक है। धन्यवाद
agenis

10

मूल रूप से बात यह है कि समय का 90% तक फसलें सिर्फ लूप का विश्लेषण कर रही हैं। क्या आपके पास सिंगल, डबल, ट्रिपल नेस्टेड लूप्स हैं? आपके पास O (n), O (n ^ 2), O (n ^ 3) रनिंग टाइम है।

बहुत कम ही (जब तक कि आप एक व्यापक बेस लाइब्रेरी (उदाहरण के लिए, .NET BCL, या C ++ के STL) के साथ एक प्लेटफ़ॉर्म नहीं लिख रहे हैं, तो आप कुछ भी सामना करेंगे जो आपके लूप (बयानों के लिए, जबकि, गोटो,) की तुलना में अधिक कठिन है आदि...)


1
छोरों पर निर्भर करता है।
केलालाका

8

बिग ओ नोटेशन उपयोगी है क्योंकि इसके साथ काम करना आसान है और अनावश्यक जटिलताओं और विवरणों को छिपाता है (अनावश्यक की कुछ परिभाषा के लिए)। फूट डालो और जीतो एल्गोरिदम की जटिलता के बाहर काम करने का एक अच्छा तरीका पेड़ विधि है। मान लें कि आपके पास माध्यिका प्रक्रिया के साथ क्विकॉर्ट का एक संस्करण है, इसलिए आप सरणी को हर बार पूरी तरह से संतुलित उपशीर्षकों में विभाजित करते हैं।

अब आप के साथ काम करने वाले सभी सरणियों के अनुरूप एक पेड़ बनाएं। मूल सरणी में मूल में दो बच्चे हैं, जो सबरेज़ हैं। इसे तब तक दोहराएं जब तक कि आपके तल में एकल तत्व सरणियाँ न हों।

चूँकि हम माध्य को O (n) समय में पा सकते हैं और सरणी को O (n) समय में दो भागों में विभाजित कर सकते हैं, प्रत्येक नोड पर किया गया कार्य O (k) है जहाँ k सरणी का आकार है। पेड़ के प्रत्येक स्तर में (अधिक से अधिक) संपूर्ण सरणी होती है, इसलिए प्रति स्तर काम हे (n) है (उप-आकारों का आकार n तक जुड़ता है, और चूंकि हमारे पास O (k) है प्रति स्तर हम इसे जोड़ सकते हैं) । पेड़ में केवल लॉग (एन) स्तर होते हैं क्योंकि हर बार हम इनपुट को आधा कर देते हैं।

इसलिए हम O (n * log (n)) द्वारा कार्य की मात्रा को बढ़ा सकते हैं।

हालाँकि, बिग ओ कुछ विवरण छिपाते हैं जिन्हें हम कभी-कभी अनदेखा नहीं कर सकते हैं। के साथ फाइबोनैचि अनुक्रम की गणना करने पर विचार करें

a=0;
b=1;
for (i = 0; i <n; i++) {
    tmp = b;
    b = a + b;
    a = tmp;
}

और मान लेते हैं कि a और b जावा में BigIntegers हैं या ऐसा कुछ है जो मनमाने ढंग से बड़ी संख्या को संभाल सकता है। ज्यादातर लोग कहेंगे कि यह एक ओ (एन) एल्गोरिथ्म है जिसमें बिना पलके झपकाए जा सकते हैं। तर्क यह है कि आपके पास लूप के लिए एन पुनरावृत्तियां हैं और लूप के पक्ष में ओ (1) काम करते हैं।

लेकिन फाइबोनैचि संख्या बड़ी होती है, n-th फाइबोनैचि संख्या n में घातांक होती है इसलिए केवल इसे संग्रहीत करने से यह n बाइट्स के क्रम में लगेगा। बड़े पूर्णांकों के साथ कार्य करने पर O (n) राशि का कार्य होगा। तो इस प्रक्रिया में किए गए काम की कुल राशि है

1 + 2 + 3 + ... + n = n (n-1) / 2 = O (n ^ 2)

तो यह एल्गोरिथ्म द्विघात समय में चलता है!


1
आपको इसकी परवाह नहीं करनी चाहिए कि नंबर कैसे संग्रहीत किए जाते हैं, यह नहीं बदलता है कि एल्गोरिथ्म O (n) के ऊपरी हिस्से में बढ़ता है।
mikek3332002

8

आम तौर पर कम उपयोगी, मुझे लगता है, लेकिन पूर्णता के लिए एक बिग ओमेगा think भी है , जो एक एल्गोरिथ्म की जटिलता पर एक निचली-सीमा को परिभाषित करता है, और एक बड़ी थीटा Θ , जो ऊपरी और निचले दोनों को परिभाषित करता है।


7

एल्गोरिथ्म को उन टुकड़ों में तोड़ें जिनके बारे में आप जानते हैं कि बड़े O संकेतन, और बड़े O ऑपरेटरों के माध्यम से संयोजित होते हैं। केवल यही एक तरीका है जिसके बारे में मुझे पता है।

अधिक जानकारी के लिए, विषय पर विकिपीडिया पृष्ठ देखें


7

एल्गोरिदम / डेटा संरचनाओं के साथ परिचित मैं उपयोग और / या त्वरित नज़र विश्लेषण के घोंसले के शिकार। कठिनाई तब होती है जब आप किसी लाइब्रेरी फ़ंक्शन को कॉल करते हैं, संभवतः कई बार - आप अक्सर इस बात के बारे में अनिश्चित हो सकते हैं कि आप फ़ंक्शन को अनावश्यक रूप से कॉल कर रहे हैं या वे किस कार्यान्वयन का उपयोग कर रहे हैं। हो सकता है कि लाइब्रेरी फ़ंक्शंस में एक जटिलता / दक्षता माप होनी चाहिए, चाहे वह बिग ओ या कुछ अन्य मीट्रिक हो, जो प्रलेखन या यहां तक ​​कि टेलिविज़न के लिए उपलब्ध हो


6

बिग ओ की गणना "आप कैसे करते हैं" के रूप में, यह कम्प्यूटेशनल जटिलता सिद्धांत का हिस्सा है । कुछ (कई) विशेष मामलों के लिए आप कुछ सरल उत्तराधिकार के साथ आने में सक्षम हो सकते हैं (जैसे नेस्टेड लूपों के लिए लूप को गुणा करना), esp। जब आप चाहते हैं कि कोई भी ऊपरी बाध्य अनुमान है, और आपको कोई आपत्ति नहीं है अगर यह बहुत निराशावादी है - जो मुझे लगता है कि शायद आपका सवाल है।

यदि आप वास्तव में किसी भी एल्गोरिथ्म के लिए अपने प्रश्न का उत्तर देना चाहते हैं तो सबसे अच्छा आप सिद्धांत को लागू कर सकते हैं। सरलीकृत "सबसे खराब स्थिति" विश्लेषण के अलावा, मैंने अमूर्त विश्लेषण को व्यवहार में बहुत उपयोगी पाया है ।


6

1 मामले के लिए, आंतरिक लूप को निष्पादित किया जाता n-iहै, इसलिए निष्पादन की कुल संख्या में iसे जाने के लिए योग 0है n-1(क्योंकि कम से कम या इसके बराबर नहीं) n-i। आप अंत में मिल जाते हैं n*(n + 1) / 2, इसलिए O(n²/2) = O(n²)

2 लूप के लिए, बाहरी लूप के iबीच में 0और nशामिल है; तब आंतरिक लूप निष्पादित किया jजाता है जब कड़ाई से अधिक होता है n, जो तब असंभव है।


5

मास्टर विधि (या इसकी विशेषज्ञता में से एक) का उपयोग करने के अलावा, मैं अपने एल्गोरिदम का प्रयोगात्मक रूप से परीक्षण करता हूं। यह साबित नहीं कर सकता है कि किसी विशेष जटिलता वर्ग को हासिल किया गया है, लेकिन यह आश्वस्त कर सकता है कि गणितीय विश्लेषण उचित है। इस आश्वासन के साथ मदद करने के लिए, मैं अपने प्रयोगों के साथ संयोजन में कोड कवरेज टूल का उपयोग करता हूं, यह सुनिश्चित करने के लिए कि मैं सभी मामलों का उपयोग कर रहा हूं।

एक बहुत ही सरल उदाहरण के रूप में, आप .NET फ्रेमवर्क की सूची प्रकार की गति पर एक विवेक जांच करना चाहते थे। आप निम्नलिखित कुछ लिख सकते हैं, फिर एक्सेल में परिणामों का विश्लेषण करके सुनिश्चित करें कि वे n * log (n) वक्र से अधिक नहीं हैं।

इस उदाहरण में मैं तुलना की संख्या को मापता हूं, लेकिन प्रत्येक नमूना आकार के लिए आवश्यक वास्तविक समय की जांच करना भी समझदारी है। हालाँकि तब आपको और भी सावधान रहना चाहिए कि आप एल्गोरिथ्म को माप रहे हैं न कि अपने टेस्ट इन्फ्रास्ट्रक्चर से कलाकृतियों को शामिल कर रहे हैं।

int nCmp = 0;
System.Random rnd = new System.Random();

// measure the time required to sort a list of n integers
void DoTest(int n)
{
   List<int> lst = new List<int>(n);
   for( int i=0; i<n; i++ )
      lst[i] = rnd.Next(0,1000);

   // as we sort, keep track of the number of comparisons performed!
   nCmp = 0;
   lst.Sort( delegate( int a, int b ) { nCmp++; return (a<b)?-1:((a>b)?1:0)); }

   System.Console.Writeline( "{0},{1}", n, nCmp );
}


// Perform measurement for a variety of sample sizes.
// It would be prudent to check multiple random samples of each size, but this is OK for a quick sanity check
for( int n = 0; n<1000; n++ )
   DoTest(n);

4

अंतरिक्ष जटिलताओं के लिए अनुमति देना भी न भूलें, यदि यह सीमित स्मृति संसाधनों के कारण भी चिंता का कारण हो सकता है। इसलिए उदाहरण के लिए आप किसी को एक निरंतर स्थान एल्गोरिथ्म चाहने वाले सुन सकते हैं जो मूल रूप से यह कहने का एक तरीका है कि एल्गोरिथ्म द्वारा ली गई अंतरिक्ष की मात्रा कोड के अंदर किसी भी कारक पर निर्भर नहीं करती है।

कभी-कभी जटिलता यह कह सकती है कि कितनी बार कुछ कहा जाता है, कितनी बार एक लूप निष्पादित किया जाता है, कितनी बार मेमोरी आवंटित की जाती है, और इसी तरह इस प्रश्न का उत्तर देने के लिए एक और हिस्सा है।

अंत में, बिग ओ का उपयोग सबसे खराब मामले, सर्वश्रेष्ठ मामले और परिशोधन के मामलों के लिए किया जा सकता है, जहां आमतौर पर यह सबसे खराब मामला है जिसका उपयोग यह बताने के लिए किया जाता है कि एल्गोरिथ्म कितना बुरा हो सकता है।


4

अक्सर जो अनदेखा किया जाता है वह आपके एल्गोरिदम का अपेक्षित व्यवहार है। यह आपके एल्गोरिथ्म के बिग-ओ को नहीं बदलता है , लेकिन यह "समय से पहले के अनुकूलन" कथन से संबंधित है।

आपके एल्गोरिथ्म का अपेक्षित व्यवहार है - बहुत डंबल डाउन - आप अपने एल्गोरिथ्म को कितनी तेजी से डेटा पर काम करने की उम्मीद कर सकते हैं जो आपको सबसे अधिक संभावना है।

उदाहरण के लिए, यदि आप किसी सूची में मान खोज रहे हैं, तो यह O (n) है, लेकिन यदि आप जानते हैं कि आपके द्वारा देखी जाने वाली अधिकांश सूचियों में आपका मान ऊपर है, तो आपके एल्गोरिथ्म का विशिष्ट व्यवहार तेज़ है।

इसे वास्तव में कील करने के लिए, आपको अपने "इनपुट स्पेस" की संभाव्यता वितरण का वर्णन करने में सक्षम होने की आवश्यकता है (यदि आपको सूची को क्रमबद्ध करने की आवश्यकता है, तो वह सूची कितनी बार पहले से ही छंटनी वाली है? कितनी बार यह पूरी तरह से उलट है? कैसे अक्सर यह ज्यादातर हल होता है?) यह हमेशा संभव नहीं है कि आप जानते हैं कि, लेकिन कभी-कभी आप ऐसा करते हैं।


4

बड़ा सवाल!

डिस्क्लेमर: इस उत्तर में गलत कथन हैं, नीचे दी गई टिप्पणियों को देखें।

यदि आप बिग ओ का उपयोग कर रहे हैं, तो आप बदतर स्थिति के बारे में बात कर रहे हैं (बाद में इसका मतलब क्या है)। इसके अतिरिक्त, औसत मामले के लिए पूंजी थीटा है और सर्वोत्तम मामले के लिए एक बड़ा ओमेगा है।

बिग ओ की एक सुंदर औपचारिक परिभाषा के लिए इस साइट को देखें : https://xlinux.nist.gov/dads/HTML/bigOnotation.html

f (n) = O (g (n)) का अर्थ है सकारात्मक स्थिरांक c और k, जैसे कि 0 ≤ f (n) n cg (n) सभी n। k के लिए। फ़ंक्शन च के लिए c और k का मान निश्चित होना चाहिए और n पर निर्भर नहीं होना चाहिए।


ठीक है, तो अब हम "बेस्ट-केस" और "सबसे खराब-केस" जटिलताओं से क्या मतलब है?

यह संभवतः उदाहरणों के माध्यम से स्पष्ट रूप से चित्रित किया गया है। उदाहरण के लिए यदि हम किसी क्रमबद्ध सरणी में संख्या ज्ञात करने के लिए रैखिक खोज का उपयोग कर रहे हैं तो सबसे खराब स्थिति यह है कि जब हम सरणी के अंतिम तत्व की खोज करने का निर्णय लेते हैं, तो इसके लिए उतने ही कदम उठाने होंगे जितने कि सरणी में आइटम हों। सबसे अच्छा मामला तब होगा जब हम पहले तत्व की खोज करेंगे क्योंकि हम पहली जांच के बाद करेंगे।

इन सभी विशेषणों की जटिलता का बिंदु यह है कि हम किसी विशेष प्रोग्राम के आकार के संदर्भ में एक काल्पनिक कार्यक्रम को पूरा करने के लिए चलने वाले समय को ग्राफ करने के लिए रास्ता तलाश रहे हैं। हालांकि कई एल्गोरिदम के लिए आप तर्क दे सकते हैं कि किसी विशेष आकार के इनपुट के लिए एक समय नहीं है। ध्यान दें कि किसी फ़ंक्शन की मूलभूत आवश्यकता के साथ यह विरोधाभास, किसी भी इनपुट में एक से अधिक आउटपुट नहीं होना चाहिए। इसलिए हम एक एल्गोरिथ्म की जटिलता का वर्णन करने के लिए कई कार्यों के साथ आते हैं । अब, भले ही आकार n की एक सरणी खोज रहे हों, आप सरणी में क्या देख रहे हैं और आनुपातिक रूप से n के आधार पर अलग-अलग समय लग सकता है, हम सर्वश्रेष्ठ-केस, औसत-केस का उपयोग करके एल्गोरिथ्म का एक सूचनात्मक विवरण बना सकते हैं। , और सबसे खराब स्थिति वाली कक्षाएं।

क्षमा करें, यह बहुत खराब लिखा गया है और तकनीकी जानकारी का अभाव है। लेकिन उम्मीद है कि यह समय जटिलता वर्गों के बारे में सोचने के लिए आसान बना देगा। एक बार जब आप इन के साथ सहज हो जाते हैं, तो यह आपके प्रोग्राम के माध्यम से पार्स करने का एक साधारण मामला बन जाता है और लूप जैसी चीजों की तलाश करता है, जो सरणी के आकार पर निर्भर करता है और आपके डेटा संरचनाओं के आधार पर तर्क देता है कि किस तरह के इनपुट से तुच्छ मामलों में परिणाम होगा और क्या इनपुट परिणाम होगा। सबसे बुरे मामलों में।


1
यह गलत है। बिग ओ का मतलब है "अपर बाउंड" सबसे खराब स्थिति नहीं है।
सैमी बेनचेरी

1
यह एक आम गलत धारणा है कि बिग-ओ सबसे खराब स्थिति को दर्शाता है। O और do सबसे खराब और सबसे अच्छे मामले से कैसे संबंधित हैं?
बर्नहार्ड बार्कर

1
यह भ्रामक है। बिग-ओ का अर्थ है किसी फ़ंक्शन के लिए ऊपरी बाउंड f (n)। ओमेगा का अर्थ है एक फ़ंक्शन के लिए कम बाउंड f (n)। यह सबसे अच्छा मामला या सबसे खराब मामले से संबंधित नहीं है।
तसनीम हैदर

1
आप बिग-ओ को सबसे अच्छे या सबसे खराब मामले के लिए ऊपरी सीमा के रूप में उपयोग कर सकते हैं, लेकिन इसके अलावा, हाँ कोई संबंध नहीं है।
सामी बेनचेरीफ

2

मुझे नहीं पता कि यह कैसे प्रोग्रामेटिक रूप से हल करना है, लेकिन पहली बात यह है कि हम किए गए कार्यों की संख्या में निश्चित पैटर्न के लिए एल्गोरिथ्म का नमूना लेते हैं, कहते हैं 4n ^ 2 + 2n + 1 हमारे पास 2 नियम हैं:

  1. यदि हमारे पास शर्तें हैं, तो सबसे बड़ी विकास दर के साथ शब्द रखा गया है, अन्य शर्तों के साथ छोड़ दिया गया है।
  2. यदि हमारे पास कई कारकों का एक उत्पाद है तो स्थिर कारक छोड़ दिए जाते हैं।

यदि हम f (x) को सरल बनाते हैं, जहाँ f (x) किए गए कार्यों की संख्या का सूत्र है, (4n ^ 2 + 2n + 1 ऊपर बताया गया है), तो हम इसमें बड़े-O मान [O (n ^ 2) प्राप्त करते हैं। मामला]। लेकिन इस कार्यक्रम में लैग्रेग प्रक्षेप के लिए जिम्मेदार होगा, जिसे लागू करना कठिन हो सकता है। और क्या होगा अगर वास्तविक बड़ा-ओ मान O (2 ^ n) था, और हमारे पास O (x ^ n) जैसा कुछ हो सकता है, इसलिए यह एल्गोरिथम शायद प्रोग्राम करने योग्य नहीं होगा। लेकिन अगर कोई मुझे गलत साबित करता है, तो मुझे कोड दें। । । ।


2

कोड ए के लिए, बाहरी लूप n+1समय के लिए निष्पादित करेगा , '1' समय का अर्थ है वह प्रक्रिया जो जांचती है कि क्या मैं अभी भी आवश्यकता को पूरा करता हूं। और भीतर का पाश nबार, n-2बार चलता है .... इस प्रकार,0+2+..+(n-2)+n= (0+n)(n+1)/2= O(n²) ,।

कोड B के लिए, हालांकि आंतरिक लूप चरण में नहीं आएगा और फू () निष्पादित करेगा, आंतरिक लूप को निष्पादित किया जाएगा n बार बाहरी लूप निष्पादन समय पर निर्भर करता है, जो O (n) है


1

मैं बिग-ओ को थोड़ा अलग पहलू से समझाना चाहूंगा।

बिग-ओ सिर्फ कार्यक्रमों की जटिलता की तुलना करने के लिए है जिसका मतलब है कि इनपुट बढ़ने पर वे कितनी तेजी से बढ़ रहे हैं और सटीक समय नहीं है जो कार्रवाई करने के लिए खर्च किया जाता है।

बड़े-ओ फ़ार्मुलों में IMHO आप अधिक जटिल समीकरणों का उपयोग नहीं करने के लिए बेहतर है (आप निम्नलिखित ग्राफ में केवल लोगों से चिपके रह सकते हैं।) हालांकि आप अभी भी अन्य अधिक सटीक सूत्र (जैसे 3 ^ n, n ^ 3, ।।) का उपयोग कर सकते हैं। ।) लेकिन इससे अधिक कभी-कभी भ्रामक हो सकता है! इसलिए इसे जितना संभव हो उतना सरल रखने के लिए बेहतर है।

यहां छवि विवरण दर्ज करें

मैं एक बार फिर जोर देना चाहूंगा कि यहां हम अपने एल्गोरिथ्म के लिए एक सटीक सूत्र नहीं प्राप्त करना चाहते हैं। हम केवल यह दिखाना चाहते हैं कि यह कैसे बढ़ता है जब इनपुट बढ़ रहे हैं और उस अर्थ में अन्य एल्गोरिदम के साथ तुलना करते हैं। अन्यथा आप बेंच-मार्किंग जैसे विभिन्न तरीकों का बेहतर उपयोग करेंगे।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.