2 संख्याओं के बीच भारी दशमलव की संख्या की गणना करें


16

मान लें कि हमारे पास एक गैर-नकारात्मक पूर्णांक है जो "भारी" है (अर्थात, "भारी") यदि इसका औसत अंक मान 7 से अधिक है।

संख्या 6959 "भारी" है क्योंकि:

(6 + 9 + 5 + 9) / 4 = 7.5

संख्या 1234 नहीं है, क्योंकि:

(१ + २ + ३ + ४) / ४ = २.५

एक फ़ंक्शन लिखें, किसी भी भाषा में,

HeftyDecimalCount(a, b)

जब, दो पॉजिटिव पूर्णांकों को प्रदान किया जाता है और बी एक पूर्णांक देता है जो यह दर्शाता है कि कितने "हेफ्टी" पूर्णांक इंटरवल [a.b], समावेशी के भीतर हैं।

उदाहरण के लिए, a = 9480 और b = 9489 दिया गया:

9480   (9+4+8+0)/4 21/4 = 5.25 
9481   (9+4+8+1)/4 22/4 = 5.5
9482   (9+4+8+2)/4 23/4 = 5.75  
9483   (9+4+8+3)/4 24/4 = 6    
9484   (9+4+8+4)/4 25/4 = 6.25     
9485   (9+4+8+5)/4 26/4 = 6.5 
9486   (9+4+8+6)/4 27/4 = 6.75  
9487   (9+4+8+7)/4 28/4 = 7
9488   (9+4+8+8)/4 29/4 = 7.25   hefty 
9489   (9+4+8+9)/4 30/4 = 7.5    hefty

इस सीमा में दो संख्याएं "भारी" हैं और इसलिए फ़ंक्शन को 2 वापस करना चाहिए।

कुछ दिशानिर्देश:

  • मान लें कि न तो 200,000,000 से अधिक का है।
  • एक एन-स्क्वैयर समाधान काम करेगा, लेकिन धीमा होगा - हम इसे हल करने के लिए सबसे तेज़ क्या हो सकते हैं?

2
क्या समय फेंक दिया?

जवाबों:


11

समस्या को O (बहुभुज (b)) में हल किया जा सकता है।

हम f(d, n)d दशमलव अंकों के पूर्णांक की संख्या को n से कम या उसके बराबर अंक के साथ परिभाषित करते हैं। यह देखा जा सकता है कि यह फ़ंक्शन सूत्र द्वारा दिया गया है

एफ (डी, एन)

चलो कुछ सरल से शुरू करते हुए, इस फ़ंक्शन को प्राप्त करें।

h (n, d) = \ binom {n + d-1} {d-1} = \ binom {(n + 1) + (d-1) -1} {d-1}

फ़ंक्शन h, d + 1 तत्वों को चुनने के तरीकों की संख्या को गिनता है, जिसमें n-1 विभिन्न तत्वों वाले बहु-सेट होते हैं। यह भी तरीकों की संख्या है n n को d डिब्बे में विभाजित करने के लिए, जो आसानी से d - 1 बाड़ n के आसपास बनाकर देखी जा सकती है और प्रत्येक अलग-अलग अनुभाग को जोड़ सकती है। N = 2, d = 3 'के लिए उदाहरण:

3-choose-2     fences        number
-----------------------------------
11             ||11          002
12             |1|1          011
13             |11|          020
22             1||1          101
23             1|1|          110
33             11||          200

तो, h, n और d अंकों के अंक-योग वाली सभी संख्याओं को गिनता है। सिवाय इसके कि यह केवल 10 से कम n के लिए काम करता है, क्योंकि अंक 0 - 9 तक सीमित हैं। 10 - 19 मानों के लिए इसे ठीक करने के लिए, हमें उन विभाजनों की संख्या को घटाना होगा, जिनकी संख्या 9 से अधिक के साथ एक बिन है, जिसे मैं अब से ओवरफ्लो बिन कहूंगा।

निम्नलिखित तरीके से h का पुन: उपयोग करके इस शब्द की गणना की जा सकती है। हम n - 10 के विभाजन के तरीकों की संख्या की गणना करते हैं, और फिर 10 में डालने के लिए एक डिब्बे का चयन करते हैं, जिसके परिणामस्वरूप एक से अधिक बिन वाले विभाजन की संख्या होती है। परिणाम निम्नलिखित प्रारंभिक कार्य है।

g (n, d) = \ binom {n + d-1} {d-1} - \ binom {d} {1} \ binom {n + d-1-10} {d-1}

हम इस तरह से n कम या बराबर 29 के लिए जारी रखते हैं, विभाजन के सभी तरीकों की गिनती करके n - 20, फिर 2 डिब्बे का चयन करते हैं जहां हम 10 का डालते हैं, जिससे 2 ओवरफ्लो डिब्बे वाले विभाजन की संख्या की गणना होती है।

लेकिन इस बिंदु पर हमें सावधान रहना होगा, क्योंकि हमने पहले ही पिछले कार्यकाल में 2 ओवरफ्लो डिब्बे वाले विभाजन गिना। केवल इतना ही नहीं, बल्कि वास्तव में हमने उन्हें दो बार गिना। आइए एक उदाहरण का उपयोग करें और 21 के साथ विभाजन (10,0,11) को देखें। पिछले कार्यकाल में, हमने 10 को घटाया, शेष 11 के सभी विभाजन की गणना की और 10 को 3 डिब्बे में से एक में डाल दिया। लेकिन इस विशेष विभाजन को दो तरीकों में से एक में पहुँचा जा सकता है:

(10, 0, 1) => (10, 0, 11)
(0, 0, 11) => (10, 0, 11)

चूँकि हमने पहले भाग में एक बार इन विभाजनों को भी गिना था, इसलिए 2 अतिप्रवाह बिन के साथ विभाजनों की कुल संख्या 1 - 2 = -1 है, इसलिए हमें अगला पद जोड़कर एक बार और गिनना होगा।

g (n, d) = \ binom {n + d-1} {d-1} - \ binom {d} {1} \ binom {n + d-1-10} {d-1} + \ binom { d} {2} \ binom {n + d-1-20} {d-1}

इस बारे में थोड़ा और सोचने पर, हमें जल्द ही पता चलता है कि एक विशेष संख्या में ओवरफ्लो डिब्बे की संख्या के साथ एक विभाजन को एक विशिष्ट शब्द में गिना जाता है, जिसे निम्न तालिका द्वारा व्यक्त किया जा सकता है (कॉलम i, जो शब्द i का प्रतिनिधित्व करता है, पंक्ति j विभाजन को jflown के साथ डिब्बे)।

1 0 0 0 0 0 . .
1 1 0 0 0 0 . .
1 2 1 0 0 0 . .
1 4 6 4 1 0 . .
. . . . . . 
. . . . . . 

हाँ, यह पास्कल त्रिकोण है। केवल हम जिस चीज में रुचि रखते हैं, वह पहली पंक्ति / कॉलम में से एक है, यानी शून्य ओवरफ्लो डिब्बे के साथ विभाजन की संख्या। और हर पंक्ति के वैकल्पिक योग के बाद से पहले 0 (जैसे 1 - 4 + 6 - 4 + 1 = 0) के बराबर है, कि कैसे हम उनसे छुटकारा पा लेते हैं और प्रायद्वीप सूत्र पर पहुंचते हैं।

g (n, d) = \ sum_ {i = 0} ^ {d} (-1) ^ i \ binom {d} {i} \ binom {n + d-1 - 10i} {d-1}

यह फ़ंक्शन सभी अंकों को गिनता है, जिसमें d अंकों के साथ n का अंक-योग होता है।

अब, अंक से योग के बारे में क्या n से कम है? हम यह दिखाने के लिए कि एक द्विपद प्लस एक आगमनात्मक तर्क के लिए एक मानक पुनरावृत्ति का उपयोग कर सकते हैं

\ bar {h} (n, d) = \ binom {n + d} {d} = \ binom {n + d-1} {d-1} + \ binom {n + d-1} {d} = एच (एन, डी) + \ बार {एच} (एन -1, डी)

अधिकांश n पर अंक-योग के साथ विभाजन की संख्या को गिना जाता है। और इस f से g के समान ही तर्कों का उपयोग करके व्युत्पन्न किया जा सकता है।

इस सूत्र का उपयोग करते हुए, हम उदाहरण के लिए 8000 से 8999 के अंतराल में भारी संख्या की संख्या का पता लगा सकते हैं 1000 - f(3, 20), क्योंकि इस अंतराल में हजार संख्याएँ हैं, और हमें अंकों की संख्या को अंकों के योग से घटाकर 28 के बराबर या उससे कम करना होगा। एकांत में ले जाते समय कि पहला अंक पहले से 8 अंक के योग में योगदान देता है।

अधिक जटिल उदाहरण के रूप में आइए अंतराल 1234..5678 में भारी संख्या की संख्या को देखें। हम सबसे पहले १२ चरणों में १२३४ से १२४० तक जा सकते हैं। फिर हम १०४० के चरणों में १२४० से १३०० तक जाते हैं। उपरोक्त सूत्र हमें प्रत्येक ऐसे अंतराल में भारी संख्या की संख्या देता है:

1240..1249:  10 - f(1, 28 - (1+2+4))
1250..1259:  10 - f(1, 28 - (1+2+5))
1260..1269:  10 - f(1, 28 - (1+2+6))
1270..1279:  10 - f(1, 28 - (1+2+7))
1280..1289:  10 - f(1, 28 - (1+2+8))
1290..1299:  10 - f(1, 28 - (1+2+9))

अब हम 100 के चरणों में 1300 से 2000 तक जाते हैं:

1300..1399:  100 - f(2, 28 - (1+3))
1400..1499:  100 - f(2, 28 - (1+4))
1500..1599:  100 - f(2, 28 - (1+5))
1600..1699:  100 - f(2, 28 - (1+6))
1700..1799:  100 - f(2, 28 - (1+7))
1800..1899:  100 - f(2, 28 - (1+8))
1900..1999:  100 - f(2, 28 - (1+9))

1000 के चरणों में 2000 से 5000 तक:

2000..2999:  1000 - f(3, 28 - 2)
3000..3999:  1000 - f(3, 28 - 3)
4000..4999:  1000 - f(3, 28 - 4)

अब हमें फिर से स्टेप साइज को कम करना होगा, 100 के चरणों में 5000 से 5600 तक, 10 के चरणों में 5600 से 5670 तक और अंत में 1 के चरणों में 5670 से 5678 तक जाना होगा।

एक उदाहरण पायथन कार्यान्वयन (जो थोड़ी सी अनुकूलन और इस बीच परीक्षण प्राप्त हुआ):

def binomial(n, k):
    if k < 0 or k > n:
        return 0
    result = 1
    for i in range(k):
        result *= n - i
        result //= i + 1
    return result

binomial_lut = [
    [1],
    [1, -1],
    [1, -2, 1],
    [1, -3, 3, -1],
    [1, -4, 6, -4, 1],
    [1, -5, 10, -10, 5, -1],
    [1, -6, 15, -20, 15, -6, 1],
    [1, -7, 21, -35, 35, -21, 7, -1],
    [1, -8, 28, -56, 70, -56, 28, -8, 1],
    [1, -9, 36, -84, 126, -126, 84, -36, 9, -1]]

def f(d, n):
    return sum(binomial_lut[d][i] * binomial(n + d - 10*i, d)
               for i in range(d + 1))

def digits(i):
    d = map(int, str(i))
    d.reverse()
    return d

def heavy(a, b):
    b += 1
    a_digits = digits(a)
    b_digits = digits(b)
    a_digits = a_digits + [0] * (len(b_digits) - len(a_digits))
    max_digits = next(i for i in range(len(a_digits) - 1, -1, -1)
                      if a_digits[i] != b_digits[i])
    a_digits = digits(a)
    count = 0
    digit = 0
    while digit < max_digits:
        while a_digits[digit] == 0:
            digit += 1
        inc = 10 ** digit
        for i in range(10 - a_digits[digit]):
            if a + inc > b:
                break
            count += inc - f(digit, 7 * len(a_digits) - sum(a_digits))
            a += inc
            a_digits = digits(a)
    while a < b:
        while digit and a_digits[digit] == b_digits[digit]:
            digit -= 1
        inc = 10 ** digit
        for i in range(b_digits[digit] - a_digits[digit]):
            count += inc - f(digit, 7 * len(a_digits) - sum(a_digits))
            a += inc
            a_digits = digits(a)
    return count

संपादित करें : कोड को एक अनुकूलित संस्करण द्वारा प्रतिस्थापित किया गया है (जो मूल कोड की तुलना में भी बदसूरत दिखता है)। जब मैं उस पर था तब कुछ कोने के मामले भी तय किए। heavy(1234, 100000000)मेरी मशीन पर लगभग एक मिलीसेकंड लगता है।


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

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

या हम केवल पूर्ववर्ती f (m, n) के साथ लुकअप टेबल का उपयोग कर सकते हैं। यह देखते हुए कि 200,000,000 की सीमा है, मेमोरी का उपयोग न्यूनतम होना चाहिए। (आपके पास मेरा +1 पहले से है)।

@ मॉरन: यह निश्चित रूप से सबसे अच्छा विकल्प लगता है - मैं इसे आज़माऊँगा।
स्वेन मार्नाच

@ मॉरन: मुझे सोर्स कोड में लुकअप टेबल को शामिल करना होगा। आमतौर पर f(d, n)कार्यक्रम के एक रन के दौरान एक ही पैरामीटर के साथ दो बार नहीं कहा जाता है।
स्वेन मार्नाच

5

पुनर्परिवर्तन करें, और क्रमपरिवर्तन का उपयोग करें।

मान लीजिए कि हम एक सामान्य फ़ंक्शन को परिभाषित करते हैं, जो कि x से अधिक भारीपन के साथ a और b के बीच के मूल्यों को पाता है:

heavy_decimal_count(a,b,x)

आपके उदाहरण के = 8675 से b = 8689 के उदाहरण के साथ, पहला अंक 8 है, इसलिए इसे फेंक दें - उत्तर 675 से 689 के समान होगा, और फिर से 75 से 89 तक होगा।

पहले दो अंक 86 का औसत वजन 7 है, इसलिए शेष अंकों को योग्यता प्राप्त करने के लिए 7 से अधिक औसत वजन की आवश्यकता है। इस प्रकार, कॉल

heavy_decimal_count(8675,8689,7)

के बराबर है

heavy_decimal_count(75,89,7)

तो इन संभावनाओं के साथ (नए) पहले अंक के लिए हमारी सीमा 7 से 8 है:

7: 5-9
8: 0-9

7 के लिए, हमें अभी भी 7 से अधिक की औसत चाहिए, जो केवल 8 या 9 के अंतिम अंक से आ सकती है, जिससे हमें 2 संभावित मान मिलेंगे।

8 के लिए, हमें 6 से अधिक की औसत चाहिए, जो केवल 7-9 के अंतिम अंक से आ सकती है, जिससे हमें 3 संभावित मान मिलेंगे।

तो, 2 + 3 पैदावार 5 संभावित मान।

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


2
तो आप भारी (886,887) = भारी (6,7) का दावा कर रहे हैं?

@ मॉरन: नहीं, क्योंकि पहले दो 8s भारीपन के लिए सीमा को बदलते हैं। उदाहरण में, पहले दो 86 थे, जो औसत 7 तक हैं और इस प्रकार सीमा को नहीं बदलते हैं। यदि (8 + 8 + x) / 3> 7, तो x> 5। इतना भारी (886,887,7.0) == भारी (6,7,5.0)।

@ पी एच, मुझे नहीं लगता कि यह विचार काम करता है क्योंकि यह काम करता है: यदि आप 9900 और 9999 लेते हैं, तो यह इसे 0 और 99 के बीच के आकाश में बदल देगा, उदाहरण के लिए 8 को ध्यान में रखते हुए और 9908 एक भारी संख्या नहीं है ( @Aryabhatta)।
हंस रोगमैन

3

हो सकता है कि आप अंतराल में कई उम्मीदवारों को उनके "भारीपन" को जमा करके छोड़ दें।

अगर आपको पता है कि आप की संख्या कितनी है, तो आप जानते हैं कि हर अंक केवल 1 / लंबाई से भारीपन को बदल सकता है।

इसलिए, यदि आप एक नंबर से शुरू करते हैं जो कि भारी नहीं है, तो आपको अगले नंबर की गणना करने में सक्षम होना चाहिए जो भारी होगा, यदि आप उन्हें एक से बढ़ाते हैं।

आपके उदाहरण में ऊपर 8680 avg = 5.5 पर शुरू, जो कि 7-5.5 = 1.5 पॉइंट है जो आपको हेवीनेस बॉर्डर से दूर है, आपको पता होगा कि बीच में 1.5 / (1/4) = 6 नंबर हैं, जो भारी नहीं हैं।

यह चाल के लिए चाहिए!


समान "भारी" संख्याओं की एक पंक्ति के लिए जाता है। आप बस संख्या की गणना कर सकते हैं और उन्हें छोड़ सकते हैं!

1
बस अंकों की संख्या से सब कुछ गुणा करें और आपको उन pesky /lengths से छुटकारा मिल जाएगा ।

1

कैसे एक सरल पुनरावर्ती कार्य के बारे में? चीजों को सरल रखने के लिए, यह digitsअंकों के साथ सभी भारी संख्याओं की गणना करता है , और न्यूनतम अंकों की राशि min_sum

int count_heavy(int digits,int min_sum) {
  if (digits * 9 < min_sum)//impossible (ie, 2 digits and min_sum=19)
    return 0; //this pruning is what makes it fast

  if (min_sum <= 0)
      return pow(10,digits);//any digit will do,
      // (ie, 2 digits gives 10*10 possibilities)

  if (digits == 1)
  //recursion base
    return 10-min_sum;//only the highest digits

  //recursion step
  int count = 0;
  for (i = 0; i <= 9; i++)
  {
     //let the first digit be i, then
     count += count_heavy(digits - 1, min_sum - i);
  }
  return count;
}

count_heavy(9,7*9+1); //average of 7,thus sum is 7*9, the +1 is 'exceeds'.

इसे अजगर में लागू किया गया और इसे ~ 2 सेकंड में सभी 9-अंकीय भारी संख्या मिली। थोड़ा गतिशील प्रोग्रामिंग इसे बेहतर बना सकता है।


0

यह एक संभव उपाय है।

public int heavy_decimal_count(int A, int B)
{
    int count = 0;                       
    for (int i = A; i <= B; i++)
    {
        char[] chrArray = i.ToString().ToCharArray();
        float sum = 0f;
        double average = 0.0f;
        for (int j = 0; j < chrArray.Length; j++)
        {
            sum = sum + (chrArray[j] - '0');                   
        }
        average = sum / chrArray.Length;                
        if (average > 7)
            count++;
    }
    return count;
}

1
कोड गोल्फ में आपका स्वागत है। जब कोई प्रश्न पहले से ही उत्तर दिया जाता है, तो अधिक जवाब का स्वागत किया जाता है यदि वे जीतने वाले मानदंडों में से एक से बेहतर हैं, या वे इसका उत्तर देने के लिए एक नया और दिलचस्प तरीका दिखाते हैं। मैं नहीं देखता कि आपका जवाब कैसा है।
ugoren

0

C, अंतराल के लिए [a, b] यह O (ba) है

c(n,s,j){return n?c(n/10,s+n%10,j+1):s>7*j;}

HeftyDecimalCount(a,b){int r; for(r=0;a<=b;++a)r+=c(a,0,0); return r;}

//कसरत

main()
{
 printf("[9480,9489]=%d\n", HeftyDecimalCount(9480,9489));
 printf("[0,9489000]=%d\n", HeftyDecimalCount(9480,9489000));
 return 0;
}

//परिणाम

//[9480,9489]=2
//[0,9489000]=66575

इसका क्या मतलब है "मानक कमियां"?
रोजलूपी

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