एक मिलियन नंबरों की एक स्ट्रिंग को देखते हुए, सभी 3 अंकों की संख्या को दोहराते हुए लौटें


137

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

मैं पहले इंटरव्यू की समस्या से काफी परेशान था ...

प्रश्न: एक मिलियन संख्याओं (उदाहरण के लिए पाई) की एक स्ट्रिंग को देखते हुए, एक फ़ंक्शन / प्रोग्राम लिखें जो सभी 3 अंकों की संख्या और दोहराव की संख्या को 1 से अधिक दोहराता है।

उदाहरण के लिए: यदि स्ट्रिंग थी: 123412345123456तो फ़ंक्शन / प्रोग्राम वापस आ जाएगा:

123 - 3 times
234 - 3 times
345 - 2 times

साक्षात्कार में असफल होने के बाद उन्होंने मुझे समाधान नहीं दिया, लेकिन उन्होंने मुझे बताया कि समाधान के लिए समय की जटिलता 1000 की निरंतरता थी क्योंकि सभी संभावित परिणाम इस प्रकार हैं:

000 -> 999

अब जब मैं इसके बारे में सोच रहा हूं, मुझे नहीं लगता कि लगातार समय एल्गोरिथ्म के साथ आना संभव है। क्या यह?


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

54
यदि इनपुट आकार एक स्थिर है, तो हर बार, एल्गोरिथ्म निरंतर होता है
;;

34
1000 की निरंतरता क्या ? (अतिरिक्त? हाथी?)
ilkachachu

31
ठीक है, अगर स्ट्रिंग की लंबाई स्थिर (1M) है और सबस्ट्रिंग / संख्या की लंबाई स्थिर (3) है, तो तकनीकी रूप से हर समाधान निरंतर समय है ...
केविन

8
They did not give me the solution after I failed the interview, but they did tell me that the time complexity for the solution was constant of 1000 since all the possible outcomes are between: 000 --> 999 यह वास्तविक परीक्षण की संभावना थी। यह देखने के लिए कि क्या आप उन्हें साबित कर सकते हैं कि यह क्यों संभव नहीं है और उन्हें सही न्यूनतम समय जटिलता दिखाने के लिए।
जेम्स

जवाबों:


168

आप हल्के से बंद हो गए, आप शायद हेज फंड के लिए काम नहीं करना चाहते हैं जहां क्वेंट बुनियादी एल्गोरिदम को नहीं समझते हैं :-)

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

हालांकि, एक अलग रूप में के रूप में, एक मामूली O(n)एल्गोरिथ्म होगा हो O(1)तो, तकनीकी रूप से एक निश्चित इनपुट आकार के लिए, वे यहाँ सही हो सकता है। हालांकि, यह आमतौर पर नहीं है कि लोग जटिलता विश्लेषण का उपयोग कैसे करते हैं।

यह मुझे प्रतीत होता है कि आप उन्हें कई तरीकों से प्रभावित कर सकते थे।

सबसे पहले, उन्हें सूचित करके कि ऐसा करना संभव नहीं है O(1), जब तक कि आप ऊपर दिए गए "संदिग्ध" तर्क का उपयोग न करें।

दूसरा, पाइथोनिक कोड प्रदान करके अपने कुलीन कौशल दिखा रहा है जैसे:

inpStr = '123412345123456'

# O(1) array creation.
freq = [0] * 1000

# O(n) string processing.
for val in [int(inpStr[pos:pos+3]) for pos in range(len(inpStr) - 2)]:
    freq[val] += 1

# O(1) output of relevant array values.
print ([(num, freq[num]) for num in range(1000) if freq[num] > 1])

यह आउटपुट:

[(123, 3), (234, 3), (345, 2)]

हालाँकि, आप अपनी इच्छा के अनुसार आउटपुट स्वरूप को संशोधित कर सकते हैं।

और, अंत में, उन्हें यह बताकर कि लगभग निश्चित रूप से एक समाधान के साथ कोई समस्या नहीं है O(n), क्योंकि ऊपर दिए गए कोड एक सेकंड में एक मिलियन-अंक स्ट्रिंग के लिए परिणाम देते हैं। यह काफी रैखिक रूप में अच्छी तरह से पैमाने पर लगता है, क्योंकि एक 10,000,000-वर्ण स्ट्रिंग को 3.5 सेकंड और 100,000,000-वर्ण वाले को 36 सेकंड लगते हैं।

और, अगर उन्हें इससे बेहतर की जरूरत है , तो इस तरह के सामान को समानांतर करने के तरीके हैं जो इसे गति प्रदान कर सकते हैं।

जीआईएल के कारण निश्चित रूप से एक भी अजगर दुभाषिया के भीतर नहीं है , लेकिन आप स्ट्रिंग को कुछ इस तरह विभाजित कर सकते हैं (द्वारा इंगित ओवरलैप vvको सीमा क्षेत्रों के उचित प्रसंस्करण की अनुमति देना आवश्यक है):

    vv
123412  vv
    123451
        5123456

आप इन श्रमिकों को अलग करने के लिए खेती कर सकते हैं और बाद में परिणामों को जोड़ सकते हैं।

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


यह मंत्र अन्य संभावनाओं पर भी लागू होता है , जैसे कि पायथन को पूरी तरह से दरकिनार करना और एक अलग भाषा का उपयोग करना जो तेज हो सकता है।

उदाहरण के लिए, निम्न सी कोड, जो पहले के पायथन कोड के समान हार्डवेयर पर चल रहा है, 0.6 सेकंड में सौ मिलियन अंकों को संभालता है , लगभग उसी समय जैसे पायथन कोड ने एक मिलियन को संसाधित किया । दूसरे शब्दों में, बहुत तेजी से:

#include <stdio.h>
#include <string.h>

int main(void) {
    static char inpStr[100000000+1];
    static int freq[1000];

    // Set up test data.

    memset(inpStr, '1', sizeof(inpStr));
    inpStr[sizeof(inpStr)-1] = '\0';

    // Need at least three digits to do anything useful.

    if (strlen(inpStr) <= 2) return 0;

    // Get initial feed from first two digits, process others.

    int val = (inpStr[0] - '0') * 10 + inpStr[1] - '0';
    char *inpPtr = &(inpStr[2]);
    while (*inpPtr != '\0') {
        // Remove hundreds, add next digit as units, adjust table.

        val = (val % 100) * 10 + *inpPtr++ - '0';
        freq[val]++;
    }

    // Output (relevant part of) table.

    for (int i = 0; i < 1000; ++i)
        if (freq[i] > 1)
            printf("%3d -> %d\n", i, freq[i]);

    return 0;
}

19
यह "निश्चित इनपुट आकार" वास्तव में साक्षात्कारकर्ता या साक्षात्कारकर्ता को एक बुरा मजाक लगता है। हर एल्गोरिथ्म तय हो जाता O(1)है nया बंध जाता है ।
एरिक ड्यूमिनिल

5
यदि उन्हें इससे बेहतर की जरूरत है, तो शायद उन्हें पायथन का उपयोग नहीं करना चाहिए, कम से कम विशिष्ट एल्गोरिथ्म के लिए।
सेबेस्टियन रेडल

3
@ezzzCash क्योंकि एक समानांतर दृष्टिकोण की कोशिश करते समय उन बिंदुओं पर ओवरलैप हो सकता है जहां स्ट्रिंग को "ब्रेक अप" किया जा रहा है। चूंकि आप 3-अंकीय समूहों की तलाश कर रहे हैं, -2 संभावित समानांतर मिलान को याद करने के लिए दोनों समानांतर समूहों की जांच की अनुमति देता है ।
code_dredd

5
@ezzzCash यह समानांतर प्रोग्रामिंग ज्ञान की कमी नहीं है। लंबाई की एक स्ट्रिंग पर विचार करें N। यदि आप इसे दो भागों में तोड़ते हैं N/2, तो आपको अभी भी इस तथ्य पर ध्यान देने की आवश्यकता है कि आप "बॉर्डर" पर, और के अंत में एक वैध 3-अंक वाला मैच मिस कर सकते string1हैं string2। इस प्रकार, आप के बीच मैचों जांच करने की आवश्यकता string1[N/2-2]है और string2[2], आदि विचार है कि (एक शून्य आधारित सूचकांक का उपयोग)।
code_dredd

1
अधिक अंकों वाले अनुक्रमों के साथ, स्लाइडिंग विंडो के साथ पूर्णांक में रूपांतरण को अनुकूलित करने से कुछ हासिल करना होगा जिससे आप उच्चतम अंक छोड़ सकते हैं और एक नया अंक जोड़ सकते हैं। (पायथन ओवरहेड शायद इसे मार देगा, इसलिए यह केवल सी या अन्य निम्न-स्तरीय कार्यान्वयन पर लागू होगा)। val -= 100 * (d[i]-'0');अग्रणी अंक को छोड़ने के लिए। val = 10*val + d[i+2]-'0'एक नया न्यूनतम महत्वपूर्ण अंक जमा करने के लिए (सामान्य स्ट्रिंग-> पूर्णांक पार्सिंग)। val % 100संभवतः गैर-भयानक है, लेकिन केवल अगर 100एक संकलन-समय स्थिर है तो यह एक वास्तविक एचडब्ल्यू डिवाइड का उपयोग नहीं करता है।
पीटर कॉर्ड्स

78

लगातार समय संभव नहीं है। सभी 1 मिलियन अंकों को कम से कम एक बार देखने की जरूरत है, इसलिए यह O (n) की समय जटिलता है, जहां इस मामले में n = 1 मिलियन है।

एक सरल ओ (एन) समाधान के लिए, आकार 1000 की एक सरणी बनाएं जो प्रत्येक संभावित 3 अंकों की संख्या की घटनाओं का प्रतिनिधित्व करता है। एक समय में 1 अंक अग्रिम, पहला सूचकांक == 0, अंतिम सूचकांक == 999997, और वेतन वृद्धि सरणी [3 अंक संख्या] एक हिस्टोग्राम बनाने के लिए (प्रत्येक संभावित 3 अंक संख्या के लिए घटनाओं की गिनती)। फिर काउंट> 1 के साथ एरे के कंटेंट को आउटपुट करें।


26
@ezzzCash - हाँ एक शब्दकोश काम करेगा, लेकिन इसकी ज़रूरत नहीं है। सभी संभव "चाबियाँ" अग्रिम में जानी जाती हैं, 0 से 999 तक सीमित हैं। ओवरहेड में अंतर वह समय होगा जिसमें कुंजी के रूप में 3 वर्ण स्ट्रिंग्स का उपयोग करते हुए एक कुंजी आधारित पहुंच करना होता है, बनाम समय जो 3 को परिवर्तित करने में लेता है। एक इंडेक्स के लिए स्ट्रिंग स्ट्रिंग और फिर एरे का उपयोग करने के लिए इंडेक्स का उपयोग करना।
rcgldr

4
यदि आप संख्यात्मक चालें चाहते हैं, तो आप BCD पर जाने और तीन अंकों को 12 बिट्स में संग्रहीत करने का निर्णय भी ले सकते हैं। और कम 4 बिट्स को मास्क करके ASCII अंक को डिकोड करें। लेकिन यह x-'0'पैटर्न पाइथन में मान्य नहीं है, यह एक सी-आइसम (जहां अक्षर पूर्णांक हैं)।
यन वर्नियर

5
@ लोरेनपेकटेल: पायथन में डिक्शनरी लुक्स वास्तव में तेज़ हैं। दी गई, सरणी पहुंच और भी तेज़ है, इसलिए यदि हम शुरुआत से पूर्णांकों के साथ काम कर रहे थे, तो आप सही होंगे। हालाँकि, इस मामले में, हमारे पास 3-लेंथ स्ट्रिंग्स हैं, जिन्हें हमें पहले पूर्णांक में बदलना होगा, यदि हम उन्हें सरणियों के साथ उपयोग करना चाहते हैं। यह पता चला है कि जो पहले की उम्मीद कर सकता है, उसके विपरीत, शब्दकोश लुकअप वास्तव में पूर्णांक रूपांतरण + सरणी पहुंच से अधिक तेज है। इस मामले में सरणी समाधान वास्तव में 50% धीमा है।
एलेक्सी तोरामो

2
मुझे लगता है कि कोई यह तर्क दे सकता है कि यदि इनपुट संख्या में हमेशा 1 मिलियन अंक हैं, तो उस एल्गोरिथ्म की तुलना में O (1) है, जिसमें 1 मिलियन का एक स्थिर कारक है।
टोबियास_एक

2
@AleksiTorhamo - यदि लक्ष्य एक एल्गोरिथम के लिए कार्यान्वयन की सापेक्ष गति की तुलना करना है, तो मैं C या C ++ जैसी पारंपरिक भाषा को पसंद करूंगा, क्योंकि पायथन काफी धीमा है और लगता है कि अन्य भाषाओं की तुलना में पायथन के लिए अद्वितीय है।
rcgldr

14

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

from collections import Counter

def triple_counter(s):
    c = Counter(s[n-3: n] for n in range(3, len(s)))
    for tri, n in c.most_common():
        if n > 1:
            print('%s - %i times.' % (tri, n))
        else:
            break

if __name__ == '__main__':
    import random

    s = ''.join(random.choice('0123456789') for _ in range(1_000_000))
    triple_counter(s)

उम्मीद है कि साक्षात्कारकर्ता मानक पुस्तकालयों के संग्रह के उपयोग की तलाश में होंगे। मुठभेड़ वर्ग।

समानांतर निष्पादन संस्करण

मैंने इस पर अधिक स्पष्टीकरण के साथ एक ब्लॉग पोस्ट लिखा ।


यह ठीक काम करता है और सबसे तेज, गैर-खस्ता समाधान लगता है।
एरिक ड्यूमिनिल

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

1
हम 100% सहमत हैं। हालांकि मुझे यकीन नहीं है कि कोई भी उत्तर बिल्कुल प्रासंगिक है अगर साक्षात्कारकर्ता वास्तव में सोचता है कि यह करना संभव है O(1)
एरिक ड्यूमिनिल

1
साक्षात्कारकर्ता पर बल दिया कि यह समय महत्वपूर्ण था, तो, तो बाद इस सीमा है पुष्टि करने के लिए रूपरेखा, यह एक सी मॉड्यूल इस अड़चन को संबोधित करने के लिखने के लिए समय हो सकता है। मेरे पास एक स्क्रिप्ट है जिसमें हमने एसी मॉड्यूल का उपयोग करने के लिए स्विच करने के बाद अजगर कोड पर 84x सुधार देखा।
टेम्पोरलवुल्फ

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

13

सरल ओ (एन) समाधान प्रत्येक 3-अंकीय संख्या की गणना करना होगा:

for nr in range(1000):
    cnt = text.count('%03d' % nr)
    if cnt > 1:
        print '%03d is found %d times' % (nr, cnt)

यह 1000 बार सभी 1 मिलियन अंकों के माध्यम से खोज करेगा।

केवल एक बार अंकों का पता लगाना:

counts = [0] * 1000
for idx in range(len(text)-2):
    counts[int(text[idx:idx+3])] += 1

for nr, cnt in enumerate(counts):
    if cnt > 1:
        print '%03d is found %d times' % (nr, cnt)

टाइमिंग से पता चलता है कि सूचकांक पर केवल एक बार पुनरावृत्ति करना उपयोग करने के मुकाबले दोगुना है count


37
क्या ब्लैक फ्राइडे पर छूट है text.count()?
एरिक डुमिनील

3
@EricDuminil आपके पास एक अच्छा बिंदु है लेकिन, चूंकि text.countएक उच्च गति संकलित भाषा (जैसे C) में किया जाता है, धीमी गति से अजगर स्तर की व्याख्या की गई लूपिंग के विपरीत, हाँ इसमें छूट है।
जॉन 1024 20

प्रत्येक संख्या को अलग से गिनना बहुत अक्षम है, लेकिन यह निरंतर समय है, इस प्रकार अभी भी ओ (एन) है।
लोरेन Pechtel

11
आपके द्वारा प्रस्तावित विकल्प countगलत है, क्योंकि यह ओवरलैपिंग पैटर्न की गणना नहीं करेगा। ध्यान दें कि '111'.count('11') == 1जब हम यह होने की उम्मीद करेंगे 2
साइरो

2
इसके अलावा, अपने "सरल O(n)समाधान" वास्तव में है O(10**d * n)के साथ dकी खोज अंकों की संख्या और nस्ट्रिंग की कुल लंबाई। दूसरा O(n)समय और O(10**d + n)स्थान है।
एरिक ड्यूमिनिल

10

यहाँ "सर्वसम्मति" O (n) एल्गोरिथ्म का एक NumPy कार्यान्वयन है: जैसे ही आप जाते हैं सभी ट्रिपल और बिन के माध्यम से चलना। बिनिंग को "385" कहने पर, एक बिन [3, 8, 5] जो एक O (1) ऑपरेशन है, को जोड़कर किया जाता है। डिब्बे एक 10x10x10घन में व्यवस्थित होते हैं । चूंकि बिनिंग पूरी तरह से वेक्टरीकृत है इसलिए कोड में कोई लूप नहीं है।

def setup_data(n):
    import random
    digits = "0123456789"
    return dict(text = ''.join(random.choice(digits) for i in range(n)))

def f_np(text):
    # Get the data into NumPy
    import numpy as np
    a = np.frombuffer(bytes(text, 'utf8'), dtype=np.uint8) - ord('0')
    # Rolling triplets
    a3 = np.lib.stride_tricks.as_strided(a, (3, a.size-2), 2*a.strides)

    bins = np.zeros((10, 10, 10), dtype=int)
    # Next line performs O(n) binning
    np.add.at(bins, tuple(a3), 1)
    # Filtering is left as an exercise
    return bins.ravel()

def f_py(text):
    counts = [0] * 1000
    for idx in range(len(text)-2):
        counts[int(text[idx:idx+3])] += 1
    return counts

import numpy as np
import types
from timeit import timeit
for n in (10, 1000, 1000000):
    data = setup_data(n)
    ref = f_np(**data)
    print(f'n = {n}')
    for name, func in list(globals().items()):
        if not name.startswith('f_') or not isinstance(func, types.FunctionType):
            continue
        try:
            assert np.all(ref == func(**data))
            print("{:16s}{:16.8f} ms".format(name[2:], timeit(
                'f(**data)', globals={'f':func, 'data':data}, number=10)*100))
        except:
            print("{:16s} apparently crashed".format(name[2:]))

अप्रत्याशित रूप से, बड़े डेटा सेट पर @ डैनियल के शुद्ध पायथन समाधान की तुलना में NumPy थोड़ा तेज है। नमूना उत्पादन:

# n = 10
# np                    0.03481400 ms
# py                    0.00669330 ms
# n = 1000
# np                    0.11215360 ms
# py                    0.34836530 ms
# n = 1000000
# np                   82.46765980 ms
# py                  360.51235450 ms

संभवत: नेस्टेड डिब्बे होने के बजाय अंक स्ट्रिंग को समतल करने के लिए काफी तेज है, जब तक कि NumPy इसे कुशल अनुक्रमण के साथ 3 डी मैट्रिक्स के रूप में लागू नहीं करता है। @ डैनियल के किस संस्करण के खिलाफ आपने समय दिया; वह जो प्रत्येक पूर्णांक के लिए एक स्ट्रिंग खोज चलाता है, या एक हिस्टोग्राम के साथ?
पीटर कॉर्ड्स

2
@PeterCordes मुझे संदेह है। ndarrayएस, मुख्य सुन्न प्रकार, संख्याओं के बहुआयामी सरणियों के कुशल भंडारण, हेरफेर और अनुक्रमण के बारे में हैं। कभी-कभी आप चपटा करके कुछ% तक शेव कर सकते हैं, लेकिन इस मामले में 100 x [0] + 10 x [1] + x [2] हाथ से करने से आपको बहुत फायदा नहीं होगा। मैंने इस्तेमाल किया @ डैनियल ने कहा कि तेज था, आप स्वयं बेंचमार्क कोड की जांच कर सकते हैं।
पॉल पैंजर

मैं वास्तव में NumPy (या सामान्य रूप से पायथन को नहीं जानता; ज्यादातर मैं C86 और x86 के लिए असेंबली परफॉर्मेंस ट्यूनिंग करता हूं), लेकिन मुझे लगता है कि आपके पास एक एकल 3D सरणी है, है ना? मैं आपके अंग्रेजी पाठ से सोच रहा था (जो स्पष्ट रूप से मैंने भी ध्यान से नहीं पढ़ा था) कि आपके पास वास्तविक नेस्टेड पायथन ऑब्जेक्ट्स थे और उन्हें अलग-अलग अनुक्रमित कर रहे थे। लेकिन यह मामला नहीं है, इसलिए मेरी पहली टिप्पणी nvm।
पीटर कॉर्ड्स

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

3

मैं समस्या को इस प्रकार हल करूंगा:

def find_numbers(str_num):
    final_dict = {}
    buffer = {}
    for idx in range(len(str_num) - 3):
        num = int(str_num[idx:idx + 3])
        if num not in buffer:
            buffer[num] = 0
        buffer[num] += 1
        if buffer[num] > 1:
            final_dict[num] = buffer[num]
    return final_dict

आपके उदाहरण स्ट्रिंग के लिए लागू, यह पैदावार:

>>> find_numbers("123412345123456")
{345: 2, 234: 3, 123: 3}

यह समाधान प्रदान की गई स्ट्रिंग की लंबाई n होने के लिए O (n) में चलता है, और मुझे लगता है, सबसे अच्छा आप प्राप्त कर सकते हैं।


आप बस एक का उपयोग कर सकते हैं Counter। आपको इसकी आवश्यकता नहीं है final_dict, और आपको इसे प्रत्येक पुनरावृत्ति पर अद्यतन करने की आवश्यकता नहीं है।
एरिक डुमिनील

2

मेरी समझ के अनुसार, आपके पास निरंतर समय में समाधान नहीं हो सकता है। यह मिलियन अंकों की संख्या (इसकी स्ट्रिंग मानकर) पर कम से कम एक पास ले जाएगा। आपके पास मिलियन लंबाई की संख्या के अंकों पर 3 अंकों की रोलिंग पुनरावृत्ति हो सकती है और हैश कुंजी के मान को 1 से बढ़ा सकती है यदि यह पहले से मौजूद है या एक नई हैश कुंजी (मूल्य 1 द्वारा आरंभीकृत) बना है यदि यह पहले से मौजूद नहीं है शब्दकोष।

कोड कुछ इस तरह दिखेगा:

def calc_repeating_digits(number):

    hash = {}

    for i in range(len(str(number))-2):

        current_three_digits = number[i:i+3]
        if current_three_digits in hash.keys():
            hash[current_three_digits] += 1

        else:
            hash[current_three_digits] = 1

    return hash

आप उन कुंजियों को फ़िल्टर कर सकते हैं जिनका आइटम मूल्य 1 से अधिक है।


2

जैसा कि एक अन्य उत्तर में उल्लेख किया गया है, आप इस एल्गोरिथ्म को निरंतर समय में नहीं कर सकते, क्योंकि आपको कम से कम n अंकों को देखना होगा। रैखिक समय सबसे तेज़ है जो आप प्राप्त कर सकते हैं।

हालाँकि, एल्गोरिथ्म को O (1) स्पेस में किया जा सकता है । आपको केवल प्रत्येक 3 अंकों की संख्या को गिनने की आवश्यकता है, इसलिए आपको 1000 प्रविष्टियों की एक सरणी की आवश्यकता है। फिर आप संख्या को स्ट्रीम कर सकते हैं।

मेरा अनुमान है कि या तो साक्षात्कारकर्ता को याद आती है जब उन्होंने आपको समाधान दिया था, या आपने "निरंतर समय" कहा था जब उन्होंने "निरंतर स्थान" कहा था।


जैसा कि अन्य ने बताया है, हिस्टोग्राम दृष्टिकोण O(10**d)अतिरिक्त स्थान है, जहां dआप जिस दशमलव अंकों की तलाश कर रहे हैं , वह संख्या है।
पीटर कॉर्ड्स

1
शब्दकोश दृष्टिकोण n अंकों के लिए O (न्यूनतम (10 ^ d, n)) होगा। उदाहरण के लिए यदि आपके पास n = 10 ^ 9 अंक हैं और दुर्लभ 15 अंकों के अनुक्रम को खोजना चाहते हैं जो एक से अधिक बार होते हैं।
gnasher729 14

1

यहाँ मेरा जवाब है:

from timeit import timeit
from collections import Counter
import types
import random

def setup_data(n):
    digits = "0123456789"
    return dict(text = ''.join(random.choice(digits) for i in range(n)))


def f_counter(text):
    c = Counter()
    for i in range(len(text)-2):
        ss = text[i:i+3]
        c.update([ss])
    return (i for i in c.items() if i[1] > 1)

def f_dict(text):
    d = {}
    for i in range(len(text)-2):
        ss = text[i:i+3]
        if ss not in d:
            d[ss] = 0
        d[ss] += 1
    return ((i, d[i]) for i in d if d[i] > 1)

def f_array(text):
    a = [[[0 for _ in range(10)] for _ in range(10)] for _ in range(10)]
    for n in range(len(text)-2):
        i, j, k = (int(ss) for ss in text[n:n+3])
        a[i][j][k] += 1
    for i, b in enumerate(a):
        for j, c in enumerate(b):
            for k, d in enumerate(c):
                if d > 1: yield (f'{i}{j}{k}', d)


for n in (1E1, 1E3, 1E6):
    n = int(n)
    data = setup_data(n)
    print(f'n = {n}')
    results = {}
    for name, func in list(globals().items()):
        if not name.startswith('f_') or not isinstance(func, types.FunctionType):
            continue
        print("{:16s}{:16.8f} ms".format(name[2:], timeit(
            'results[name] = f(**data)', globals={'f':func, 'data':data, 'results':results, 'name':name}, number=10)*100))
    for r in results:
        print('{:10}: {}'.format(r, sorted(list(results[r]))[:5]))

सरणी लुकअप विधि बहुत तेज़ है (@ paul-panzer's numpy विधि से भी तेज़!)। बेशक, यह धोखा देता है क्योंकि यह तकनीकी रूप से पूरा होने के बाद समाप्त नहीं होता है, क्योंकि यह एक जनरेटर वापस कर रहा है। यदि मूल्य पहले से मौजूद है, तो भी इसे हर पुनरावृत्ति की जांच करने की आवश्यकता नहीं है, जो बहुत मदद करने की संभावना है।

n = 10
counter               0.10595780 ms
dict                  0.01070654 ms
array                 0.00135370 ms
f_counter : []
f_dict    : []
f_array   : []
n = 1000
counter               2.89462101 ms
dict                  0.40434612 ms
array                 0.00073838 ms
f_counter : [('008', 2), ('009', 3), ('010', 2), ('016', 2), ('017', 2)]
f_dict    : [('008', 2), ('009', 3), ('010', 2), ('016', 2), ('017', 2)]
f_array   : [('008', 2), ('009', 3), ('010', 2), ('016', 2), ('017', 2)]
n = 1000000
counter            2849.00500992 ms
dict                438.44007806 ms
array                 0.00135370 ms
f_counter : [('000', 1058), ('001', 943), ('002', 1030), ('003', 982), ('004', 1042)]
f_dict    : [('000', 1058), ('001', 943), ('002', 1030), ('003', 982), ('004', 1042)]
f_array   : [('000', 1058), ('001', 943), ('002', 1030), ('003', 982), ('004', 1042)]

1
तो आप वास्तव में क्या तुलना कर रहे हैं? क्या आपको अप्रयुक्त जनरेटर के बजाय सूची वापस नहीं करनी चाहिए?
एरिक डुमिनील

Countersउस तरह से इस्तेमाल नहीं कर रहे हैं। ठीक से उपयोग किया जाता है, वे आपके उदाहरण के साथ सबसे तेज़ विकल्प बन जाते हैं। यदि आप timeitकिसी जनरेटर से संबंधित सूची के साथ उपयोग करते हैं, तो आपकी विधि Counterया की तुलना में धीमी हो जाती है dict। देखें यहाँ
एरिक डुमिनील

अंत में, आपका f_arrayतेज हो सकता है यदि आप पहले हर चार को एक इंट में परिवर्तित करते हैं: ints = [int(c) for c in text]और फिर उपयोग करें i, j, k = ints[n:n+3]
एरिक डुमिनील

1

उत्तर के रूप में छवि:

छवि के रूप में छवि

स्लाइडिंग विंडो की तरह दिखता है।


1

यहाँ मेरा समाधान है:

from collections import defaultdict
string = "103264685134845354863"
d = defaultdict(int)
for elt in range(len(string)-2):
    d[string[elt:elt+3]] += 1
d = {key: d[key] for key in d.keys() if d[key] > 1}

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


देखें pho7 का जवाब । और टिप्पणियाँ। यह जानने की कोशिश करें कि इसे वोटों का भार क्यों नहीं मिला।
ग्रेबर्ड

0

सी। के परिप्रेक्ष्य से -आप एक अंतर 3-डी सरणी परिणाम हो सकता है [10] [10] [10]; - 0 वें स्थान से n-4th स्थान तक, जहां n स्ट्रिंग स्ट्रिंग का आकार है। प्रत्येक स्थान पर, वर्तमान, अगले और अगले के अगले की जांच करें। -कंट्रेंट को cutr को resutls के रूप में बढ़ाना [वर्तमान] [अगला] [अगले का अगला] ++; के मानों की जाँच करें

results[1][2][3]
results[2][3][4]
results[3][4][5]
results[4][5][6]
results[5][6][7]
results[6][7][8]
results[7][8][9]

-यह O (n) समय है, इसमें कोई तुलना शामिल नहीं है। -आप ऐरे को पार्टीशन करके और पार्टीशन के आस-पास के मैच की गणना करके यहां कुछ समानांतर सामान चला सकते हैं।


-1
inputStr = '123456123138276237284287434628736482376487234682734682736487263482736487236482634'

count = {}
for i in range(len(inputStr) - 2):
    subNum = int(inputStr[i:i+3])
    if subNum not in count:
        count[subNum] = 1
    else:
        count[subNum] += 1

print count

आपके उत्तर के लिए धन्यवाद, लेकिन यह भी एक एल्गोरिथ्म के समान है जैसा कि 5-6 दिन पहले @abhishek arora द्वारा दिया गया था। इसके अलावा मूल प्रश्न एल्गोरिथ्म के लिए नहीं पूछ रहा था, बल्कि एक अलग प्रश्न (जो पहले से ही कई बार उत्तर दिया गया था)
its.david
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.