Cho một chuỗi một triệu số, trả về tất cả các số có 3 chữ số lặp lại


137

Tôi đã có một cuộc phỏng vấn với một công ty quỹ phòng hộ ở New York vài tháng trước và thật không may, tôi đã không nhận được lời mời thực tập với tư cách là một kỹ sư dữ liệu / phần mềm. (Họ cũng yêu cầu giải pháp là bằng Python.)

Tôi gặp khá nhiều rắc rối về vấn đề phỏng vấn đầu tiên ...

Câu hỏi: Cho một chuỗi gồm một triệu số (ví dụ Pi), hãy viết hàm / chương trình trả về tất cả các số có 3 chữ số lặp lại và số lần lặp lại lớn hơn 1

Ví dụ: nếu chuỗi là: 123412345123456thì hàm / chương trình sẽ trả về:

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

Họ đã không cho tôi giải pháp sau khi tôi thất bại trong cuộc phỏng vấn, nhưng họ đã nói với tôi rằng độ phức tạp thời gian cho giải pháp là không đổi 1000 vì tất cả các kết quả có thể xảy ra là:

000 -> 999

Bây giờ tôi đang nghĩ về nó, tôi không nghĩ rằng có thể đưa ra một thuật toán thời gian không đổi. Là nó?


68
Nếu họ nghĩ rằng giải pháp là hằng số 1000, thì điều đó khiến tôi nghĩ rằng họ sẽ xây dựng tất cả các số có ba chữ số, và sau đó regex tìm kiếm chúng. Mọi người thường nghĩ rằng các hoạt động mà họ không thực sự viết / xem là "miễn phí". Tôi khá chắc chắn rằng đây sẽ là tuyến tính theo chiều dài của chuỗi.
mypetlion

54
Thật đáng tiếc, nếu kích thước đầu vào là một hằng số, mọi thuật toán đều là thời gian không đổi ;-)
Paŭlo Ebermann

34
hằng số 1000 là ? (bổ sung? voi?)
ilkkachu

31
Chà, nếu độ dài chuỗi không đổi (1M) và độ dài chuỗi con / số không đổi (3), thì về mặt kỹ thuật, mọi giải pháp đều không đổi thời gian
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 Đây có thể là thử nghiệm thực tế. Để xem liệu bạn có thể chứng minh cho họ tại sao điều này là không thể và cho họ thấy độ phức tạp thời gian tối thiểu chính xác.
James

Câu trả lời:


168

Bạn bước ra một cách nhẹ nhàng, có lẽ bạn không muốn làm việc cho một quỹ phòng hộ mà người đi thuê không hiểu các thuật toán cơ bản :-)

Không cách nào để xử lý cấu trúc dữ liệu có kích thước tùy ý O(1)trong trường hợp này, như trong trường hợp này, bạn cần phải truy cập mọi phần tử ít nhất một lần. Điều tốt nhất bạn có thể hy vọng là O(n)trong trường hợp này, nđộ dài của chuỗi.

Mặc dù, như là một sang một bên, một danh nghĩa O(n)thuật toán sẽ được O(1)cho một kích thước đầu vào cố định như vậy, về mặt kỹ thuật, họ có thể đã đúng ở đây. Tuy nhiên, đó thường không phải là cách mọi người sử dụng phân tích phức tạp.

Nó xuất hiện với tôi bạn có thể đã gây ấn tượng với họ theo một số cách.

Đầu tiên, bằng cách thông báo cho họ rằng không thể thực hiện được O(1), trừ khi bạn sử dụng lý do "nghi ngờ" nêu trên.

Thứ hai, bằng cách thể hiện các kỹ năng ưu tú của bạn bằng cách cung cấp mã Pythonic như:

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

Kết quả này:

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

mặc dù bạn có thể, tất nhiên, sửa đổi định dạng đầu ra thành bất cứ điều gì bạn muốn.

Và cuối cùng, bằng cách nói với họ gần như chắc chắn không có vấn đề gì với một O(n)giải pháp, vì đoạn mã trên mang lại kết quả cho chuỗi một triệu chữ số trong chưa đầy nửa giây. Nó dường như cũng có quy mô khá tuyến tính, vì một chuỗi 10.000.000 ký tự mất 3,5 giây và một chuỗi 100.000.000 ký tự mất 36 giây.

Và, nếu họ cần tốt hơn thế, có nhiều cách để song song loại công cụ này có thể tăng tốc đáng kể.

Tất nhiên, không phải trong một trình thông dịch Python duy nhất , do GIL, nhưng bạn có thể chia chuỗi thành một cái gì đó giống như ( vvyêu cầu chồng chéo được chỉ định để cho phép xử lý đúng các khu vực biên):

    vv
123412  vv
    123451
        5123456

Bạn có thể nuôi chúng ra để tách công nhân và kết hợp các kết quả sau đó.

Việc phân tách đầu vào và kết hợp đầu ra có khả năng làm lu mờ bất kỳ sự tiết kiệm nào với các chuỗi nhỏ (và thậm chí có thể là chuỗi triệu chữ số), nhưng đối với các tập dữ liệu lớn hơn nhiều, nó cũng có thể tạo ra sự khác biệt. Tất nhiên, câu thần chú thông thường của tôi về "biện pháp, đừng đoán" áp dụng ở đây.


Câu thần chú này cũng áp dụng cho các khả năng khác , chẳng hạn như bỏ qua Python hoàn toàn và sử dụng một ngôn ngữ khác có thể nhanh hơn.

Ví dụ, mã C sau, chạy trên cùng phần cứng với mã Python trước đó, xử lý một trăm triệu chữ số trong 0,6 giây, gần bằng thời gian khi mã Python xử lý một triệu. Nói cách khác, nhanh hơn nhiều :

#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
"Kích cỡ đầu vào cố định" này thực sự nghe giống như một trò đùa tồi tệ mà người phỏng vấn hoặc người được phỏng vấn không nhận được. Mỗi thuật toán trở nên O(1)được ncố định hoặc bị chặn.
Eric Duminil

5
Nếu họ cần tốt hơn thế, có lẽ họ không nên sử dụng Python, ít nhất là cho thuật toán cụ thể.
Sebastian Redl

3
@ezzzCash Bởi vì có thể có sự chồng chéo tại các điểm mà chuỗi bị "phá vỡ" khi thử một cách tiếp cận song song. Vì bạn đang tìm kiếm các nhóm có 3 chữ số, -2 cho phép kiểm tra cả hai nhóm song song để không bỏ lỡ một kết quả khớp hợp lệ.
code_dredd

5
@ezzzCash Không thiếu kiến ​​thức lập trình song song. Hãy xem xét một chuỗi chiều dài N. Nếu bạn chia nó thành hai phần tại vị trí N/2, bạn vẫn cần tính đến thực tế là bạn có thể bỏ lỡ trận đấu 3 chữ số hợp lệ ở "đường viền", vào cuối string1và đầu string2. Vì vậy, bạn cần kiểm tra sự trùng khớp giữa string1[N/2-2]string2[2](sử dụng chỉ số dựa trên số không), v.v ... Đó là ý tưởng.
code_dredd

1
Với các chuỗi chữ số dài hơn, sẽ có một cái gì đó để đạt được từ việc tối ưu hóa chuyển đổi thành số nguyên với một cửa sổ trượt cho phép bạn thả chữ số cao nhất và thêm một chữ số mới. (Chi phí Python có thể sẽ giết chết điều này, vì vậy nó chỉ áp dụng cho C hoặc các triển khai cấp thấp khác). val -= 100 * (d[i]-'0');để giảm chữ số hàng đầu. val = 10*val + d[i+2]-'0'để tích lũy một chữ số có ý nghĩa nhỏ nhất mới (chuỗi bình thường-> phân tích số nguyên). val % 100có thể không kinh khủng, nhưng chỉ khi 100là hằng số thời gian biên dịch nên nó không sử dụng phép chia CT thực sự.
Peter Cordes

78

Thời gian liên tục là không thể. Tất cả 1 triệu chữ số cần được xem xét ít nhất một lần, do đó, đó là độ phức tạp thời gian của O (n), trong đó n = 1 triệu trong trường hợp này.

Đối với giải pháp O (n) đơn giản, hãy tạo một mảng có kích thước 1000 đại diện cho số lần xuất hiện của mỗi số có 3 chữ số có thể. Tiến lên 1 chữ số tại một thời điểm, chỉ mục đầu tiên == 0, chỉ mục cuối cùng == 999997 và mảng tăng [số 3 chữ số] để tạo biểu đồ (số lần xuất hiện cho mỗi số có 3 chữ số có thể). Sau đó xuất nội dung của mảng với số lượng> 1.


26
@ezzzCash - vâng, một từ điển sẽ hoạt động, nhưng nó không cần thiết. Tất cả các "khóa" có thể được biết trước, giới hạn trong phạm vi 0 đến 999. Sự khác biệt về chi phí sẽ là thời gian cần thiết để truy cập dựa trên khóa sử dụng 3 chuỗi ký tự làm khóa, so với thời gian cần để chuyển đổi 3 chuỗi chữ số cho một chỉ mục và sau đó sử dụng chỉ mục để truy cập vào mảng.
RCgldr

4
Nếu bạn muốn các thủ thuật số, bạn cũng có thể quyết định đi BCD và lưu trữ ba chữ số trong 12 bit. Và giải mã các chữ số ASCII bằng cách che đi 4 bit thấp. Nhưng x-'0'mẫu đó không hợp lệ trong Python, đó là C-ism (trong đó các ký tự là số nguyên).
Yann Vernier

5
@LorenPechtel: Tra cứu từ điển trong Python rất nhanh. Cấp, truy cập mảng thậm chí còn nhanh hơn, vì vậy nếu chúng ta đã xử lý các số nguyên ngay từ đầu, bạn sẽ đúng. Tuy nhiên, trong trường hợp này, chúng tôi có các chuỗi 3 độ dài, trước tiên chúng tôi phải chuyển đổi thành số nguyên nếu chúng tôi muốn sử dụng chúng với các mảng. Nó chỉ ra rằng trái với những gì người ta có thể mong đợi đầu tiên, việc tra cứu từ điển thực sự nhanh hơn so với chuyển đổi số nguyên + truy cập mảng. Các giải pháp mảng trong thực tế là chậm hơn 50% trong trường hợp này.
Aleksi Torhamo

2
Tôi đoán người ta có thể lập luận rằng nếu số đầu vào luônchính xác 1 triệu chữ số, thì thuật toán đó O (1), với hệ số không đổi là 1 triệu.
tobias_k

2
@AleksiTorhamo - Nếu mục tiêu là so sánh tốc độ triển khai tương đối cho một thuật toán, tôi sẽ thích một ngôn ngữ truyền thống như C hoặc C ++, vì Python chậm hơn đáng kể và dường như có chi phí vượt trội so với Python so với các ngôn ngữ khác.
RCgldr

14

Một triệu là nhỏ cho câu trả lời tôi đưa ra dưới đây. Chỉ mong rằng bạn phải có thể chạy giải pháp trong cuộc phỏng vấn, không cần tạm dừng, sau đó Công việc sau chỉ trong chưa đầy hai giây và đưa ra kết quả cần thiết:

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)

Hy vọng rằng người phỏng vấn sẽ tìm kiếm việc sử dụng các bộ sưu tập thư viện tiêu chuẩn.

Phiên bản thực hiện song song

Tôi đã viết một bài đăng trên blog này với nhiều lời giải thích.


Nó hoạt động tốt và dường như là giải pháp nhanh nhất, không numpy.
Eric Duminil

3
@EricDuminil, tôi không nghĩ bạn nên lo lắng về việc có thời gian nhanh ở đây, khi hầu hết các giải pháp được đưa ra sẽ không làm bạn trì hoãn nhiều. Tốt hơn nhiều để cho thấy rằng bạn có một nắm bắt tốt về thư viện tiêu chuẩn Python và có thể viết mã có thể duy trì trong một tình huống phỏng vấn tôi sẽ nghĩ. (Trừ khi người phỏng vấn nhấn mạnh mức độ quan trọng của thời gian, bạn nên yêu cầu thời gian thực tế trước khi đánh giá điều gì tiếp theo).
Paddy3118

1
Chúng tôi đồng ý 100%. Mặc dù tôi không chắc có câu trả lời nào phù hợp hay không nếu người phỏng vấn thực sự nghĩ rằng nó có thể thực hiện được O(1).
Eric Duminil

1
Nếu người phỏng vấn nhấn mạnh rằng đó là thời điểm quan trọng, thì sau khi định hình để xác nhận đây là giới hạn, có lẽ đã đến lúc viết một mô-đun C để giải quyết nút thắt này. Tôi có một đoạn script thấy sự cải thiện 84 lần so với mã python sau khi chúng tôi chuyển sang sử dụng mô-đun ac.
TemporalWolf

Xin chào @TemporalWolf, tôi đã đọc những gì bạn nói sau đó nghĩ rằng một giải pháp khác, nhanh hơn và có thể mở rộng có thể là thay đổi nó thành thuật toán song song để có thể chạy trên nhiều quy trình trên trang trại / đám mây tính toán. Bạn phải chia chuỗi thành n phần; chồng chéo 3 ký tự cuối cùng của mỗi phần với phần tiếp theo. Mỗi phần sau đó có thể được quét ba lần một cách độc lập, các bộ ba tổng hợp và ba phần ba char ở cuối tất cả, nhưng phần cuối cùng được trừ đi vì nó sẽ được tính gấp đôi. Tôi có mã, và có lẽ sẽ biến nó thành một bài đăng trên blog ...
Paddy3118

13

Giải pháp O (n) đơn giản sẽ là đếm từng số có 3 chữ số:

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

Điều này sẽ tìm kiếm thông qua tất cả 1 triệu chữ số 1000 lần.

Di chuyển các chữ số chỉ một lần:

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)

Thời gian cho thấy rằng việc lặp lại chỉ một lần so với chỉ mục nhanh gấp đôi so với sử dụng count.


37
Có giảm giá thứ sáu đen text.count()?
Eric Duminil

3
@EricDuminil Bạn có một điểm tốt, nhưng, text.countđược thực hiện bằng ngôn ngữ được biên dịch tốc độ cao (ví dụ C) trái ngược với vòng lặp diễn giải cấp độ python chậm, vâng, có giảm giá.
John1024

Rất không hiệu quả khi đếm từng số riêng biệt nhưng đó là thời gian không đổi, do đó vẫn là O (n).
Loren Pechtel

11
Tùy chọn bạn đề xuất sử dụng countlà không chính xác, vì nó sẽ không tính các mẫu chồng chéo. Lưu ý rằng '111'.count('11') == 1khi chúng ta mong đợi nó sẽ được 2.
Cireo

2
Ngoài ra, " O(n)giải pháp đơn giản" của bạn thực sự làO(10**d * n)dsố chữ số tìm kiếm và ntổng độ dài của chuỗi. Cái thứ hai là O(n)thời gian và O(10**d + n)không gian.
Eric Duminil

10

Dưới đây là một triển khai NumPy của thuật toán "đồng thuận" O (n): đi qua tất cả các bộ ba và bin khi bạn đi. Việc tạo thùng được thực hiện bằng cách bắt gặp nói "385", thêm một vào bin [3, 8, 5] là thao tác O (1). Thùng được sắp xếp trong một 10x10x10khối. Vì binning được vector hóa hoàn toàn, không có vòng lặp trong mã.

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

Không có gì đáng ngạc nhiên, NumPy nhanh hơn một chút so với giải pháp Python thuần túy của @ Daniel trên các tập dữ liệu lớn. Đầu ra mẫu:

# 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

Có lẽ nhanh hơn đáng kể để làm phẳng chuỗi chữ số thay vì có các thùng lồng nhau, trừ khi NumPy kết thúc việc thực hiện nó như một ma trận 3D với việc lập chỉ mục hiệu quả. Phiên bản nào của @ Daniel đã làm bạn mất thời gian chống lại; cái nào chạy một chuỗi tìm kiếm cho mỗi số nguyên, hay cái có biểu đồ?
Peter Cordes

2
@PeterCordes Tôi nghi ngờ điều đó. ndarrays, loại numpy cốt lõi, là tất cả về lưu trữ, thao tác và lập chỉ mục hiệu quả của các mảng số đa chiều. Đôi khi bạn có thể cạo đi một vài% bằng cách làm phẳng, nhưng trong trường hợp này, thực hiện 100 x [0] + 10 x [1] + x [2] bằng tay sẽ không giúp bạn kiếm được nhiều tiền. Tôi đã sử dụng một @Daniel nói là nhanh hơn, bạn có thể tự kiểm tra mã điểm chuẩn.
Paul Panzer

Tôi thực sự không biết NumPy (hoặc Python nói chung; chủ yếu tôi làm C và điều chỉnh hiệu suất lắp ráp cho x86), nhưng tôi nghĩ bạn có một mảng 3D, phải không? Tôi đã suy nghĩ từ văn bản tiếng Anh của bạn (dường như tôi thậm chí không đọc kỹ) rằng bạn có các đối tượng Python lồng nhau thực sự và đang lập chỉ mục chúng một cách riêng biệt. Nhưng đó không phải là trường hợp, vì vậy nvm bình luận đầu tiên của tôi.
Peter Cordes

Tôi nghĩ rằng phiên bản Python thuần mà bạn đã sử dụng khá giống với cách thực hiện biểu đồ mà các câu trả lời được bình chọn thậm chí cao hơn được sử dụng, nhưng nếu các cách viết khác nhau trong Python ảnh hưởng đến tốc độ nhiều.
Peter Cordes

3

Tôi sẽ giải quyết vấn đề như sau:

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

Áp dụng cho chuỗi ví dụ của bạn, điều này mang lại:

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

Giải pháp này chạy trong O (n) với n là độ dài của chuỗi được cung cấp, và, tôi đoán là tốt nhất bạn có thể nhận được.


Bạn chỉ có thể sử dụng một Counter. Bạn không cần một final_dictvà bạn không phải cập nhật nó ở mỗi lần lặp.
Eric Duminil

2

Theo hiểu biết của tôi, bạn không thể có giải pháp trong một thời gian liên tục. Nó sẽ mất ít nhất một lần vượt qua số triệu chữ số (giả sử đó là một chuỗi). Bạn có thể lặp lại 3 chữ số trên các chữ số của số triệu chiều dài và tăng giá trị của khóa băm lên 1 nếu nó đã tồn tại hoặc tạo khóa băm mới (được khởi tạo bởi giá trị 1) nếu nó chưa tồn tại từ điển.

Mã sẽ trông giống như thế này:

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

Bạn có thể lọc xuống các khóa có giá trị mục lớn hơn 1.


2

Như đã đề cập trong một câu trả lời khác, bạn không thể thực hiện thuật toán này trong thời gian liên tục, bởi vì bạn phải xem ít nhất n chữ số. Thời gian tuyến tính là nhanh nhất bạn có thể nhận được.

Tuy nhiên, thuật toán có thể được thực hiện trong không gian O (1) . Bạn chỉ cần lưu trữ số đếm của mỗi số có 3 chữ số, vì vậy bạn cần một mảng gồm 1000 mục. Sau đó bạn có thể truyền số vào.

Tôi đoán là hoặc người phỏng vấn sai chính tả khi họ đưa ra giải pháp cho bạn, hoặc bạn nghe nhầm "thời gian không đổi" khi họ nói "không gian không đổi".


Như những người khác đã chỉ ra, cách tiếp cận biểu đồ là O(10**d)không gian thừa, trong đó dsố lượng chữ số thập phân bạn đang tìm kiếm.
Peter Cordes

1
Cách tiếp cận từ điển sẽ là O (min (10 ^ d, n)) cho n chữ số. Ví dụ: nếu bạn có n = 10 ^ 9 chữ số và muốn tìm chuỗi 15 chữ số hiếm xảy ra nhiều lần.
gnasher729

1

Đây là câu trả lời của tôi:

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

Phương pháp tra cứu mảng rất nhanh (thậm chí nhanh hơn phương pháp numpy của @ paul-panzer!). Tất nhiên, nó gian lận vì nó không hoàn thành về mặt kỹ thuật sau khi hoàn thành, bởi vì nó trả lại một máy phát điện. Nó cũng không phải kiểm tra mỗi lần lặp nếu giá trị đã tồn tại, có khả năng giúp ích rất nhiều.

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
Vì vậy, những gì bạn đang so sánh chính xác? Bạn không nên trả lại danh sách thay vì máy phát điện không sử dụng?
Eric Duminil

Counterskhông được sử dụng theo cách đó. Được sử dụng đúng cách, chúng trở thành lựa chọn nhanh nhất với ví dụ của bạn. Nếu bạn sử dụng timeitvới một danh sách bắt nguồn từ một trình tạo, phương thức của bạn sẽ trở nên chậm hơn Counterhoặc dict. Xem ở đây .
Eric Duminil

Cuối cùng, bạn f_arraycó thể nhanh hơn nếu trước tiên bạn chuyển đổi mọi char thành int: ints = [int(c) for c in text]và sau đó sử dụng i, j, k = ints[n:n+3].
Eric Duminil

1

Hình ảnh như câu trả lời:

HÌNH ẢNH TRẢ LỜI

Trông giống như một cửa sổ trượt.


1

Đây là giải pháp của tôi:

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}

Với một chút sáng tạo trong vòng lặp (và danh sách tra cứu bổ sung với True / Sai / Không có ví dụ), bạn sẽ có thể thoát khỏi dòng cuối cùng, vì bạn chỉ muốn tạo các khóa trong dict mà chúng tôi đã truy cập một lần cho đến thời điểm đó . Hy vọng nó giúp :)


Xem câu trả lời của pho7 . Và ý kiến. Cố gắng tìm hiểu tại sao nó không nhận được nhiều phiếu bầu.
greybeard

0

-Tìm hiểu theo quan điểm của C. -Bạn có thể có kết quả mảng int 3-d [10] [10] [10]; -Đi từ vị trí thứ 0 đến vị trí thứ 4, trong đó n là kích thước của mảng chuỗi. -Trên mỗi địa điểm, kiểm tra hiện tại, tiếp theo và tiếp theo. -Xác định cntr dưới dạng resutls [current] [next] [next's next] ++; -Xác định các giá trị của

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]

-Đó là thời gian O (n), không có sự so sánh nào liên quan. -Bạn có thể chạy một số nội dung song song ở đây bằng cách phân vùng mảng và tính toán các trận đấu xung quanh các phân vùng.


-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

Cảm ơn câu trả lời của bạn nhưng nó quá giống với thuật toán đã được đưa ra bởi @abhishek arora 5-6 ngày trước. Ngoài ra, câu hỏi ban đầu không phải là yêu cầu thuật toán mà là một câu hỏi khác (đã được trả lời nhiều lần)
it.david
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.