PHP कार्यों के लिए बिग-ओ की सूची


346

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

//very slow for large $prime_array
$prime_array = array( 2, 3, 5, 7, 11, 13, .... 104729, ... );
$result_array = array();
foreach( $prime_array => $number ) {
    $result_array[$number] = in_array( $number, $large_prime_array );
}

//speed is much less dependent on size of $prime_array, and runs much faster.
$prime_array => array( 2 => NULL, 3 => NULL, 5 => NULL, 7 => NULL,
                       11 => NULL, 13 => NULL, .... 104729 => NULL, ... );
foreach( $prime_array => $number ) {
    $result_array[$number] = array_key_exists( $number, $large_prime_array );
}

ऐसा इसलिए है क्योंकि in_arrayइसे रैखिक खोज O (n) के साथ कार्यान्वित किया जाता है जो कि रैखिक रूप से $prime_arrayबढ़ता हुआ धीमा हो जाएगा । जहां array_key_existsफ़ंक्शन को हैश लुकिंग O (1) के साथ कार्यान्वित किया जाता है जो तब तक धीमा नहीं होगा जब तक कि हैश तालिका अत्यंत आबादी वाली न हो (जिस स्थिति में यह केवल O (n) है)।

अब तक मुझे परीक्षण और त्रुटि के माध्यम से बिग-ओ की खोज करनी थी, और कभी-कभी स्रोत कोड को देखना पड़ता था । अब सवाल के लिए ...

क्या सभी * अंतर्निहित PHP कार्यों के लिए सैद्धांतिक (या व्यावहारिक) बड़े ओ समय की सूची है?

* या कम से कम दिलचस्प हैं

उदाहरण के लिए, मैं सूचीबद्ध कार्यों का बड़ा हे भविष्यवाणी करने के लिए यह बहुत मुश्किल है क्योंकि संभव कार्यान्वयन PHP के अज्ञात मूल डेटा संरचनाओं पर निर्भर करता है: array_merge, array_merge_recursive, array_reverse, array_intersect, array_combine, str_replace(सरणी जानकारी के साथ), आदि


31
पूरी तरह से विषय से दूर लेकिन, 1 प्रमुख नहीं है।
जेसन Punyon

24
PHP में Arrays hashtables हैं। आपको वह सब कुछ बताना चाहिए जो आपको जानना चाहिए। हैशटेबल में एक कुंजी की खोज ओ (1) है। मान के लिए खोज करना हे (n) - जिसे आप एक बिना सेट के हरा नहीं सकते। अधिकांश फ़ंक्शंस जिनके बारे में आप उत्सुक हैं, संभवतः O (n) हैं। बेशक, यदि आप वास्तव में जानना चाहते हैं, तो आप स्रोत पढ़ सकते हैं: cvs.php.net/viewvc.cgi/php-src/ext/standard/…
फ्रैंक किसान

11
रिकॉर्ड के लिए, आप जो करने की कोशिश कर रहे हैं उसका सबसे तेज़ कार्यान्वयन (अपने मूल्यों के लिए NULL का उपयोग करने के बजाय) उपयोग करें trueऔर फिर उपस्थिति का उपयोग करके परीक्षण करें isset($large_prime_array[$number])। अगर मुझे सही याद है, तो यह in_arrayफंक्शन से सैकड़ों गुना तेज होने के क्रम में है।
मटकाबस्टा

3
बिग ओ नोटेशन गति के बारे में नहीं है। यह व्यवहार को सीमित करने के बारे में है।
गुम्बो

3
@ केंडल मैं तुलना नहीं कर array_key_existsरहा हूं, मैं तुलना कर रहा हूं in_arrayin_arrayसरणी में प्रत्येक आइटम को पुनरावृत्त करता है और सुई के मूल्य की तुलना करता है जो आप इसे पास करते हैं। यदि आप मानों को कुंजी पर फ्लिप करते हैं (और बस प्रत्येक मान को एक डमी मान से बदल देते हैं जैसे true, का उपयोग issetकरना कई गुना तेज है। ऐसा इसलिए है क्योंकि सरणी की कुंजी PHP द्वारा अनुक्रमित की जाती है (जैसे हैशटेबल की तरह)। इस तरह से एक सरणी में गति में एक महत्वपूर्ण सुधार हो सकता है।
मटकाबस्टा

जवाबों:


650

चूँकि ऐसा नहीं लगता कि किसी ने भी ऐसा किया है, इससे पहले कि मुझे लगता है कि इसे कहीं संदर्भ के लिए रखना अच्छा होगा। मैं हालांकि या तो बेंचमार्क या कोड-स्कीमिंग के माध्यम से array_*कार्यों को चिह्नित करने के लिए चला गया हूं । मैंने शीर्ष के पास अधिक दिलचस्प बिग-ओ लगाने की कोशिश की है। यह सूची पूर्ण नहीं है।

नोट: सभी बिग-ओ जहां हैश लुकिंग की गणना ओ (1) है, हालांकि यह वास्तव में ओ (एन) है। N का गुणांक इतना कम है, एक बड़े पर्याप्त सरणी के भंडारण का राम उपरि आपको बिग-ओ देखने की विशेषताओं को प्रभावित करने से पहले आपको चोट पहुंचाएगा। उदाहरण के लिए array_key_exists, N = 1 और N = 1,000,000 पर कॉल के बीच का अंतर ~ 50% समय वृद्धि है।

दिलचस्प अंक :

  1. isset/ array_key_existsकी तुलना में बहुत तेज है in_arrayऔरarray_search
  2. +(संघ) की तुलना में थोड़ा तेज है array_merge(और अच्छा लग रहा है)। लेकिन यह अलग तरह से काम करता है इसलिए इसे ध्यान में रखें।
  3. shuffle के रूप में एक ही बिग-ओ टियर पर है array_rand
  4. array_pop/ array_pushसे अधिक है array_shift/ array_unshiftपुन: इंडेक्स पेनल्टी के कारण

लुकअप :

array_key_existsO (n) लेकिन वास्तव में O (1) के करीब है - यह टक्करों में रैखिक मतदान के कारण है, लेकिन क्योंकि टक्करों की संभावना बहुत कम है, गुणांक भी बहुत छोटा है। मुझे लगता है कि आप हैश लुकअप को ओ (1) के रूप में मानते हैं ताकि अधिक यथार्थवादी बिग-ओ दिया जा सके। उदाहरण के लिए N = 1000 और N = 100000 के बीच का अंतर केवल 50% धीमा है।

isset( $array[$index] )O (n) लेकिन वास्तव में O (1) के करीब है - यह array_key_exists के समान लुकअप का उपयोग करता है। चूंकि यह भाषा का निर्माण है, कुंजी को हार्डकोड किए जाने पर लुकअप कैश कर देगा, जिसके परिणामस्वरूप उन मामलों में गति होगी जहां एक ही कुंजी का बार-बार उपयोग किया जाता है।

in_array O (n) - यह इसलिए है क्योंकि यह एक रैखिक खोज करता है, हालांकि यह मान मिलने तक सरणी को दर्शाता है।

array_search O (n) - यह in_array के समान कोर फ़ंक्शन का उपयोग करता है, लेकिन मान लौटाता है।

कतार कार्य :

array_push O (∑ var_i, सभी के लिए)

array_pop हे (1)

array_shift O (n) - इसमें सभी चाबियों को फिर से लगाना होता है

array_unshift O (n + (var_i, सभी i के लिए) - इसमें सभी कुंजियों को फिर से लिखना होगा

सरणी अंतर्ग्रहण, संघ, घटाव :

array_intersect_key यदि चौराहा 100% ओ (अधिकतम (param_i_size) * _param_i_count, सभी के लिए), अगर प्रतिच्छेदन 0% प्रतिच्छेदन O (amparam_i_size, सभी के लिए)

array_intersect यदि चौराहे 100% हे (n ^ 2 * _param_i_count, सभी i के लिए), यदि चौराहा 0% प्रतिच्छेदन O (n ^ 2)

array_intersect_assoc यदि चौराहा 100% ओ (अधिकतम (param_i_size) * _param_i_count, सभी के लिए), अगर प्रतिच्छेदन 0% प्रतिच्छेदन O (amparam_i_size, सभी के लिए)

array_diff O (for param_i_size, सभी के लिए) - यह सभी param_sizes का उत्पाद है

array_diff_key O (i param_i_size, i के लिए! = 1) - ऐसा इसलिए है क्योंकि हमें पहली सरणी पर पुनरावृति करने की आवश्यकता नहीं है।

array_merge O (- array_i, i! = 1) - पहली सरणी पर पुनरावृति करने की आवश्यकता नहीं है

+ (संघ) O (n), जहां n का दूसरा आकार है (यानी array_first + array_second) - array_merge से कम ओवरहेड क्योंकि इसे फिर से नहीं करना है

array_replace O (∑ array_i, सभी के लिए)

रैंडम :

shuffle पर)

array_rand O (n) - एक रैखिक मतदान की आवश्यकता है।

स्पष्ट बिग-ओ :

array_fill पर)

array_fill_keys पर)

range पर)

array_splice O (ऑफसेट + लंबाई)

array_slice O (ऑफसेट + लंबाई) या O (n) यदि लंबाई = NULL

array_keys पर)

array_values पर)

array_reverse पर)

array_pad ओ (pad_size)

array_flip पर)

array_sum पर)

array_product पर)

array_reduce पर)

array_filter पर)

array_map पर)

array_chunk पर)

array_combine पर)

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

संपादित करें:

उन लोगों के लिए जो संदेह करते हैं कि PHP सरणी लुकअप हैं O(N), मैंने परीक्षण करने के लिए एक बेंचमार्क लिखा है कि (वे अभी भी O(1)सबसे यथार्थवादी मूल्यों के लिए प्रभावी रूप से हैं)।

php सरणी लुकअप ग्राफ

$tests = 1000000;
$max = 5000001;


for( $i = 1; $i <= $max; $i += 10000 ) {
    //create lookup array
    $array = array_fill( 0, $i, NULL );

    //build test indexes
    $test_indexes = array();
    for( $j = 0; $j < $tests; $j++ ) {
        $test_indexes[] = rand( 0, $i-1 );
    }

    //benchmark array lookups
    $start = microtime( TRUE );
    foreach( $test_indexes as $test_index ) {
        $value = $array[ $test_index ];
        unset( $value );
    }
    $stop = microtime( TRUE );
    unset( $array, $test_indexes, $test_index );

    printf( "%d,%1.15f\n", $i, $stop - $start ); //time per 1mil lookups
    unset( $stop, $start );
}

5
@ केंडल: धन्यवाद! मैंने थोड़ा सा पढ़ा और यह पता चला कि PHP टकरावों के लिए 'नेस्टेड' हैशटैब का उपयोग करती है। यही है, टक्करों के लिए एक लॉगऑन संरचना के बजाय यह बस एक और हैशटेबल का उपयोग करता है। और मैं समझता हूं कि व्यावहारिक रूप से PHP हैशटेबल्स ओ (1) प्रदर्शन देते हैं, या कम से कम ओ (1) औसतन - यही हैशटैबल्स के लिए हैं। मैं सिर्फ इस बात के लिए उत्सुक था कि आपने यह क्यों कहा कि वे वास्तव में ओ (एन) हैं और "वास्तव में ओ (लॉगन)" नहीं हैं। वैसे बढ़िया पोस्ट!
कैम

10
समय की जटिलताओं को प्रलेखन के साथ शामिल किया जाना चाहिए! सही फ़ंक्शन का चयन करने से आप इतना समय बचा सकते हैं, या आपको बताएंगे कि आपने जो करने की योजना बनाई है उससे बचने के लिए: p इस सूची के लिए पहले से ही धन्यवाद!
शमूएल

41
मुझे पता है यह पुराना है ... लेकिन क्या? वह वक्र O (n) को बिल्कुल नहीं दिखाता है, यह O (log n), en.wikipedia.org/wiki/Logarithm दिखाता है । आप नेस्टेड हैश-मैप्स के लिए क्या उम्मीद करेंगे, यह भी सटीक है।
एंड्रियास

5
किसी सरणी के एक तत्व पर बिग-ओ क्या है?
चंद्रे 16

12
जबकि हैशटेबल्स में वास्तव में सबसे खराब स्थिति ओ (एन) लुकिंग जटिलता है, औसत केस ओ (1) है और आपके बेंचमार्क का परीक्षण करने वाला विशेष मामला भी ओ (1) की गारंटी है , क्योंकि यह शून्य-आधारित, निरंतर, संख्यात्मक रूप से अनुक्रमित है सरणी, जिसमें हैश टकराव कभी नहीं होगा। जिस कारण से आप अभी भी सरणी आकार पर निर्भरता देख रहे हैं, उसका एल्गोरिथम जटिलता से कोई लेना-देना नहीं है, यह CPU कैश प्रभाव के कारण होता है। सरणी जितनी बड़ी होगी, उतनी ही अधिक संभावना होगी कि रैंडम-एक्सेस लुकअप के परिणामस्वरूप कैश मिस (और कैश पदानुक्रम में उच्चतर मिस) हो जाएगा।
निकीक २ N

5

आपके द्वारा विशेष रूप से वर्णित मामले के लिए स्पष्टीकरण यह है कि साहचर्य सरणियों को हैश तालिकाओं के रूप में लागू किया जाता है - इसलिए कुंजी द्वारा खोज (और तदनुसार array_key_exists), हे (1) है। हालांकि, सरणियों को मूल्य द्वारा अनुक्रमित नहीं किया जाता है, इसलिए सामान्य मामले में यह पता लगाने का एकमात्र तरीका है कि क्या सरणी में मौजूद मूल्य एक रैखिक खोज है। वहां कोई आश्चर्य नहीं है।

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


यह वास्तव में एक जवाब नहीं है। जैसा कि मैंने प्रश्न में कहा है, मैंने पहले ही PHP स्रोत कोड देखने की कोशिश की है। चूंकि PHP को कार्यान्वित किया जाता है, इसलिए इसे जटिल मैक्रोज़ के उपयोग के C बनाने में लिखा जाता है, जो कई बार कार्यों के लिए अंतर्निहित बड़े O को "देखना" कर सकता है।
केंडल हॉपकिंस

@ केंडल मैं स्रोत कोड में गोताखोरी के लिए आपके संदर्भ को नजरअंदाज कर दिया। हालांकि, मेरे जवाब में एक जवाब है: "मुझे नहीं लगता कि PHP विधियों के एल्गोरिथ्म जटिलता की विशिष्ट व्यापक प्रलेखन है।" "नहीं" पूरी तरह से मान्य उत्तर है। (c:
दतान

4

आप लगभग हमेशा के issetबजाय उपयोग करना चाहते हैं array_key_exists। मैं इंटर्नल को नहीं देख रहा हूं, लेकिन मुझे पूरा यकीन है कि array_key_existsयह ओ (एन) है क्योंकि यह एरे के प्रत्येक कुंजी पर निर्भर issetकरता है , जबकि उसी हैश एल्गोरिथ्म का उपयोग करते हुए तत्व तक पहुंचने की कोशिश करता है जो आपके उपयोग करने पर उपयोग किया जाता है एक सरणी सूचकांक। वह ओ (1) होना चाहिए।

यह देखने के लिए एक "गोत्र" है:

$search_array = array('first' => null, 'second' => 4);

// returns false
isset($search_array['first']);

// returns true
array_key_exists('first', $search_array);

मैं उत्सुक था, इसलिए मैंने अंतर को चिह्नित किया:

<?php

$bigArray = range(1,100000);

$iterations = 1000000;
$start = microtime(true);
while ($iterations--)
{
    isset($bigArray[50000]);
}

echo 'is_set:', microtime(true) - $start, ' seconds', '<br>';

$iterations = 1000000;
$start = microtime(true);
while ($iterations--)
{
    array_key_exists(50000, $bigArray);
}

echo 'array_key_exists:', microtime(true) - $start, ' seconds';
?>

is_set:0.132308959961 सेकंड
array_key_exists:2.33202195168 सेकंड

बेशक, यह समय जटिलता नहीं दिखाता है, लेकिन यह दिखाता है कि 2 फ़ंक्शन एक दूसरे की तुलना कैसे करते हैं।

समय की जटिलता के लिए परीक्षण करने के लिए, पहली कुंजी और अंतिम कुंजी पर इनमें से किसी एक कार्य को चलाने में लगने वाले समय की तुलना करें।


9
ये गलत है। मुझे 100% यकीन है कि array_key_exists को प्रत्येक कुंजी पर चलना नहीं पड़ता है। यदि आपको विश्वास नहीं है कि नीचे दिए गए लिंक पर एक नज़र डालें। परेशान होने का कारण इतना तेज है कि यह एक भाषा निर्माण है। जिसका अर्थ है कि इसमें फंक्शन कॉल करने का ओवरहेड नहीं है। इसके अलावा, मुझे लगता है कि यह लुक कैशिंग हो सकता है, इस वजह से। इसके अलावा, यह सवाल का जवाब नहीं है! मैं PHP कार्यों के लिए बिग (ओ) की एक सूची (प्रश्न राज्यों के रूप में) चाहूंगा। मेरे उदाहरणों का एक भी बेंचमार्क नहीं। svn.php.net/repository/php/php-src/branches/PHP_5_3/ext/…
केंडल हॉपकिंस

यदि आप अभी भी मुझ पर विश्वास नहीं करते हैं, तो मैंने बिंदु को प्रदर्शित करने के लिए एक छोटा बेंचमार्क बनाया है। pastebin.com/BdKpNvkE
केंडल हॉपकिंस

आपके बेंचमार्क में क्या गलत है कि आपको xdebug को अक्षम करना होगा। =)
गिलहर्मे ब्लांको

3
दो महत्वपूर्ण कारण हैं कि आप array_key_exists पर isset का उपयोग क्यों करना चाहते हैं। सबसे पहले, isset एक फ़ंक्शन कॉल की लागत को कम करने वाली एक भाषा निर्माण है। यह $arrray[] = $appendबनाम array_push($array, $append)तर्क के समान है । दूसरा, array_key_exists गैर-सेट और शून्य मानों के बीच अंतर भी करता है। क्योंकि $a = array('fred' => null); array_key_exists('fred', $a)सच्चा लौटेगा जबकि isset($['fred'])असत्य लौटेगा। यह अतिरिक्त कदम गैर-तुच्छ है और निष्पादन समय को बहुत बढ़ा देगा।
ओर्का

0

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

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