2012-01-03 42 views
11

Vì vậy, tôi có một danh sách của các nhómcụ thể danh sách shuffling bằng Python

[['a', 'b'], ['c', 'd', 'e'], ['f']] 

và tôi cần phải xáo trộn một phiên bản phẳng của danh sách này

[a, b, c, d, e, f] 

để các phần tử của cùng một nhóm sẽ kết thúc ở khoảng cách nào đó với nhau. Ví dụ.

[a, c, b, d, f, e] và không [a, c, b, d, e, f], vì de thuộc cùng một nhóm.

Tôi không quan tâm nếu khoảng cách chỉ là một phần tử trở lên, nhưng bất kỳ thành phần nào phải không ở gần yếu tố khác từ nhóm đó. Có bất kỳ thuật toán cho điều này?

Thuật toán cũng cần phải biết nếu điều này không thể thực hiện được.

+3

Điều gì xảy ra khi một nhóm có nhiều thành viên hơn tất cả các nhóm khác được kết hợp? –

+4

bạn mong đợi điều gì xảy ra nếu không thể thực hiện được, ví dụ: '[[a], [b, c, d, e, f]]' – amit

+0

Đây là danh sách xáo trộn bí mật ở Santa. Và các nhóm là những người không quan tâm đến việc tặng quà cho nhau. – Shark

Trả lời

5

Mã này tạo bản sao các nhóm ban đầu của bạn và mất mỗi lần một phần tử ngẫu nhiên từ nhóm (còn lại tại mỗi thời điểm) lớn nhất còn lại. Không hoàn toàn ngẫu nhiên, nhưng nên làm việc cho mọi trường hợp khi không có nhóm với hơn một nửa của tất cả các yếu tố.

import random 

list_of_groups = [['a', 'b'], ['c', 'd', 'e'], ['f']] 
l = [group[:] for group in list_of_groups] # make a copy 
random.shuffle(l) 
out = [] 
prev_i = -1 
while any(a for a in l): 
    new_i = max(((i,a) for i,a in enumerate(l) if i != prev_i), key=lambda x: len(x[1]))[0] 
    out.append(l[new_i].pop(random.randint(0, len(l[new_i]) - 1))) 
    prev_i = new_i 

print out 
+0

Phân bố có thể xảy ra nhất của các nhóm sẽ giống như '[[a, b], [c, d], [e, f], [g], [h], [j]]' I. e. một vài cặp và một vài đĩa đơn, vì vậy tôi nghĩ rằng 'new_i = random.choice (max (((i, a) cho i, a trong liệt kê (l) nếu a! = prev_i), key = lambda x: len (x [1]))) 'sẽ làm điều đó. – Shark

+1

@Shark - không, bạn hiểu nhầm dòng đó. 'Max()' trả về tuple 'i, a' trong đó' i' là id của nhóm (0… len-1) và 'a' là nhóm. Vì vậy, tôi lấy id với '[0]'. Bạn không thể 'random.choice' từ đó. Chỉ cần sử dụng toàn bộ mã và nó sẽ làm việc cho '[[a, b], [c, d], [e, f], [g], [h], [j]]' của bạn. – eumiro

+1

Bây giờ tôi thấy. Tôi chỉ cần xáo trộn 'list_of_groups' trước khi tạo, do đó thuật toán chọn các nhóm ngẫu nhiên có cùng độ dài mỗi lần. – Shark

0

Chỉ cần một nhanh, mã không-rất-nhã:

example = [[1, 2], [3, 4, 5], [6]] 
result = [] 
block = None 
last_block = None 

while example: 
    if block: 
     last_block = block 

    block = choice(example) 

    if last_block: 
     example.append(last_block) 

    element = choice(block) 
    result.append(element) 

    block.remove(element) 
    example.remove(block) 
    example = [a for a in example if a] 

print result 

Mặc dù vấn đề mà có thể xảy ra là trường hợp khi chỉ có một khối cho sự lựa chọn. Giống như trong trường hợp này:

[1, 6, 2, 3, 4, 5]

Tất nhiên phải có một số loại bắt ngoại lệ này, nhưng bây giờ tôi hy vọng mã này có thể cung cấp cho bạn một số ý tưởng làm thế nào để giải quyết vấn đề của bạn.

  • Được chỉnh sửa do một ngoại lệ xảy ra một lần trong một thời gian.
  • Quên không sử dụng các từ như "danh sách" làm tên biến. Cảm ơn bạn đã nhận xét - không được chỉnh sửa
+2

Không sử dụng 'danh sách' làm tên biến. – eumiro

2

Hãy để tôi lấy một cách tiếp cận khác nhau từ các câu trả lời hiện tại

Nguyên tắc cơ bản sẽ được shuffle mỗi danh sách trong danh sách, sort nó dựa trên chiều dài, zip_longest dựa trên lập luận biến mà được truyền từ danh sách và cuối cùng là chain danh sách và sau đó unwrap nó.điều đặc biệt cần lưu ý ở đây như thế nào chúng ta có thể vượt qua một danh sách như một args biến để một iterator, mà làm cho cuộc sống dễ dàng hơn ở đây :-)

Cho phép nói danh sách của bạn là

yourList=[['a', 'b'],['c', 'd', 'e'],['f']] 

Danh sách công việc của tôi (sau khi sao chép để giữ gìn danh sách ban đầu)

workList=yourList[::] 

Xáo trộn mỗi danh sách trong danh sách của bạn

[random.shuffle(x) for x in workList] 

Nex t chúng tôi sắp xếp danh sách dựa trên độ dài của mỗi danh sách

workList.sort(key=len,reverse=True) 

và sau đó cuối cùng chaining qua danh sách xáo trộn nén

[x for x in itertools.chain(*[x for x in itertools.izip_longest(*workList)]) if x] 

Và danh sách cuối cùng của bạn trông như

['e', 'b', 'f', 'c', 'a', 'd'] 
+0

Điều này giống như câu trả lời từ @eumiro, nhưng thanh lịch hơn :) – Shark

+0

Tôi thích phong cách của bạn, nhưng sẽ dễ dàng sao chép và dán điều này khi nhận xét giữa các dòng mã sẽ là nhận xét thực tế. – blinry

0

Tôi sẽ cung cấp cho câu trả lời "câm", đơn giản: chỉ cần san bằng danh sách, sau đó lặp lại ngẫu nhiên cho đến khi bạn nhận được danh sách có thể chấp nhận được.

Phương pháp này có tài sản được phân phối thống nhất trong tất cả các danh sách pháp lý, cũng như dễ dàng chứng minh chính xác và dễ hiểu đối với mã. Đó là bất lợi duy nhất là nó có thể trở nên chậm - nhưng với trường hợp sử dụng được đề cập trong một bình luận khác, tôi tin rằng nó sẽ đủ nhanh.

Đối với "là có thể" - cho nó một cái nhìn đầu tiên, tôi nghĩ rằng nó sẽ có thể nếu và chỉ khi không có nhóm có hơn một nửa các yếu tố, làm tròn lên. Nếu bạn xem xét các phần tử đầu tiên và cuối cùng liền kề, thay đổi "làm tròn" thành "làm tròn xuống".

+0

Tôi đã nghĩ về nó, nhưng đây là ứng dụng web và nó sẽ là kinda đau trong ass cho máy chủ để thử nhiều lần cho nhiều người dùng. Và, kết hợp nhóm đặc biệt không may mắn, điều này có thể làm chậm ứng dụng rất nhiều. – Shark

+0

@Shark: Bạn nói đúng, nó sẽ (có thể) chậm hơn - thậm chí có thể * nhiều * chậm hơn - nhưng điều đó không thực sự có nghĩa là nó sẽ chậm. Tôi không thể hứa rằng cách tiếp cận này sẽ đủ nhanh, nhưng tôi * sẽ * khuyên bạn không nên rơi vào cái bẫy chi tiêu nhiều nỗ lực và hy sinh để thử và tăng tốc một cái gì đó trước khi bạn thực sự biết nó thậm chí cần được tăng tốc lên. (Tôi nói "hy sinh" bởi vì tất cả các giải pháp được đăng khác xuất hiện không được phân phối đồng đều và tôi giả định rằng phân phối đồng đều thực sự mong muốn) – Hurkyl

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