इंजेक्शन (: +) की तुलना में योग इतना तेज़ क्यों है?


129

इसलिए मैं रूबी 2.4.0 में कुछ बेंचमार्क चला रहा था और महसूस किया

(1...1000000000000000000000000000000).sum

जबकि तुरंत गणना करता है

(1...1000000000000000000000000000000).inject(:+)

इतना समय लगता है कि मैंने ऑपरेशन को रद्द कर दिया। मैं इस धारणा के तहत था कि Range#sumइसके लिए एक उपनाम था Range#inject(:+)लेकिन ऐसा लगता है कि यह सच नहीं है। तो कैसे sumकाम करता है , और यह इतनी तेजी से क्यों हैinject(:+) ?

एनबी के लिए प्रलेखन Enumerable#sum(जिसके द्वारा कार्यान्वित किया जाता है Range) आलसी मूल्यांकन या उन पंक्तियों के साथ कुछ भी नहीं कहता है।

जवाबों:


227

संक्षिप्त जवाब

पूर्णांक श्रेणी के लिए:

  • Enumerable#sum रिटर्न (range.max-range.min+1)*(range.max+range.min)/2
  • Enumerable#inject(:+) हर तत्व पर पुनरावृत्ति।

सिद्धांत

1 के बीच पूर्णांक का योग और nएक त्रिकोणीय संख्या कहा जाता है , और के बराबर है n*(n+1)/2

के बीच पूर्णांकों का योग nऔर mकी त्रिकोणीय संख्या है mशून्य से की त्रिकोणीय संख्या n-1है, जो के बराबर है m*(m+1)/2-n*(n-1)/2, और लिखा जा सकता है (m-n+1)*(m+n)/2

रूबी 2.4 में अनगिनत # योग

Enumerable#sumपूर्णांक श्रेणी के लिए उपयोग की गई यह संपत्ति :

if (RTEST(rb_range_values(obj, &beg, &end, &excl))) {
    if (!memo.block_given && !memo.float_value &&
            (FIXNUM_P(beg) || RB_TYPE_P(beg, T_BIGNUM)) &&
            (FIXNUM_P(end) || RB_TYPE_P(end, T_BIGNUM))) { 
        return int_range_sum(beg, end, excl, memo.v);
    } 
}

int_range_sum इस तरह दिखता है :

VALUE a;
a = rb_int_plus(rb_int_minus(end, beg), LONG2FIX(1));
a = rb_int_mul(a, rb_int_plus(end, beg));
a = rb_int_idiv(a, LONG2FIX(2));
return rb_int_plus(init, a);

जो इसके बराबर है:

(range.max-range.min+1)*(range.max+range.min)/2

उक्त समानता!

जटिलता

इस भाग के लिए @k_g और @ Hynek-Pichi-Vychodil को बहुत-बहुत धन्यवाद!

योग

(1...1000000000000000000000000000000).sum तीन परिवर्धन, एक गुणन, एक घटाव और एक विभाजन की आवश्यकता होती है।

यह संचालन की एक निरंतर संख्या है, लेकिन गुणन Enumerable#sumएक पूर्णांक सीमा के लिए O ((log n) operations) है, इसलिए O ((log n) operations) है।

इंजेक्षन

(1...1000000000000000000000000000000).inject(:+)

999999999999999999999999999998 परिवर्धन की आवश्यकता है!

जोड़ ओ (लॉग एन) है, इसलिए Enumerable#injectओ (एन लॉग एन) है।

साथ 1E30इनपुट के रूप में, injectके साथ वापस कभी नहीं। सूरज बहुत पहले फट जाएगा!

परीक्षा

यह जांचना आसान है कि रूबी इंटेगर को जोड़ा जा रहा है या नहीं:

module AdditionInspector
  def +(b)
    puts "Calculating #{self}+#{b}"
    super
  end
end

class Integer
  prepend AdditionInspector
end

puts (1..5).sum
#=> 15

puts (1..5).inject(:+)
# Calculating 1+2
# Calculating 3+3
# Calculating 6+4
# Calculating 10+5
#=> 15

enum.cटिप्पणियों से वास्तव में :

Enumerable#sumमेथड रिडिफाइनमेंट ऑफ मेथड इज़ नॉट द "+" मेथड्स ऑफ़ मेथड जैसे Integer#+


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

तो प्रदर्शन का विस्तार n+1केवल श्रेणियों के लिए है? मेरे पास 2.4 स्थापित नहीं हैं या मैं खुद का परीक्षण करूंगा, लेकिन बुनियादी रूप से इसके अलावा अन्य अनगिनत वस्तुओं को संभाला जाएगा क्योंकि वे खरीद inject(:+)के लिए प्रतीक के उपर ऋण में होंगे ।
इंजीनियरमंकी

8
पाठक, अपने हाई-स्कूल गणित से याद करते हैं जो n, n+1, n+2, .., mएक अंकगणितीय श्रृंखला का गठन करता है जिसकी राशि बराबर होती है (m-n+1)*(m+n)/2। इसी तरह, एक का योग ज्यामितीय श्रृंखला , n, (α^1)n, (α^2)n, (α^3)n, ... , (α^m)n। एक बंद-रूप अभिव्यक्ति से गणना की जा सकती है।
कैरी स्वेवेलैंड

4
\ start {nitpick} Enumerable # योग है ओ ((लॉग एन) ^ 2) और इंजेक्शन है ओ (एन लॉग एन) जब आपके नंबर अनबिके जाने की अनुमति है। \ end {
नाइटपिक

6
@EliSadoff: इसका मतलब वास्तव में बड़ी संख्या है। इसका मतलब है कि वे नंबर जो आर्किटेक्चर शब्द को फिट नहीं करते हैं, अर्थात एक निर्देश और एक कोर सीपीयू कोर में एक ऑपरेशन द्वारा गणना नहीं की जा सकती है। आकार N की संख्या log_2 द्वारा एनकोड की जा सकती है इसलिए बी बिट्स O (logN) ऑपरेशन है और गुणा O ((logN) ^ 2) है, लेकिन O ((logN) ^ 1.585) (Karasuba) या O O (logN * हो सकता है) log (logN) * ​​log (लॉग (logN)) (FFT)
Hynek -Pichi- Vychodil
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.