Đây là dòng mã giả hiện chưa hoàn thiện của Thijser. Ý tưởng là sử dụng thường xuyên nhất các loại mặt hàng còn lại trừ khi nó mới được lấy. (Xem thêm việc triển khai thuật toán này của Coady .)
import collections
import heapq
class Sentinel:
pass
def david_eisenstat(lst):
counts = collections.Counter(lst)
heap = [(-count, key) for key, count in counts.items()]
heapq.heapify(heap)
output = []
last = Sentinel()
while heap:
minuscount1, key1 = heapq.heappop(heap)
if key1 != last or not heap:
last = key1
minuscount1 += 1
else:
minuscount2, key2 = heapq.heappop(heap)
last = key2
minuscount2 += 1
if minuscount2 != 0:
heapq.heappush(heap, (minuscount2, key2))
output.append(last)
if minuscount1 != 0:
heapq.heappush(heap, (minuscount1, key1))
return output
Bằng chứng về tính đúng đắn
Đối với hai loại vật phẩm, với số đếm k1 và k2, giải pháp tối ưu có k2 - k1 - 1 khuyết tật nếu k1 <k2, 0 khuyết tật nếu k1 = k2 và k1 - k2 - 1 khuyết tật nếu k1> k2. Trường hợp = là hiển nhiên. Những cái khác là đối xứng; mỗi trường hợp của phần tử thiểu số ngăn chặn tối đa hai khuyết tật trong tổng số k1 + k2 - 1 có thể.
Thuật toán tham lam này trả về các giải pháp tối ưu, theo logic sau. Chúng tôi gọi tiền tố (giải pháp từng phần) là an toàn nếu nó mở rộng thành giải pháp tối ưu. Rõ ràng tiền tố trống là an toàn và nếu tiền tố an toàn là một giải pháp toàn diện thì giải pháp đó là tối ưu. Nó đủ để thể hiện một cách cảm tính rằng mỗi bước tham lam duy trì sự an toàn.
Cách duy nhất để bước tham lam đưa ra một khiếm khuyết là nếu chỉ còn lại một loại mặt hàng, trong trường hợp này chỉ có một cách để tiếp tục và cách đó là an toàn. Nếu không, hãy đặt P là tiền tố (an toàn) ngay trước bước đang xem xét, đặt P 'là tiền tố ngay sau và đặt S là một giải pháp tối ưu mở rộng P. Nếu S cũng mở rộng P', thì chúng ta đã hoàn thành. Ngược lại, đặt P '= Px và S = PQ và Q = yQ', trong đó x và y là các mục và Q và Q 'là các chuỗi.
Giả sử đầu tiên rằng P không kết thúc bằng y. Theo lựa chọn của thuật toán, x ít nhất là thường xuyên trong Q bằng y. Xét các chuỗi con cực đại của Q chỉ chứa x và y. Nếu chuỗi con đầu tiên có ít nhất bao nhiêu số x bằng số y, thì nó có thể được viết lại mà không có thêm các khuyết tật bắt đầu bằng x. Nếu chuỗi con đầu tiên có nhiều y hơn x, thì một số chuỗi con khác có nhiều x hơn y và chúng ta có thể viết lại các chuỗi con này mà không có thêm khuyết tật để x đi trước. Trong cả hai trường hợp, chúng tôi tìm thấy một giải pháp tối ưu T kéo dài P ', nếu cần.
Giả sử bây giờ P kết thúc bằng y. Điều chỉnh Q bằng cách chuyển vị trí xuất hiện đầu tiên của x lên phía trước. Khi làm như vậy, chúng tôi giới thiệu nhiều nhất một khuyết tật (trước đây là x) và loại bỏ một khuyết tật (yy).
Tạo tất cả các giải pháp
Đây là câu trả lời của tobias_k cộng với các bài kiểm tra hiệu quả để phát hiện khi nào lựa chọn đang được xem xét bị hạn chế toàn cầu theo một cách nào đó. Thời gian chạy tiệm cận là tối ưu, vì chi phí phát sinh theo thứ tự độ dài của đầu ra. Không may là độ trễ trong trường hợp xấu nhất là bậc hai; nó có thể được giảm xuống tuyến tính (tối ưu) với cấu trúc dữ liệu tốt hơn.
from collections import Counter
from itertools import permutations
from operator import itemgetter
from random import randrange
def get_mode(count):
return max(count.items(), key=itemgetter(1))[0]
def enum2(prefix, x, count, total, mode):
prefix.append(x)
count_x = count[x]
if count_x == 1:
del count[x]
else:
count[x] = count_x - 1
yield from enum1(prefix, count, total - 1, mode)
count[x] = count_x
del prefix[-1]
def enum1(prefix, count, total, mode):
if total == 0:
yield tuple(prefix)
return
if count[mode] * 2 - 1 >= total and [mode] != prefix[-1:]:
yield from enum2(prefix, mode, count, total, mode)
else:
defect_okay = not prefix or count[prefix[-1]] * 2 > total
mode = get_mode(count)
for x in list(count.keys()):
if defect_okay or [x] != prefix[-1:]:
yield from enum2(prefix, x, count, total, mode)
def enum(seq):
count = Counter(seq)
if count:
yield from enum1([], count, sum(count.values()), get_mode(count))
else:
yield ()
def defects(lst):
return sum(lst[i - 1] == lst[i] for i in range(1, len(lst)))
def test(lst):
perms = set(permutations(lst))
opt = min(map(defects, perms))
slow = {perm for perm in perms if defects(perm) == opt}
fast = set(enum(lst))
print(lst, fast, slow)
assert slow == fast
for r in range(10000):
test([randrange(3) for i in range(randrange(6))])
[1, 2, 1, 3, 1, 4, 1, 5]
hệt như[1, 3, 1, 2, 1, 4, 1, 5]
tiêu chí của bạn?