สิ่งนี้เป็นไปตามแนวของรหัสเทียมที่ไม่สมบูรณ์ในขณะนี้ของ Thijser แนวคิดคือการใช้ประเภทรายการที่เหลืออยู่บ่อยที่สุดเว้นแต่จะเพิ่งถ่าย (ดูการใช้อัลกอริทึมนี้ของ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
หลักฐานความถูกต้อง
สำหรับรายการสองประเภทที่มีการนับ k1 และ k2 วิธีแก้ปัญหาที่เหมาะสมที่สุดจะมีข้อบกพร่อง k2 - k1 - 1 หาก k1 <k2, 0 ข้อบกพร่องถ้า k1 = k2 และ k1 - k2 - 1 บกพร่องถ้า k1> k2 กรณี = ชัดเจน คนอื่น ๆ มีความสมมาตร แต่ละอินสแตนซ์ขององค์ประกอบส่วนน้อยจะป้องกันข้อบกพร่องได้มากที่สุดสองข้อจากทั้งหมด k1 + k2 - 1 ที่เป็นไปได้
อัลกอริทึมโลภนี้ส่งคืนโซลูชันที่ดีที่สุดโดยใช้ตรรกะต่อไปนี้ เราเรียกคำนำหน้า (โซลูชันบางส่วน) ว่าปลอดภัยหากขยายไปสู่โซลูชันที่เหมาะสมที่สุด เห็นได้ชัดว่าคำนำหน้าว่างนั้นปลอดภัยและหากคำนำหน้าปลอดภัยเป็นวิธีแก้ปัญหาทั้งหมดวิธีนั้นก็เหมาะสมที่สุด เป็นการแสดงให้เห็นอย่างอุปนัยว่าแต่ละขั้นตอนที่ละโมบรักษาความปลอดภัย
วิธีเดียวที่ขั้นตอนที่ละโมบทำให้เกิดข้อบกพร่องคือหากมีเพียงรายการเดียวที่เหลืออยู่ซึ่งในกรณีนี้มีทางเดียวที่จะดำเนินการต่อและวิธีนั้นปลอดภัย มิฉะนั้นให้ P เป็นคำนำหน้า (ปลอดภัย) ก่อนขั้นตอนที่กำลังพิจารณาให้ P 'เป็นคำนำหน้าหลังจากนั้นและปล่อยให้ S เป็นทางออกที่ดีที่สุดในการขยาย P หาก S ขยาย P' ด้วยแสดงว่าเราทำเสร็จแล้ว มิฉะนั้นให้ P '= px และ S = PQ และ Q = yQ' โดยที่ x และ y คือรายการและ Q และ Q 'เป็นลำดับ
สมมติก่อนว่า P ไม่ได้ลงท้ายด้วย y ตามตัวเลือกของอัลกอริทึม x อย่างน้อยที่สุดใน Q เท่ากับ y พิจารณาสตริงย่อยสูงสุดของ Q ที่มีเพียง x และ y หากสตริงย่อยแรกมีค่า x อย่างน้อยเท่ากับ y ก็สามารถเขียนใหม่ได้โดยไม่ต้องนำข้อบกพร่องเพิ่มเติมขึ้นต้นด้วย x หากสตริงย่อยแรกมีค่า y มากกว่า x แสดงว่าสตริงย่อยอื่น ๆ บางส่วนมีค่า x มากกว่า y และเราสามารถเขียนสตริงย่อยเหล่านี้ใหม่ได้โดยไม่มีข้อบกพร่องเพิ่มเติมเพื่อให้ x ไปก่อน ในทั้งสองกรณีเราพบทางออกที่ดีที่สุด T ที่ขยาย P 'ตามความจำเป็น
สมมติว่าตอนนี้ P ลงท้ายด้วย y แก้ไข Q โดยย้าย x ที่เกิดขึ้นครั้งแรกไปด้านหน้า ในการทำเช่นนั้นเราจะแนะนำข้อบกพร่องอย่างมากที่สุดหนึ่งข้อ (โดยที่ x เคยเป็น) และกำจัดข้อบกพร่องหนึ่งข้อ (yy)
สร้างโซลูชันทั้งหมด
นี่คือคำตอบของ tobias_kพร้อมการทดสอบที่มีประสิทธิภาพเพื่อตรวจจับเมื่อตัวเลือกที่อยู่ระหว่างการพิจารณาถูก จำกัด ทั่วโลกในทางใดทางหนึ่ง เวลาทำงานแบบ asymptotic นั้นเหมาะสมที่สุดเนื่องจากค่าโสหุ้ยของการสร้างเป็นไปตามลำดับความยาวของเอาต์พุต ความล่าช้าในกรณีที่เลวร้ายที่สุดน่าเสียดายคือกำลังสอง สามารถลดลงเป็นเชิงเส้น (เหมาะสมที่สุด) ด้วยโครงสร้างข้อมูลที่ดีกว่า
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]
จะเหมือนกับ[1, 3, 1, 2, 1, 4, 1, 5]
เกณฑ์ของคุณหรือไม่