Milyon sayıdan oluşan bir dize verildiğinde, yinelenen tüm 3 basamaklı sayıları döndürün


137

Birkaç ay önce New York'ta bir hedge fon şirketi ile röportaj yaptım ve maalesef veri / yazılım mühendisi olarak staj teklifi alamadım. (Ayrıca çözümün Python'da olmasını istediler.)

İlk röportaj sorununa çok sıktım ...

Soru: Milyon sayıdan oluşan bir dize verildiğinde (örneğin Pi), yinelenen tüm 3 basamaklı sayıları ve 1'den büyük tekrar sayısını döndüren bir işlev / program yazın

Örneğin: dize:: 123412345123456işlev / program şunu döndürür:

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

Röportajda başarısız olduktan sonra bana çözüm vermediler, ancak olası tüm sonuçlar arasında olduğu için çözümün zaman karmaşıklığının 1000 sabit olduğunu söylediler:

000 -> 999

Şimdi bunu düşündüğüme göre, sabit bir zaman algoritması bulmak mümkün değil. Bu mu?


68
Çözümün 1000 sabiti olduğunu düşünürlerse, bu bana üç basamaklı sayıların hepsini inşa edeceklerini düşündürür ve sonra onları regex aradı. İnsanların aslında yazma / görmedikleri işlemlerin "ücretsiz" olduğunu düşünmesi çok yaygındır. Bu dizenin uzunluğu için doğrusal olacağından eminim.
mypetlion

54
Nitpickingly, eğer giriş boyutu sabitse, her algoritma sabit süredir ;-)
Paŭlo Ebermann

34
1000 sabiti ne ? (eklemeler? filler?)
ilkkachu

31
Eğer dize uzunluğu sabitse (1M) ve alt dize / sayı uzunluğu sabitse (3), o zaman teknik olarak her çözüm sabit süredir…
Kevin

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 Muhtemelen asıl test buydu. Bunun neden mümkün olmadığını kanıtlayıp kanıtlayamayacağınızı görmek ve onlara doğru minimum zaman karmaşıklığını göstermek.
James

Yanıtlar:


168

Sen muhtemelen, hafifçe indi yok quantlar temel algoritmalar anlamıyorum hedge fonu için çalışan olmak istiyorum :-)

Orada hiçbir bir keyfi boyutlu veri yapısını işlemek için bir yol O(1)bu durumda olduğu gibi, bir kez en az her eleman ziyaret gerekir, eğer. En iyi için umut olabilir olduğunu O(n)bu durumda, içinde ndize uzunluğudur.

Bir kenara olarak, nominal rağmen O(n)algoritma olacak olması O(1)bu yüzden, teknik olarak sabit bir giriş boyutu için, burada doğru olmuş olabilir. Ancak, genellikle karmaşıklık analizini bu şekilde kullanmazlar.

Bana öyle geliyor ki onları çeşitli şekillerde etkilemiş olabilirsiniz.

İlk olarak, yukarıda verilen "şüpheli" muhakemeyi kullanmadığınız sürece, bunu yapmanın mümkün olmadığını bildirerek O(1).

İkincisi, elit becerilerinizi aşağıdaki gibi Pythonic kodu sağlayarak göstererek:

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])

Bu çıktılar:

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

tabii ki çıktı formatını istediğiniz gibi değiştirebilirsiniz.

Ve son olarak, onlara bir çözümle neredeyse hiç sorun olmadığını söyleyerek O(n), yukarıdaki kod yarım saniyenin altında bir milyon basamaklı bir dize için sonuçlar verir. 10.000.000 karakterlik bir dize 3.5 saniye ve 100.000.000 karakterlik bir 36 saniye sürdüğü için oldukça doğrusal olarak ölçeklendirilmiş gibi görünüyor.

Ve bundan daha iyisine ihtiyaç duyarlarsa , bu tür şeyleri büyük ölçüde hızlandıracak paralel hale getirmenin yolları vardır.

Tabii ki, GIL nedeniyle tek bir Python yorumlayıcısı içinde değil , ancak dizeyi benzer bir şeye bölebilirsiniz ( vvsınır alanlarının düzgün işlenmesine izin vermek için belirtilen örtüşme gerekir):

    vv
123412  vv
    123451
        5123456

Bunları işçileri ayırmak ve daha sonra sonuçları birleştirmek için toplayabilirsiniz.

Girdinin bölünmesi ve çıktının birleştirilmesi muhtemelen küçük dizelerle (ve hatta milyon haneli dizelerle) herhangi bir tasarrufu azaltacaktır, ancak çok daha büyük veri kümeleri için fark yaratabilir. Burada her zamanki "ölçmek, tahmin etmemek" mantrası geçerlidir.


Bu mantra , Python'u tamamen atlamak ve daha hızlı olabilecek farklı bir dil kullanmak gibi diğer olasılıklar için de geçerlidir .

Örneğin, önceki Python koduyla aynı donanımda çalışan aşağıdaki C kodu, 0,6 saniyede yüz milyon basamağı işlemektedir , yaklaşık olarak Python kodunun bir milyon işlediği süre . Başka bir deyişle, çok daha hızlı:

#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
Bu "sabit girdi boyutu", görüşmecinin veya görüşmecinin alamadığı kötü bir şaka gibi geliyor. Her algoritma haline O(1)edilmektedir nsabit veya sınırlandırılmaktadır.
Eric Duminil

5
Bundan daha iyisine ihtiyaçları varsa, belki de en azından belirli algoritma için Python kullanmamalıdırlar.
Sebastian Redl

3
@ezzzCash Paralel yaklaşımı denerken dizenin "parçalandığı" noktalarda çakışma olabilir. Eğer 3 basamaklı gruplar arıyorsanız yana üzere, -2 hem paralel gruplar üzerinde kontrol verir değil potansiyel olarak geçerli maçı özledim.
code_dredd

5
@ezzzCash Paralel programlama bilgisi eksikliği değil. Bir uzunluk dizesi düşünün N. Eğer pozisyonda iki parçaya ayırırsanız N/2, yine de "sınır" ında, sonunda string1ve başında geçerli 3 basamaklı bir eşleşmeyi kaçırabileceğinizi hesaba katmanız gerekir string2. Bu nedenle, string1[N/2-2]ve string2[2](sıfır tabanlı bir dizin kullanarak), vb. Arasındaki eşleşmeleri kontrol etmeniz gerekir .
code_dredd

1
Daha uzun basamak dizileriyle, en yüksek basamağı bırakmanıza ve yeni bir basamak eklemenize olanak tanıyan bir kayan pencereyle dönüştürmeyi tamsayıya optimize etmekten kazanılacak bir şey olurdu. (Python yükü muhtemelen bunu öldürür, bu yüzden sadece C veya diğer düşük seviyeli uygulamalar için geçerlidir). val -= 100 * (d[i]-'0');baştaki rakamı bırakmak için. val = 10*val + d[i+2]-'0'yeni bir en az anlamlı basamak (normal string-> integer parsing) biriktirmek için. val % 100muhtemelen korkunç değildir, ancak yalnızca 100derleme zamanı sabiti olduğu için gerçek bir HW bölünmesi kullanmaz.
Peter Cordes

78

Sabit zaman mümkün değildir. Tüm 1 milyon haneye en az bir kez bakılması gerekir, bu nedenle bu durumda n = 1 milyon olan O (n) 'nin zaman karmaşıklığıdır.

Basit bir O (n) çözümü için, olası her 3 basamaklı sayının yineleme sayısını temsil eden 1000 büyüklüğünde bir dizi oluşturun. Bir histogram oluşturmak için bir seferde 1 basamak, ilk dizin == 0, son dizin == 999997 ve artış dizisini [3 basamaklı sayı] ilerletin (olası her 3 basamaklı sayı için tekrar sayısı). Daha sonra dizi içeriğini sayılar> 1 ile çıktılayın.


26
@ezzzCash - evet bir sözlük işe yarar ama gerekli değildir. Olası tüm "tuşlar" önceden bilinir, 0 ila 999 aralığıyla sınırlıdır. Tepegözdeki fark, 3 karakter dizesini anahtar olarak kullanarak bir anahtar tabanlı erişim yapmak için geçen süre, bir 3'ü dönüştürmek için gereken süre olacaktır. dizeyi bir dizine girin ve diziye erişmek için dizini kullanın.
rcgldr

4
Sayısal numaralar istiyorsanız, BCD'ye gitmeye ve üç basamağı 12 bitte depolamaya da karar verebilirsiniz. Ve düşük 4 biti maskeleyerek ASCII basamaklarını deşifre edin. Ancak bu x-'0'desen Python'da geçerli değildir, bir C-izmidir (karakterlerin tamsayı olduğu yerlerde).
Yann Vernier

5
@LorenPechtel: Python'daki sözlük aramaları gerçekten hızlı. Verilen diziye erişim daha da hızlıdır, bu yüzden baştan tamsayılarla uğraşsaydık, haklı olacaksınız. Bununla birlikte, bu durumda, dizilerle kullanmak istiyorsak, önce tamsayılara dönüştürmemiz gereken 3 uzunluklu dizelerimiz vardır. Birinin ilk beklediğinin aksine, sözlük aramasının aslında tamsayı dönüşüm + dizi erişiminden daha hızlı olduğu ortaya çıkıyor. Dizi çözümü aslında bu durumda% 50 daha yavaştır.
Aleksi Torhamo

2
Bir iddia olabilir tahmin giriş numarası olup olmadığını bu tam olarak, her zaman bu algoritma, daha 1 milyon basamak olan O (1) 1 milyon sabit bir katsayı ile.
tobias_k

2
@AleksiTorhamo - Amaç bir algoritma için göreli uygulama hızlarını karşılaştırmaksa, Python önemli ölçüde daha yavaş olduğundan ve diğer dillere kıyasla Python'a özgü ek yükleri olduğu için C veya C ++ gibi geleneksel bir dili tercih ederim.
rcgldr

14

Aşağıda verdiğim cevap için bir milyon küçük. Sadece görüşmeyi duraklama olmadan çalıştırabilmenizi bekledikten sonra, aşağıdakiler iki saniyeden daha kısa sürede çalışır ve gerekli sonucu verir:

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)

Umarım görüşmeci standart kütüphaneler koleksiyonlarını kullanmak ister.

Paralel yürütme sürümü

Bunun hakkında daha fazla açıklama içeren bir blog yazısı yazdım .


İyi çalışıyor ve en hızlı, numpy olmayan bir çözüm gibi görünüyor.
Eric Duminil

3
@EricDuminil, verilen çözümlerin çoğu sizi geciktirmediğinde, burada fastet zamanlamaları konusunda endişelenmeniz gerektiğini düşünmüyorum. Python standart kütüphanesini iyi bir şekilde kavradığınızı ve röportaj durumunda korunabilir kod yazabileceğinizi göstermek çok daha iyi. (Görüşmeci zaman kritikliğini vurgulamadığı sürece bundan sonra ne olacağını değerlendirmeden önce gerçek zamanlamaları istemelisiniz).
Paddy3118

1
% 100 katılıyorum. Eğer görüşmeci gerçekten yapmanın mümkün olduğunu düşünüyorsa, herhangi bir cevabın hiç alakalı olmadığından emin değilim O(1).
Eric Duminil

1
Görüşmeci buraya kadar, zaman kritikti vurguladı Eğer sonra bu sınırıdır onaylamak için profilleme, darboğaz gidermek için bir C modülü yazmak için zaman olabilir. Biz ac modülünü kullandıktan sonra python kodu üzerinde 84x gelişme gördüm bir komut dosyası var.
TemporalWolf

Merhaba @TemporalWolf, söylediklerinizi okudum ve daha sonra başka bir, daha hızlı ve ölçeklenebilir bir çözümün paralel bir algoritmaya değiştirmek olabileceğini düşündüm, böylece bir hesaplama çiftliğinde / bulutta birçok işlemde çalıştırılabilir. Dizeyi n bölüme ayırmanız gerekir; her bölümün son 3 karakterini bir sonraki bölümüyle örtüşüyor. Her bölüm daha sonra bağımsız olarak üçlüler için taranabilir, üçlüler toplanabilir ve son bölüm hariç her birinin sonunda üç karakter üçlü üçlü sayılabilir. Ben kodu var ve muhtemelen bir blog yazıya dönecek ...
Paddy3118

13

Basit O (n) çözümü her 3 basamaklı sayıyı saymak olacaktır:

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

Bu, 1 milyon basamağın tamamını 1000 kez arayacaktır.

Rakamları yalnızca bir kez kaydırma:

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)

Zamanlama, dizin üzerinde yalnızca bir kez yinelemenin kullanımdan iki kat daha hızlı olduğunu gösterir count.


37
Üzerinde siyah cuma indirimi var text.count()mı?
Eric Duminil

3
@EricDuminil İyi bir noktanız var, ancak text.countyavaş python düzeyinde yorumlanmış döngünün aksine yüksek hızlı derlenmiş bir dilde (örneğin C) yapıldığı için, bir indirim var.
John1024

Her sayıyı ayrı ayrı saymak çok verimsizdir, ancak sabit bir zamandır, bu yüzden hala O (n).
Loren Pechtel

11
countÇakışan kalıpları saymayacağından, kullanma önerisinde bulunduğunuz seçenek yanlıştır. '111'.count('11') == 1Ne zaman beklediğimizi unutmayın 2.
Cireo

2
Ayrıca, "basit O(n)çözüm" aslında O(10**d * n)ile daranır basamak sayısı ve ndize toplam uzunluğu. İkincisi O(n)zaman ve O(10**d + n)mekan.
Eric Duminil

10

İşte "konsensüs" O (n) algoritmasının bir NumPy uygulaması: giderken tüm üçüzler ve bin boyunca yürüyün. Çökeltme, "385" ile karşılaşıldığında O (1) işlemi olan çöpe [3, 8, 5] bir tane ekleyerek yapılır. Kutular bir 10x10x10küp içinde düzenlenmiştir . Binning tamamen vektörleştirildiğinden kodda bir döngü yoktur.

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:]))

Şaşırtıcı olmayan bir şekilde, NumPy @ Daniel'in büyük veri kümelerindeki saf Python çözümünden biraz daha hızlı. Örnek çıktı:

# 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 onu verimli indeksleme ile bir 3D matris olarak uygulamadığı sürece, iç içe geçmiş kutular yerine rakam dizesini düzleştirmek için önemli ölçüde daha hızlı. @ Daniel'in hangi versiyonuna karşı mücadele ettiniz; her tamsayı için dize araması yapan veya histogramı olan?
Peter Cordes

2
@PeterCordes Bundan şüphe ediyorum. ndarrayÇekirdek numpy türü, çok boyutlu sayı dizilerinin verimli depolanması, manipülasyonu ve endekslenmesi ile ilgilidir. Bazen düzleştirerek% birkaç tıraş edebilirsiniz, ancak bu durumda 100 x [0] + 10 x [1] + x [2] 'yi elle yapmak size fazla bir şey kazandırmaz. @Daniel'in daha hızlı olduğunu söyledim, kıyaslama kodunu kendiniz kontrol edebilirsiniz.
Paul Panzer

Gerçekten NumPy bilmiyorum (veya genel olarak Python; çoğunlukla x86 için C ve montaj performans ayarlaması yapıyorum), ama tek bir 3D diziniz olduğunu düşünüyorum, değil mi? İngilizce metninizden (görünüşte dikkatle okumadım bile) gerçek iç içe Python nesnelerine sahip olduğunuzu ve bunları ayrı olarak dizine eklediğini düşünüyordum. Ama durum böyle değil, bu yüzden ilk yorumum.
Peter Cordes

Kullandığınız saf Python sürümü, daha da yüksek oylanan cevapların kullandığı histogram uygulamasının hemen hemen aynı olduğunu düşünüyorum, ancak Python'a yazmanın farklı yolları hızı çok fazla etkiliyorsa.
Peter Cordes

3

Sorunu aşağıdaki gibi çözerdim:

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

Örnek dizenize uygulandığında, bu şu sonuçları verir:

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

Bu çözüm, sağlanan dizginin uzunluğu olduğu için O (n) 'de çalışır ve sanırım, alabileceğiniz en iyisidir.


Sadece bir Counter. A'ya ihtiyacınız final_dictyoktur ve her yinelemede güncellemeniz gerekmez.
Eric Duminil

2

Anladığım kadarıyla, çözümü sabit bir zamanda elde edemezsiniz. Milyon rakamının en az bir geçişini alacaktır (bir dize olduğu varsayılarak). Milyon uzunluk numarasının rakamları üzerinde 3 basamaklı yuvarlama yinelemesi yapabilir ve zaten varsa karma anahtarının değerini 1 artırabilir veya zaten yoksa yeni bir karma anahtarı oluşturabilirsiniz (değer 1 tarafından başlatıldı) sözlük.

Kod şöyle görünecektir:

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

Öğe değeri 1'den büyük anahtarlara filtre uygulayabilirsiniz.


2

Başka bir cevapta belirtildiği gibi, bu algoritmayı sabit zamanda yapamazsınız, çünkü en az n basamağa bakmanız gerekir. Doğrusal zaman alabileceğiniz en hızlı zamandır.

Ancak, algoritma O (1) boşluğunda yapılabilir . Sadece her 3 basamaklı sayının sayısını saklamanız gerekir, böylece 1000 girişlik bir diziye ihtiyacınız vardır. Ardından numarayı akış olarak aktarabilirsiniz.

Tahminimce mülakatçı size çözümü verdiğinde yanlış yazıyor ya da "sabit alan" dedikleri zaman "sabit zaman" ı yanlış anlıyorsunuz.


Diğerlerinin de işaret ettiği gibi, histogram yaklaşımı, aradığınız ondalık hane sayısının olduğu O(10**d)ekstra alan d.
Peter Cordes

1
Sözlük yaklaşımı n basamak için O (dk (10 ^ d, n)) olacaktır. Örneğin, n = 10 ^ 9 basamaklarınız varsa ve birden fazla ortaya çıkan nadir 15 basamak dizilerini bulmak istiyorsanız.
gnasher729

1

İşte cevabım:

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]))

Dizi arama yöntemi çok hızlı (@ paul-panzer'ın numpy yönteminden bile daha hızlı!). Tabii ki, tamamlandıktan sonra teknik olarak bitmediği için hile yapıyor, çünkü bir jeneratör geri dönüyor. Değer zaten mevcutsa her yinelemeyi kontrol etmek zorunda değildir, bu da çok yardımcı olacaktır.

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
Peki tam olarak neyi karşılaştırıyorsunuz? Kullanılmayan jeneratörler yerine listeler döndürmemelisiniz?
Eric Duminil

Countersbu şekilde kullanılmaz. Düzgün kullanıldığında, örneğinizle en hızlı seçenek haline gelirler. timeitBir jeneratöre ait bir listeyle kullanırsanız , yönteminiz Counterveya ' dan daha yavaş olur dict. Buraya bakın .
Eric Duminil

Son olarak, f_arrayher karakteri bir int: biçimine dönüştürüp ints = [int(c) for c in text]daha sonra kullanırsanız daha hızlı olabilirsiniz i, j, k = ints[n:n+3].
Eric Duminil


1

İşte benim çözümüm:

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}

Döngü için biraz yaratıcılık (ve örneğin Doğru / Yanlış / Hiçbiri ile ek arama listesi) ile, yalnızca o noktaya kadar bir kez ziyaret ettiğimiz dikte anahtarlar oluşturmak istediğiniz için son satırdan kurtulabilmelisiniz. . Umarım yardımcı olur :)


Pho7'nin cevabına bakınız . Ve yorumlar. Neden çok fazla oy almadığını anlamaya çalışın .
greybeard

0

-C perspektifinden anlatmak. -Bir int 3-d dizi sonucu olabilir [10] [10] [10]; -0. Konumdan n-4. konumuna gidin, burada n string dizisinin büyüklüğüdür. -Her yerde, mevcut, sonraki ve sonraki sonraki kontrol edin. - cntr değerini resutls [current] [next] [next's next] ++ olarak artırın; -Basın değerlerini yazdır

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) zamanı geldi, karşılaştırma yapılmadı. Diziyi bölümlere ayırarak ve bölümlerin etrafındaki eşleşmeleri hesaplayarak burada bazı paralel şeyler çalıştırabilirsiniz.


-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

Cevabınız için teşekkürler ama 5-6 gün önce @abhishek arora tarafından verilen bir algoritmaya çok benziyor. Ayrıca orijinal soru algoritma istemiyordu, daha ziyade farklı bir soru (zaten birkaç kez cevaplandı)
its.david
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.