Bu, Thijser'in şu anda tamamlanmamış sözde kodunun çizgileri boyunca. Buradaki fikir, yeni alınmadıkça kalan öğe türlerinden en sık olanını almaktır. (Ayrıca Coady'nin bu algoritma uygulamasına bakın .)
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
Doğruluğun kanıtı
K1 ve k2 sayımlarına sahip iki öğe türü için, optimum çözüm k1 <k2 ise k2 - k1 - 1, k1 = k2 ise 0 ve k1> k2 ise k1 - k2 - 1 kusurlarına sahiptir. = Durum açıktır. Diğerleri simetriktir; azınlık unsurunun her bir örneği, olası toplam k1 + k2 - 1'den en fazla iki kusuru önler.
Bu açgözlü algoritma, aşağıdaki mantıkla en uygun çözümleri döndürür. Optimal bir çözüme genişlemesi halinde güvenli bir önek (kısmi çözüm) diyoruz . Açıktır ki boş önek güvenlidir ve eğer güvenli bir önek tam bir çözümse, o zaman bu çözüm en uygunudur. Her açgözlü adımın güvenliği koruduğunu endüktif olarak göstermek yeterlidir.
Açgözlü bir adımın bir kusuru getirmesinin tek yolu, yalnızca bir öğe türünün kalmasıdır, bu durumda devam etmenin tek bir yolu vardır ve bu yol güvenlidir. Aksi takdirde, P, ele alınan adımdan hemen önceki (güvenli) önek olsun, P 'hemen sonra önek olsun ve S, P'yi genişleten optimal bir çözüm olsun. S de P'yi genişletirse, işimiz bitti demektir. Aksi takdirde, P '= Px ve S = PQ ve Q = yQ' olsun, burada x ve y öğelerdir ve Q ve Q 'dizilerdir.
Önce P'nin y ile bitmediğini varsayalım. Algoritmanın seçimine göre, x en az Q'da y kadar sıktır. Yalnızca x ve y içeren maksimum Q alt dizelerini düşünün. İlk alt dize en az y'ler kadar x'e sahipse, o zaman x ile başlamak için ek kusurlara neden olmadan yeniden yazılabilir. İlk alt dizede x'ten daha fazla y varsa, diğer bazı alt dizelerde y'den daha fazla x bulunur ve bu alt dizeleri ek kusurlar olmadan yeniden yazabiliriz, böylece x önce gelir. Her iki durumda da, gerektiğinde P '' yi genişleten optimal bir çözüm T buluyoruz.
Şimdi P'nin y ile bittiğini varsayalım. X'in ilk oluşumunu öne taşıyarak Q'yu değiştirin. Bunu yaparken, en fazla bir kusur ortaya koyuyoruz (x'in olduğu yerde) ve bir kusuru ortadan kaldırıyoruz (yy).
Tüm çözümleri üretmek
Bu, tobias_k'ın yanıtı artı şu anda değerlendirilen seçimin bir şekilde küresel olarak kısıtlandığını tespit etmek için etkili testlerdir. Asimptotik çalışma süresi optimumdur, çünkü üretimin ek yükü çıktının uzunluğuna göre sıralanır. En kötü durum gecikmesi ne yazık ki ikinci dereceden; daha iyi veri yapıları ile doğrusal (optimal) düzeye indirilebilir.
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]
tamamen aynı[1, 3, 1, 2, 1, 4, 1, 5]
mı?