2012-04-06 45 views
49

Giả sử tôi có một danh sách x với độ dài không xác định mà từ đó tôi muốn phát ngẫu nhiên một phần tử để danh sách không chứa phần tử sau đó. Cách nhiệt tình nhất để làm điều này là gì?Cách pythonic nhất để bật một phần tử ngẫu nhiên từ danh sách là gì?

tôi có thể làm điều đó bằng cách sử dụng combincation khá vụng về pop, random.randint, và len và muốn nhìn thấy các giải pháp ngắn hơn hoặc đẹp hơn:

import random 
x = [1,2,3,4,5,6] 
x.pop(random.randint(0,len(x)-1)) 

Edit: Những gì tôi đang cố gắng để đạt được là liên tục pop các phần tử ngẫu nhiên từ danh sách. (Ví dụ, một cách ngẫu nhiên bật một yếu tố và di chuyển nó đến một cuốn từ điển, pop ngẫu nhiên một yếu tố khác và di chuyển nó vào từ điển khác, ...)


Lưu ý rằng tôi đang sử dụng Python 2.6 và không tìm thấy bất kỳ giải pháp qua chức năng tìm kiếm.

+3

Tôi không phải là một Pythonista, nhưng điều đó có vẻ khá tốt với tôi. –

Trả lời

52

gì bạn dường như lên đến không trông rất Pythonic ở nơi đầu tiên. Bạn không nên loại bỏ các công cụ từ giữa danh sách, vì các danh sách được thực hiện như các mảng trong tất cả các triển khai Python mà tôi biết, vì vậy đây là một hoạt động O(n).

Nếu bạn thực sự cần chức năng này như là một phần của thuật toán, bạn nên kiểm tra cấu trúc dữ liệu như blist hỗ trợ xóa hiệu quả từ giữa.

Trong Python tinh khiết, những gì bạn có thể làm gì nếu bạn không cần truy cập đến các yếu tố còn lại chỉ là xáo trộn danh sách đầu tiên và sau đó lặp trên nó:

lst = [1,2,3] 
random.shuffle(lst) 
for x in lst: 
    # ... 

Nếu bạn thực sự cần phần còn lại (đó là một chút của một mùi mã, IMHO), ít nhất bạn có thể pop() từ cuối danh sách bây giờ (mà là nhanh chóng!):

while lst: 
    x = lst.pop() 
    # do something with the element  

Nói chung, bạn có thể thường xuyên bày tỏ chương trình của bạn thanh lịch hơn nếu bạn sử dụng nhiều hơn phong cách chức năng, thay vì trạng thái biến đổi (giống như bạn làm với danh sách).

+3

Vì vậy, một ý tưởng tốt hơn (nhanh hơn) sẽ là sử dụng 'random.shuffle (x)' và sau đó là 'x.pop()'? Tôi không hiểu làm thế nào để làm điều này "chức năng"? – Henrik

+0

@Henrik: Tôi không biết bạn đang cố gắng làm gì, vì vậy tôi không thể nói được. Bạn nên thêm thông tin thêm vào câu hỏi, hoặc chỉ cần bình luận ở đây những gì bạn muốn đạt được :) Điều này có vẻ là một trường hợp của [vấn đề XY] (http://meta.stackexchange.com/questions/66377/what-is -e-xy-problem) ... –

+0

Tôi có một danh sách các yếu tố mà từ đó tôi muốn liên tục bật các phần tử ngẫu nhiên. – Henrik

29

Bạn sẽ không nhận được nhiều hơn thế, nhưng đây là một sự cải thiện nhẹ:

x.pop(random.randrange(len(x))) 

Tài liệu trên random.randrange():

random.randrange ([bắt đầu], dừng lại [, bước ])
Trả lại phần tử được chọn ngẫu nhiên từ range(start, stop, step). Điều này tương đương với choice(range(start, stop, step)), nhưng không thực sự xây dựng một đối tượng phạm vi.

3

Một cách để làm điều đó là:

x.remove(random.choice(x)) 
+6

Điều này có thể gặp sự cố nếu các thành phần xuất hiện nhiều hơn một lần. –

+2

Điều này sẽ xóa phần tử ngoài cùng bên trái khi có các bản sao, gây ra kết quả không hoàn toàn ngẫu nhiên. – FogleBird

+0

Với 'pop', bạn có thể chỉ một tên tại phần tử đã xóa, với điều này bạn không thể. – agf

8

Đây là giải pháp thay thế khác: tại sao bạn không xáo trộn danh sách trước tiên và sau đó bắt đầu phần tử của nó cho đến khi không còn yếu tố nào nữa?như thế này:

import random 

x = [1,2,3,4,5,6] 
random.shuffle(x) 

while x: 
    p = x.pop() 
    # do your stuff with p 
+1

Tại sao không 'cho p trong x'? –

+3

@NiklasB. bởi vì chúng tôi đang xóa các phần tử khỏi danh sách. Nếu nó không hoàn toàn cần thiết để loại bỏ các phần tử, vâng tôi đồng ý với bạn: '[cho p in x]' –

+0

Bởi vì nó thay đổi danh sách và nếu bạn chỉ muốn chọn một nửa các phần tử bây giờ và một nửa sau, bạn sẽ có tập còn lại sau. – Henrik

7

Để loại bỏ một đơn yếu tố tại chỉ số ngẫu nhiên từ danh sách nếu thứ tự của các phần còn lại của các yếu tố danh sách không quan trọng:

import random 

L = [1,2,3,4,5,6] 
i = random.randrange(len(L)) # get random index 
L[i], L[-1] = L[-1], L[i] # swap with the last element 
x = L.pop()     # pop last element O(1) 

Các trao đổi được sử dụng để tránh O (n) hành vi xóa từ giữa danh sách.

2

Trong khi không xuất hiện trong danh sách, tôi đã gặp phải câu hỏi này trên Google trong khi cố gắng lấy X các mục ngẫu nhiên từ danh sách không trùng lặp. Dưới đây là những gì tôi cuối cùng được sử dụng:

items = [1, 2, 3, 4, 5] 
items_needed = 2 
from random import shuffle 
shuffle(items) 
for item in items[:items_needed]: 
    print(item) 

này có thể hơi kém hiệu quả như bạn đang xáo trộn toàn bộ danh sách nhưng chỉ sử dụng một phần nhỏ của nó, nhưng tôi không phải là một chuyên gia tối ưu hóa vì vậy tôi có thể là sai.

+1

'random.sample (items, items_needed)' – jfs

1

Câu trả lời này đến lịch sự của @niklas-b:

"Bạn có thể muốn sử dụng cái gì đó như pypi.python.org/pypi/blist"

Để trích dẫn PYPI page: ... một loại danh sách giống như

với hiệu suất tiệm cận tốt hơn và hiệu suất tương tự trên danh sách nhỏ

Blist là một thay thế thả cho danh sách Python cung cấp hiệu suất tốt hơn khi sửa đổi danh sách lớn . Các gói blist cũng cung cấp danh sách sắp xếp, sắp xếp, weaksortedlist, weaksortedset, sắp xếpdict, và các loại btuple.

Một sẽ giả định giảm hiệu suất trên truy cập ngẫu nhiên/ngẫu nhiên chạy cuối, vì nó là một "bản sao trên ghi" cấu trúc dữ liệu. Điều này vi phạm nhiều giả định về trường hợp sử dụng trên danh sách Python, để sử dụng nó với sự chăm sóc.

BAO GIỜ, nếu trường hợp sử dụng chính của bạn là làm điều gì đó kỳ lạ và không tự nhiên với danh sách (như trong ví dụ bắt buộc được đưa ra bởi @OP hoặc vấn đề hàng đợi Python 2.6 FIFO), thì điều này sẽ phù hợp với hóa đơn độc đáo.

0

Tôi biết đây là một câu hỏi cũ, nhưng chỉ vì tài liệu về:

Nếu bạn (người googling cùng một câu hỏi) đang làm những gì tôi nghĩ rằng bạn đang làm, đó là lựa chọn số k các mặt hàng ngẫu nhiên từ một danh sách (trong đó k < = len (danh sách của bạn)), nhưng đảm bảo mỗi mục không bao giờ được chọn nhiều lần (= lấy mẫu mà không cần thay thế), bạn có thể sử dụng random.sample như @ jf-sebastian. Nhưng mà không biết nhiều hơn về trường hợp sử dụng, tôi không biết đây có phải là thứ bạn cần hay không.

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