2009-11-03 32 views
7

Tôi có hàm sumranges(), tổng hợp tất cả các dãy số liên tiếp được tìm thấy trong một bộ dữ liệu. Để minh họa:Tổng các dãy liên tiếp bằng Python

def sumranges(nums): 
    return sum([sum([1 for j in range(len(nums[i])) if 
        nums[i][j] == 0 or 
        nums[i][j - 1] + 1 != nums[i][j]]) for 
       i in range(len(nums))]) 

>>> nums = ((1, 2, 3, 4), (1, 5, 6), (19, 20, 24, 29, 400)) 
>>> print sumranges(nums) 
7 

Như bạn có thể thấy, nó trả về số dãy chữ số liên tiếp trong tuple, đó là: len ((1, 2, 3, 4), (1), (5, 6), (19, 20), (24), (29), (400)) = 7. Các bộ dữ liệu luôn được đặt hàng.

Vấn đề của tôi là tổng hợp của tôi() là khủng khiếp. Tôi ghét nhìn nó. Tôi hiện đang chỉ lặp qua bộ tuple và mỗi subtuple, gán 1 nếu số đó không phải là (1 + số trước đó), và tổng cộng tổng số. Tôi cảm thấy như tôi đang thiếu một cách dễ dàng hơn nhiều để hoàn thành mục tiêu đã nêu của tôi. Có ai biết một cách sâu sắc hơn để làm điều này?

Chỉnh sửa: Tôi đã đánh giá tất cả các câu trả lời được đưa ra từ trước đến nay. Nhờ tất cả các bạn cho câu trả lời của bạn.

Mã điểm chuẩn như sau, sử dụng một kích thước mẫu 100K:

from time import time 
from random import randrange 
nums = [sorted(list(set(randrange(1, 10) for i in range(10)))) for 
     j in range(100000)] 

for func in sumranges, alex, matt, redglyph, ephemient, ferdinand: 
    start = time() 
    result = func(nums) 
    end = time() 
    print ', '.join([func.__name__, str(result), str(end - start) + ' s']) 

Kết quả như sau. Câu trả lời thực tế chứng minh là xác minh rằng tất cả các chức năng trả lại câu trả lời đúng:

sumranges, 250281, 0.54171204567 s 
alex, 250281, 0.531121015549 s 
matt, 250281, 0.843333005905 s 
redglyph, 250281, 0.366822004318 s 
ephemient, 250281, 0.805964946747 s 
ferdinand, 250281, 0.405596971512 s 

RedGlyph không cạnh bật về tốc độ, nhưng câu trả lời đơn giản nhất có lẽ là Ferdinand, và có lẽ chiến thắng đối với hầu hết pythonic.

Trả lời

14

My 2 cents:

>>> sum(len(set(x - i for i, x in enumerate(t))) for t in nums) 
7 

Đó là về cơ bản là ý tưởng giống như descriped trong Alex' post, nhưng sử dụng một set thay vì itertools.groupby, kết quả trong một biểu thức ngắn hơn. Kể từ khi set s được thực hiện trong C và len() của một tập chạy trong thời gian không đổi, điều này cũng nên được khá nhanh.

+2

Đó là thực sự trơn. –

+0

Ah, tôi đã bỏ lỡ "tuples luôn được sắp xếp" bit trong câu hỏi - rằng bit _does_ làm cho câu trả lời này thích hợp hơn (tôi sẽ làm việc cho các bộ dữ liệu tùy ý, chứ không phải chỉ là những thứ đặt hàng, nhưng, có một mức giá nhỏ để trả tiền cho điều đó tổng quát). –

9

xem xét:

>>> nums = ((1, 2, 3, 4), (1, 5, 6), (19, 20, 24, 29, 400)) 
>>> flat = [[(x - i) for i, x in enumerate(tu)] for tu in nums] 
>>> print flat 
[[1, 1, 1, 1], [1, 4, 4], [19, 19, 22, 26, 396]] 
>>> import itertools 
>>> print sum(1 for tu in flat for _ in itertools.groupby(tu)) 
7 
>>> 

chúng ta "làm phẳng" the "tăng dốc" quan tâm bằng cách trừ các chỉ số từ giá trị, biến chúng thành "chạy" liên tiếp của giá trị giống nhau; sau đó chúng tôi xác định và có thể "chạy" với quý giá itertools.groupby. Điều này có vẻ là một giải pháp khá thanh lịch (và nhanh chóng) cho vấn đề của bạn.

+3

Bạn vừa mới thổi tâm trí của mình. –

+2

Tôi tự hỏi nếu 'tổng hợp (len (danh sách (itertools.groupby (tu))) cho tu trong căn hộ)' sẽ nhanh hơn hoặc chậm hơn. hơn 'tổng (1 cho ...)'. – ephemient

+2

@ephemient, tại sao lại tự hỏi? Đo! 'python -mtimeit' là bạn của bạn. –

0

này có lẽ có thể được đặt lại với nhau trong một hình thức nhỏ gọn hơn, nhưng tôi nghĩ rằng rõ ràng sẽ phải chịu:

def pairs(seq): 
    for i in range(1,len(seq)): 
     yield (seq[i-1], seq[i]) 

def isadjacent(pair): 
    return pair[0]+1 == pair[1] 

def sumrange(seq): 
    return 1 + sum([1 for pair in pairs(seq) if not isadjacent(pair)]) 

def sumranges(nums): 
    return sum([sumrange(seq) for seq in nums]) 


nums = ((1, 2, 3, 4), (1, 5, 6), (19, 20, 24, 29, 400)) 
print sumranges(nums) # prints 7 
+2

Sẽ rất tuyệt nếu bạn đánh giá các giải pháp được đề xuất khác nhau và cho chúng tôi biết cách chúng hoạt động. Đó là cách mọi người quyết định sử dụng '' .join (sequence) như là một thành ngữ Python, bằng cách đánh giá tất cả các cách khác nhau mà nó có thể được mã hóa. –

+0

Tôi sẽ tổng hợp các điểm chuẩn trong câu hỏi của mình. Ngoài ra, cảm ơn câu trả lời Matt. –

0

Bạn có thể làm tốt hơn điều này nếu bạn đã có một IntervalSet class vì sau đó bạn sẽ quét qua dãy của bạn để xây dựng IntervalSet của bạn, sau đó chỉ cần sử dụng số lượng thành viên đã đặt.

Một số tác vụ không phải lúc nào cũng cho vay mã gọn gàng, đặc biệt nếu bạn cần viết mã cho hiệu suất.

7

Chỉ cần để hiển thị một cái gì đó gần gũi hơn với mã ban đầu của bạn:

def sumranges(nums): 
    return sum((1 for i in nums 
        for j, v in enumerate(i) 
        if j == 0 or v != i[j-1] + 1)) 

Ý tưởng ở đây là:

  • tránh xây dựng danh sách trung gian nhưng sử dụng một máy phát điện thay vào đó, nó sẽ tiết kiệm được một số tài nguyên
  • tránh sử dụng các chỉ mục khi bạn đã chọn một phần phụ (i và v ở trên).

Còn lại sum() vẫn là cần thiết với ví dụ của tôi.

0

Có công thức cho điều này, tổng của các số n đầu tiên, 1+ 2+ ... + n = n (n + 1)/2. Sau đó, nếu bạn muốn có tổng của i-j thì đó là (j (j + 1)/2) - (i (i + 1)/2) điều này tôi chắc chắn đơn giản hóa nhưng bạn có thể làm việc đó. Nó có thể không phải là pythonic nhưng đó là những gì tôi sẽ sử dụng.

+0

Bạn có thể giải thích điều này một chút không? Hãy nhớ rằng tôi không tìm kiếm tổng số nhưng số lượng các chuỗi liên tiếp trong các bộ dữ liệu của tôi. –

+0

Được rồi, tôi đang trả lời sai câu hỏi, tôi nhớ hiểu. Tại sao không tìm thấy sự khác biệt của tất cả sau đó giữ chỉ 1s và thêm sau đó hoặc Len() danh sách. Tôi sẽ hiển thị nhưng gõ trên điện thoại của tôi hút – Vincent

1

Đây là nỗ lực của tôi:

def ranges(ls): 
    for l in ls: 
     consec = False 
     for (a,b) in zip(l, l[1:]+(None,)): 
      if b == a+1: 
       consec = True 
      if b is not None and b != a+1: 
       consec = False 
      if consec: 
       yield 1 

''' 
>>> nums = ((1, 2, 3, 4), (1, 5, 6), (19, 20, 24, 29, 400)) 
>>> print sum(ranges(nums)) 
7 
''' 

Nó nhìn vào những con số cặp, kiểm tra nếu họ là một cặp liên tiếp (trừ khi đó là lúc yếu tố cuối cùng của danh sách). Mỗi lần có một cặp số liên tiếp, nó tạo ra 1.

+0

Tôi đã phải thay đổi này một chút để làm việc với điểm chuẩn của tôi (đáng chú ý nhất bằng cách làm cho một danh sách ra khỏi biểu thức máy phát điện). Thời gian ở mức 0,64. Tôi nghĩ rằng bằng cách sử dụng một biểu thức máy phát điện là một giải pháp thú vị, tuy nhiên. –

Các vấn đề liên quan