आसन्न समान तत्वों के बिना किसी सूची के सभी क्रमपरिवर्तन उत्पन्न करें


87

जब हम किसी सूची को क्रमबद्ध करते हैं, जैसे

a = [1,2,3,3,2,2,1]
sorted(a) => [1, 1, 2, 2, 2, 3, 3]

समान तत्व हमेशा परिणामी सूची में आसन्न होते हैं।

मैं विपरीत कार्य कैसे प्राप्त कर सकता हूं - सूची में फेरबदल करें ताकि समतुल्य तत्व (या संभव के रूप में शायद ही कभी) आसन्न न हों?

उदाहरण के लिए, उपरोक्त सूची के लिए संभव समाधानों में से एक है

p = [1,3,2,3,2,1,2]

औपचारिक रूप से, एक सूची दी गई a, pइसका एक क्रमांक तैयार करें जो जोड़े की संख्या को कम करता है p[i]==p[i+1]

चूंकि सूचियाँ बड़ी हैं, इसलिए सभी क्रमपरिवर्तन उत्पन्न करना और फ़िल्टर करना एक विकल्प नहीं है।

बोनस प्रश्न: कुशलता से इस तरह के सभी क्रमपरिवर्तन कैसे उत्पन्न करें?

यह वह कोड है जिसका मैं समाधानों का परीक्षण करने के लिए उपयोग कर रहा हूं: https://gist.github.com/gebrkn/9f550094b3d24a35aebd

UPD: यहां विजेता चुनना एक कठिन विकल्प था, क्योंकि कई लोगों ने उत्कृष्ट उत्तर पोस्ट किए थे। @VincentvanderWeele , @David Eisenstat , @Coady , @ enrico.bacis और @srgerg ने ऐसे कार्य प्रदान किए जो त्रुटिपूर्ण रूप से सर्वोत्तम संभव क्रमचय उत्पन्न करते हैं। @tobias_k और डेविड ने बोनस प्रश्न (सभी क्रमपरिवर्तन उत्पन्न) का उत्तर दिया। सही प्रमाण के लिए डेविड को अतिरिक्त अंक।

@VincentvanderWeele का कोड सबसे तेज़ प्रतीत होता है।


1
तो आप केवल समानता के लिए परवाह करते हैं ? [1, 2, 1, 3, 1, 4, 1, 5]जैसा [1, 3, 1, 2, 1, 4, 1, 5]आपकी कसौटी है वैसा ही कुछ है ?
बकुरीउ

1
एक "कुशल" एल्गोरिथ्म नहीं हो सकता। तत्वों के [1, 1, 1, ..., 2, 3, 4, ..., N]साथ एक सूची लें 2N। आप एक अच्छा क्रमचय प्राप्त करने के लिए n > 1लगातार प्रत्येक जोड़ी के बीच एक संख्या डाल सकते 1हैं। फिर आप N/2तत्वों को अनुमति देते हैं और सभी वैध क्रमपरिवर्तन प्राप्त करते हैं (मतलब कोई भी एक बुरा नहीं है, लेकिन अधिक हो सकता है)। इस तरह के क्रमपरिवर्तन की संख्या O (N ^ 2) है, इसलिए आप O (N ^ 2) से बेहतर नहीं कर सकते। हालांकि भोले दृष्टिकोण के ओ (एन ^ 3) से बेहतर है।
बकुरीउ

6
@ बकुरी: दो बातें: (1) स्पष्ट होने के लिए, आपका उदाहरण दिखाता है कि बोनस प्रश्न के लिए कोई कुशल एल्गोरिदम नहीं हो सकता है । (2) अपने उदाहरण के लिए सभी इष्टतम समाधानों को मानते हुए O ((N / 2)!) है, जो O (N ^ 2) की तुलना में बहुत खराब है (यानी आपका उदाहरण आपको एहसास की तुलना में बहुत मजबूत है :-)
j_random_hacker

11
@msw: मैं एक वेबसाइट बना रहा हूं, और विभिन्न प्रदाताओं के विज्ञापन ब्लॉक के साथ एक पंक्ति है। मैं उन्हें व्यवस्थित करना चाहता हूं ताकि एक ही प्रदाता से कोई भी ब्लॉक अगल-बगल न खड़ा हो।
जियोर्ज

2
मैं यह नहीं कहूंगा कि यह "डुप्लिकेट के करीब भी नहीं है", लेकिन कथित डुप्लिकेट एक अलग सवाल है, क्योंकि समान तत्वों के बीच की दूरी को माना जाता है। व्हाईटरी की टिप्पणी के बाद जिन लोगों ने मतदान किया था: कृपया भविष्य में अधिक ध्यान दें।
डेविड आइसेनस्टैट

जवाबों:


30

यह थिज़र के वर्तमान में अपूर्ण छद्मकोश की तर्ज पर है। विचार शेष आइटम प्रकारों में से सबसे अधिक बार लेने के लिए है जब तक कि इसे अभी नहीं लिया गया। ( इस एल्गोरिथम के कोएडी के कार्यान्वयन को भी देखें ।)

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 दोष है तो b1 / k2। = मामला स्पष्ट है। दूसरे सममित हैं; अल्पसंख्यक तत्व का प्रत्येक उदाहरण कुल संभव k1 + k2 - 1 के अधिकांश दो दोषों को रोकता है।

यह लालची एल्गोरिथम इष्टतम समाधान लौटाता है, निम्न तर्क द्वारा। हम एक उपसर्ग (आंशिक समाधान) को सुरक्षित कहते हैं यदि यह एक इष्टतम समाधान तक विस्तारित होता है। स्पष्ट रूप से खाली उपसर्ग सुरक्षित है, और यदि एक सुरक्षित उपसर्ग एक संपूर्ण समाधान है तो वह समाधान इष्टतम है। यह व्यावहारिक रूप से यह दिखाने के लिए पर्याप्त है कि प्रत्येक लालची कदम सुरक्षा बनाए रखता है।

एक लालची कदम एक दोष का परिचय देता है कि केवल एक ही वस्तु प्रकार रहता है, जिस स्थिति में जारी रखने का केवल एक ही तरीका है, और वह तरीका सुरक्षित है। अन्यथा, P को विचाराधीन कदम से ठीक पहले P (सुरक्षित) उपसर्ग होने दें, P 'को उपसर्ग के ठीक बाद होने दें, और S को P का विस्तार करने वाला एक इष्टतम समाधान होने दें। यदि S, P का विस्तार करता है, तो हम कर रहे हैं। अन्यथा, P '= Px और S = PQ और Q = yQ' दें, जहाँ x और y आइटम हैं और Q और Q 'क्रम हैं।

मान लीजिए कि पहले P, y के साथ समाप्त नहीं होता है। एल्गोरिथ्म की पसंद से, x कम से कम क्यू में लगातार वाई के रूप में है। केवल x और y वाले Q के अधिकतम सबस्ट्रिंग पर विचार करें। यदि पहले सबरिंग में कम से कम कई x की y के रूप में है, तो इसे x के साथ शुरू करने के लिए अतिरिक्त दोषों को प्रस्तुत किए बिना फिर से लिखा जा सकता है। यदि पहली सबस्ट्रिंग में x की तुलना में अधिक y है, तो कुछ अन्य सबस्ट्रिंग में y की तुलना में अधिक x है, और हम अतिरिक्त दोषों के बिना इन सबस्ट्रिंग्स को फिर से लिख सकते हैं ताकि x पहले चला जाए। दोनों ही मामलों में, हमें एक इष्टतम समाधान T मिलता है, जो कि आवश्यकतानुसार P 'का विस्तार करता है।

मान लीजिए कि अब P, y के साथ समाप्त होता है। X की पहली घटना को सामने की ओर ले जाकर Q को संशोधित करें। ऐसा करने में, हम अधिकांश एक दोष (जहां x हुआ करते थे) और एक दोष (yy) को समाप्त करते हैं।

सभी समाधान उत्पन्न करना

यह पता लगाने के लिए कि वर्तमान में विचाराधीन किसी तरह से विश्व स्तर पर विवश है, यह पता लगाने के लिए टोबियास_क का उत्तर प्लस कुशल परीक्षण है। स्पर्शोन्मुख चलने का समय इष्टतम है, क्योंकि उत्पादन की लंबाई के क्रम में पीढ़ी का ओवरहेड होता है। दुर्भाग्य से सबसे खराब मामला द्विघात है; इसे बेहतर डेटा संरचनाओं के साथ रैखिक (इष्टतम) तक कम किया जा सकता है।

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))])

23

स्यूडोकोड:

  1. सूची को क्रमबद्ध करें
  2. सॉर्ट की गई सूची की पहली छमाही में लूप करें और परिणाम सूची के सभी सूचक भी भरें
  3. सॉर्ट की गई सूची की दूसरी छमाही में लूप करें और परिणाम सूची के सभी विषम सूचकांक भरें

आपके पास केवल तभी होगा p[i]==p[i+1]जब आधे से अधिक इनपुट में एक ही तत्व होता है, जिस स्थिति में एक ही तत्व को लगातार स्पॉट्स में डालने के अलावा और कोई विकल्प नहीं होता है (पिजन होल सिद्धांत द्वारा)।


जैसा कि टिप्पणियों में कहा गया है, इस दृष्टिकोण में एक संघर्ष भी हो सकता है अगर तत्वों में से एक कम से कम n/2समय (या n/2+1विषम के लिए होता है n; यह सामान्य (n+1)/2)और विषम दोनों के लिए सामान्य है )। ऐसे अधिकांश दो तत्व हैं और यदि दो हैं, तो एल्गोरिथ्म ठीक काम करता है। एकमात्र समस्याग्रस्त मामला तब होता है जब एक तत्व होता है जो कम से कम आधे समय में होता है। हम बस तत्व को खोजने और पहले से निपटने के द्वारा इस समस्या को हल कर सकते हैं।

मुझे इसे ठीक से लिखने के लिए अजगर के बारे में पर्याप्त जानकारी नहीं है, इसलिए मैंने ओपी के जीथुब से पिछले संस्करण के कार्यान्वयन की नकल करने की स्वतंत्रता ली:

# Sort the list
a = sorted(lst)

# Put the element occurring more than half of the times in front (if needed)
n = len(a)
m = (n + 1) // 2
for i in range(n - m + 1):
    if a[i] == a[i + m - 1]:
        a = a[i:] + a[:i]
        break

result = [None] * n

# Loop over the first half of the sorted list and fill all even indices of the result list
for i, elt in enumerate(a[:m]):
    result[2*i] = elt

# Loop over the second half of the sorted list and fill all odd indices of the result list
for i, elt in enumerate(a[m:]):
    result[2*i+1] = elt

return result

मेरी समझ से, यह वही है जो @jojo करता है - हमेशा इष्टतम नहीं।
georg

10
इस के लिए या तो विफल रहता है [0, 1, 1]या [0, 0, 1], आप 0-आधारित या 1-आधारित सूचकांक का उपयोग के आधार पर।
फुलाना

@georg वास्तव में मेरे उत्तर में भी यही दृष्टिकोण है। (ध्यान दें कि हेस्टर ने मेरे सामने जवाब दिया!)। मेरे कोड में हालांकि चरण 2 और 3. संयुक्त हैं, इस प्रकार दक्षता का अनुकूलन होता है।
जोजो

3
@flornquake अच्छी पकड़! यह अच्छा-पुराना ऑफ-बाय-वन एरर है जो मुझे डर लगता है। तो, यह दृष्टिकोण इष्टतम नहीं है, क्योंकि इसमें 1 संघर्ष भी हो सकता है।
विंसेंट वैन डेर वीले

1
@ हेयर्स: सभी लाइट्स ग्रीन हैं! "0 दोष"।
georg

10

एल्गोरिथ्म पहले से ही छोड़ दिया सबसे आम आइटम है कि पिछले आइटम नहीं है सही है ले लिया। यहां एक सरल कार्यान्वयन है, जो सबसे आम ट्रैक करने के लिए एक ढेर का उपयोग करता है।

import collections, heapq
def nonadjacent(keys):
    heap = [(-count, key) for key, count in collections.Counter(a).items()]
    heapq.heapify(heap)
    count, key = 0, None
    while heap:
        count, key = heapq.heapreplace(heap, (count, key)) if count else heapq.heappop(heap)
        yield key
        count += 1
    for index in xrange(-count):
        yield key

>>> a = [1,2,3,3,2,2,1]
>>> list(nonadjacent(a))
[2, 1, 2, 3, 1, 2, 3]

पायथन में एल्गोरिदम लिखने के लिए कैसे नहीं पर अच्छा उदाहरण। यह सिंटैक्स को पचाने के लिए 30 मिनट की तरह सरल लेकिन आवश्यक है।
alex904

8

आप एक पुनरावर्ती बैकट्रैकिंग एल्गोरिथ्म का उपयोग करके सभी 'पूरी तरह से अनसुमित' क्रमपरिवर्तन (आसन्न स्थिति में कोई दो समान तत्व नहीं) उत्पन्न कर सकते हैं । वास्तव में, सभी क्रमपरिवर्तन उत्पन्न करने के लिए एकमात्र अंतर यह है कि आप अंतिम संख्या पर नज़र रखते हैं और उसके अनुसार कुछ समाधान निकालते हैं:

def unsort(lst, last=None):
    if lst:
        for i, e in enumerate(lst):
            if e != last:
                for perm in unsort(lst[:i] + lst[i+1:], e):
                    yield [e] + perm
    else:
        yield []

ध्यान दें कि इस रूप में फ़ंक्शन बहुत कुशल नहीं है, क्योंकि यह बहुत सारी उप-सूचियाँ बनाता है। इसके अलावा, हम सबसे पहले सबसे अधिक विवश संख्या (सबसे अधिक गिनती वाले) को देखकर इसे गति दे सकते हैं। यहां केवल countsसंख्याओं का उपयोग करते हुए बहुत अधिक कुशल संस्करण है ।

def unsort_generator(lst, sort=False):
    counts = collections.Counter(lst)
    def unsort_inner(remaining, last=None):
        if remaining > 0:
            # most-constrained first, or sorted for pretty-printing?
            items = sorted(counts.items()) if sort else counts.most_common()
            for n, c in items:
                if n != last and c > 0:
                    counts[n] -= 1   # update counts
                    for perm in unsort_inner(remaining - 1, n):
                        yield [n] + perm
                    counts[n] += 1   # revert counts
        else:
            yield []
    return unsort_inner(len(lst))

आप इसका उपयोग सिर्फ nextसही क्रमपरिवर्तन, या listउन सभी को धारण करने के लिए कर सकते हैं। लेकिन ध्यान दें, अगर कोई पूरी तरह से अनियोजित क्रमपरिवर्तन नहीं है, तो यह जनरेटर परिणामस्वरूप कोई परिणाम नहीं देगा ।

>>> lst = [1,2,3,3,2,2,1]
>>> next(unsort_generator(lst))
[2, 1, 2, 3, 1, 2, 3]
>>> list(unsort_generator(lst, sort=True))
[[1, 2, 1, 2, 3, 2, 3], 
 ... 36 more ...
 [3, 2, 3, 2, 1, 2, 1]]
>>> next(unsort_generator([1,1,1]))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

इस समस्या को दरकिनार करने के लिए, आप अन्य उत्तरों में प्रस्तावित एल्गोरिदम में से एक के साथ इसका उपयोग कर सकते हैं। यह एक पूरी तरह से अनियोजित क्रमचय वापस करने की गारंटी देगा, अगर वहाँ एक है, या एक अच्छा सन्निकटन अन्यथा।

def unsort_safe(lst):
    try:
        return next(unsort_generator(lst))
    except StopIteration:
        return unsort_fallback(lst)

यह O (N ^ 2) मेमोरी का उपयोग करता है ... क्रमपरिवर्तन में प्रत्येक तत्व के लिए आप पुनरावर्ती कॉल के लिए सूची की एक प्रतिलिपि बना रहे हैं। इसके अलावा, पुनरावर्ती होने के नाते, यह "छोटी" लंबाई के साथ विफल हो जाता है।
बकुरीउ १३'१४

@ बकुरी सहमत, कि मेरा मतलब "दक्षता के लिए अनुकूलित नहीं" के साथ है ... हालांकि मुझे यह स्वीकार करना होगा कि मैंने O (n ^ 2) स्थान को छोड़कर नहीं किया था, लेकिन आप सही हैं ... मैं इसे सुधारने की कोशिश करूंगा। ।
टोबियास_

O (N ^ 2) हमेशा पीठ के पीछे होता है जब आपके पास एक पुनरावर्तन होता है जैसे कि T(n+1) = something + T(n)
बकुरीउ १३'१४

@tobias_k: क्या आप परीक्षण के लिए सिर्फ एक परमिट के लिए एक समारोह पोस्ट कर सकते हैं?
georg

@ सर्ग ज़रूर; next(unsort2(collections.Counter(a)));-) लेकिन चूंकि यह अहंकार सभी संभावनाओं को उत्पन्न करता है, इसलिए उन सभी की जांच क्यों नहीं? उस 7 तत्व परीक्षण सूची के लिए इसका केवल 38।
टोबियास_

5

अजगर में आप निम्न कार्य कर सकते हैं।

विचार करें कि आपके पास एक क्रमबद्ध सूची है l, आप यह कर सकते हैं:

length = len(l)
odd_ind = length%2
odd_half = (length - odd_ind)/2
for i in range(odd_half)[::2]:
    my_list[i], my_list[odd_half+odd_ind+i] = my_list[odd_half+odd_ind+i], my_list[i]

ये केवल जगह के संचालन में हैं और इस प्रकार तेजी से होना चाहिए ( O(N))। ध्यान दें कि आप से परिवर्तन होगा l[i] == l[i+1]करने के लिएl[i] == l[i+2] तो आपको अंत कुछ भी लेकिन यादृच्छिक है, लेकिन मैं कैसे सवाल को समझने से यह अनियमितता नहीं है आप के लिए देख रहे हैं।

विचार यह है कि बीच में छांटी गई सूची को विभाजित कर दो भागों में हर दूसरे तत्व का आदान-प्रदान किया जाए।

इसके लिए l= [1, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5]जाता हैl = [3, 1, 4, 2, 5, 1, 3, 1, 4, 2, 5]

विधि सभी से छुटकारा पाने में विफल रहती है l[i] == l[i + 1]जैसे ही एक तत्व की प्रचुरता सूची की लंबाई के आधे से अधिक या उसके बराबर होती है।

जब तक उपरोक्त ठीक काम करता है जब तक कि सबसे लगातार तत्व की बहुतायत सूची के आधे आकार से छोटी होती है, तो निम्न फ़ंक्शन सीमा मामलों (प्रसिद्ध ऑफ-बाय-वन अंक) को भी संभालता है जहां हर दूसरा तत्व शुरू होता है पहला सबसे प्रचुर मात्रा में होना चाहिए:

def no_adjacent(my_list):
    my_list.sort()
    length = len(my_list)
    odd_ind = length%2
    odd_half = (length - odd_ind)/2
    for i in range(odd_half)[::2]:
        my_list[i], my_list[odd_half+odd_ind+i] = my_list[odd_half+odd_ind+i], my_list[i]

    #this is just for the limit case where the abundance of the most frequent is half of the list length
    if max([my_list.count(val) for val in set(my_list)]) + 1 - odd_ind > odd_half:
        max_val = my_list[0]
        max_count = my_list.count(max_val)
        for val in set(my_list):
            if my_list.count(val) > max_count:
               max_val = val
               max_count = my_list.count(max_val)
        while max_val in my_list:
            my_list.remove(max_val)
        out = [max_val]
        max_count -= 1
        for val in my_list:
            out.append(val)
            if max_count:
                out.append(max_val)
                max_count -= 1
        if max_count:
            print 'this is not working'
            return my_list
            #raise Exception('not possible')
        return out
    else:
        return my_list

धन्यवाद! यह [3, 2, 1, 2, 1, 3, 2](रिटर्न [2, 1, 3, 1, 2, 2, 3], होना चाहिए (3, 2, 1, 2, 1, 3, 2)) के लिए विफल रहता है
जिस्त

@ सर्ग माफ करना, मेरा बुरा मैं भूल गया a +1। अब फिर से कोशिश करें।
जोजो

अभी भी समस्याएं, [1, 3, 3, 3, 3, 1, 1]=>[3, 1, 3, 3, 1, 3, 1]
जॉर्ज

@georg जैसा कि मैंने बताया, यह तब तक काम करता है जब तक कि सबसे प्रचुर मात्रा सूची की आधी लंबाई से कम मौजूद हो, जो इस उदाहरण में नहीं है।
जोजो

@georg तो मैंने एक-एक त्रुटि को संभालने वाला हिस्सा जोड़ा। यह भाग विशेष रूप से तेज़ नहीं है (थिज़र द्वारा सुझाए गए एल्गोरिदम के समान), हालांकि यह केवल दुर्लभ मामलों के लिए चलाया जाएगा।
जोजो

5

यहाँ एक अच्छा एल्गोरिथ्म है:

  1. सबसे पहले सभी संख्याओं के लिए गणना करें कि वे कितनी बार होती हैं। उत्तर को मानचित्र में रखें।

  2. इस नक्शे को क्रमबद्ध करें ताकि सबसे अधिक बार होने वाले अंक पहले आएं।

  3. आपके उत्तर की पहली संख्या क्रमबद्ध नक्शे में पहली संख्या है।

  4. पहले वाले नक्शे को अब छोटा कर दें।

यदि आप छँटाई कदम की दक्षता बढ़ाने के तरीकों के लिए दक्षता में सुधार करना चाहते हैं।


हाँ, यह @tobias_k ने क्या किया। अच्छा काम करने लगता है!
जॉर्ज

@georg यह थोड़ा अलग है ... मैं केवल अंतरिक्ष जटिलता को कम करने के लिए काउंटर का उपयोग करता हूं, लेकिन मैं किसी भी विशिष्ट क्रम से संख्याओं का परीक्षण नहीं करता (सोचा कि यह एक और गति हो सकती है)। यह अलग है कि मेरा समाधान हमेशा सभी 'सही' क्रमपरिवर्तन देता है, यदि कोई हो , जबकि यह सबसे अच्छा (?) समाधान (सही या नहीं) का उत्पादन करना चाहिए।
तोबियस_के

3
यह छद्म कोड काफी सही नहीं है; यदि आइटम की गणना 5 x, 2 y, 2 z है, तो यह अनावश्यक रूप से x को एक साथ रख देगा। एक फिक्स के लिए मेरा जवाब देखें ।
डेविड आइजनस्टैट

1
माना। उदाहरण के लिए [1,1,1,2,3] यह [1,2,1,3,1] के बजाय उदा [1,1,2,1,3] उत्पन्न करेगा।
Tobias_k

चरण 3 वास्तव में प्रति-उत्पादक है। यदि कोई संख्या सामान्य है (अगले सबसे लगातार संख्या की तुलना में कम से कम दो और प्रविष्टियाँ), तो चरण 3 उस संख्या का उपयोग पंक्ति में दो बार करेगा, बिना किसी औचित्य के।
एमएसलेटर

5

बोनस प्रश्न के उत्तर में: यह एक एल्गोरिथ्म है जो एक सेट के सभी क्रमपरिवर्तन पाता है जहां कोई भी आसन्न तत्व समान नहीं हो सकता है। मेरा मानना ​​है कि यह वैचारिक रूप से सबसे कुशल एल्गोरिथ्म है (हालांकि अन्य व्यवहार में तेज हो सकते हैं क्योंकि वे सरल कोड में अनुवाद करते हैं)। यह जानवर बल का उपयोग नहीं करता है, यह केवल अद्वितीय क्रमपरिवर्तन उत्पन्न करता है, और समाधान के लिए अग्रणी पथ जल्द से जल्द कट नहीं जाते हैं।

मैं एक तत्व के लिए "प्रचुर मात्रा में तत्व" शब्द का उपयोग एक सेट में करता हूं जो संयुक्त रूप से अन्य सभी तत्वों की तुलना में अधिक बार होता है, और प्रचुर तत्वों की संख्या के लिए "बहुतायत" शब्द अन्य तत्वों की संख्या को घटाता है।
जैसे सेट abacनहीं प्रचुर मात्रा में तत्व है, सेट abacaऔर aabcaaहै aप्रचुर मात्रा में तत्व के रूप में, और बहुतायत 1 और 2 क्रमशः।

  1. सेट की तरह शुरू करें:

aaabbcd

  1. दोहराव से पहली घटनाओं को अलग करें:

फ़र्स्ट: एब्सकांड
रिपीट: आब

  1. रिपीट में प्रचुर मात्रा में तत्व खोजें, यदि कोई हो, और बहुतायत की गणना करें:

प्रचुर तत्व: एक
बहुतायत: १

  1. सबसे पहले उन सभी क्रमचयों को उत्पन्न करें जहां प्रचुर तत्व के बाद तत्वों की संख्या बहुतायत से कम नहीं है: (इसलिए उदाहरण में "a" अंतिम नहीं हो सकता है)

abcd, abdc, acbd, acdb, adbc, adcb, Bacd, badc, bcad, bcac , bdac ,
bdca, cabd, Cadd, cbda , cbda , cdab, cdba, dbc, dacb , abac, dbca , dbab, dbc

  1. प्रत्येक क्रमपरिवर्तन के लिए, इन नियमों का पालन करते हुए, एक-एक करके बार-बार अक्षरों के सेट को डालें:

5.1। यदि सेट की बहुतायत अब तक क्रमचय में प्रचुर मात्रा में तत्व के अंतिम विघटन के बाद तत्वों की संख्या से अधिक है, तो अगले क्रमचय पर जाएं।
उदाहरण के लिए, जब अब तक क्रमपरिवर्तन है abc, प्रचुर तत्व वाला एक सेट aकेवल तभी डाला जा सकता है जब बहुतायत 2 या उससे कम हो, ऐसा aaaabcठीक है, aaaaabcनहीं।

5.2। उस सेट से तत्व का चयन करें, जिसकी क्रमपरिवर्तन में अंतिम स्थिति पहले आती है।
उदाहरण के लिए जब अब तक क्रमचय है abcbaऔर सेट है ab, का चयन करेंb

5.3। क्रमांकन में इसके अंतिम विक्षोभ के दाईं ओर चयनित तत्व को कम से कम 2 स्थिति में सम्मिलित करें।
उदाहरण के लिए, जब bक्रमचय में सम्मिलित किया जाता है babca, तो परिणाम babcbaऔर होते हैंbabcab

5.4। प्रत्येक परिणामी क्रमपरिवर्तन और सेट के बाकी हिस्सों के साथ चरण 5 को पुनर्खरीद करें।

EXAMPLE:
set = abcaba
firsts = abc
repeats = aab

perm3  set    select perm4  set    select perm5  set    select perm6

abc    aab    a      abac   ab     b      ababc  a      a      ababac  
                                                               ababca  
                                          abacb  a      a      abacab  
                                                               abacba  
                     abca   ab     b      abcba  a      -
                                          abcab  a      a      abcaba  
acb    aab    a      acab   ab     a      acaba  b      b      acabab  
                     acba   ab     b      acbab  a      a      acbaba  
bac    aab    b      babc   aa     a      babac  a      a      babaca  
                                          babca  a      -
                     bacb   aa     a      bacab  a      a      bacaba  
                                          bacba  a      -  
bca    aab    -
cab    aab    a      caba   ab     b      cabab  a      a      cababa  
cba    aab    -

यह एल्गोरिथम अद्वितीय क्रमपरिवर्तन उत्पन्न करता है। यदि आप क्रमपरिवर्तन की कुल संख्या जानना चाहते हैं (जहां abaदो बार गिना जा सकता है क्योंकि आप ए को स्विच कर सकते हैं), तो एक कारक के साथ अद्वितीय क्रमपरिवर्तन की संख्या को गुणा करें:

एफ = एन ! * एन 2 ! * ... * एन एन !

जहां N सेट में प्रत्येक तत्व के घटित होने की संख्या है। एक सेट के लिएabcdabcaba यह 4 होगा! * ३! * २! * 1! या 288, जो दर्शाता है कि एल्गोरिथ्म कितना अक्षम है कि केवल अद्वितीय लोगों के बजाय सभी क्रमपरिवर्तन उत्पन्न करता है। इस मामले में सभी क्रमपरिवर्तन की सूची के लिए, बस 288 बार अनूठे क्रमपरिवर्तन की सूची बनाएं :-)

नीचे जावास्क्रिप्ट में एक (बल्कि अनाड़ी) कार्यान्वयन है; मुझे संदेह है कि इस तरह की चीज़ के लिए पायथन जैसी भाषा बेहतर अनुकूल हो सकती है। "Abracadabra" के अलग-अलग क्रमपरिवर्तन की गणना करने के लिए कोड स्निपेट चलाएँ।

// FIND ALL PERMUTATONS OF A SET WHERE NO ADJACENT ELEMENTS ARE IDENTICAL
function seperatedPermutations(set) {
    var unique = 0, factor = 1, firsts = [], repeats = [], abund;

    seperateRepeats(set);
    abund = abundance(repeats);
    permutateFirsts([], firsts);
    alert("Permutations of [" + set + "]\ntotal: " + (unique * factor) + ", unique: " + unique);

    // SEPERATE REPEATED CHARACTERS AND CALCULATE TOTAL/UNIQUE RATIO
    function seperateRepeats(set) {
        for (var i = 0; i < set.length; i++) {
            var first, elem = set[i];
            if (firsts.indexOf(elem) == -1) firsts.push(elem)
            else if ((first = repeats.indexOf(elem)) == -1) {
                repeats.push(elem);
                factor *= 2;
            } else {
                repeats.splice(first, 0, elem);
                factor *= repeats.lastIndexOf(elem) - first + 2;
            }
        }
    }

    // FIND ALL PERMUTATIONS OF THE FIRSTS USING RECURSION
    function permutateFirsts(perm, set) {
        if (set.length > 0) {
            for (var i = 0; i < set.length; i++) {
                var s = set.slice();
                var e = s.splice(i, 1);
                if (e[0] == abund.elem && s.length < abund.num) continue;
                permutateFirsts(perm.concat(e), s, abund);
            }
        }
        else if (repeats.length > 0) {
            insertRepeats(perm, repeats);
        }
        else {
            document.write(perm + "<BR>");
            ++unique;
        }
    }

    // INSERT REPEATS INTO THE PERMUTATIONS USING RECURSION
    function insertRepeats(perm, set) {
        var abund = abundance(set);
        if (perm.length - perm.lastIndexOf(abund.elem) > abund.num) {
            var sel = selectElement(perm, set);
            var s = set.slice();
            var elem = s.splice(sel, 1)[0];
            for (var i = perm.lastIndexOf(elem) + 2; i <= perm.length; i++) {
                var p = perm.slice();
                p.splice(i, 0, elem);
                if (set.length == 1) {
                    document.write(p + "<BR>");
                    ++unique;
                } else {
                    insertRepeats(p, s);
                }
            }
        }
    }

    // SELECT THE ELEMENT FROM THE SET WHOSE LAST OCCURANCE IN THE PERMUTATION COMES FIRST
    function selectElement(perm, set) {
        var sel, pos, min = perm.length;
        for (var i = 0; i < set.length; i++) {
            pos = perm.lastIndexOf(set[i]);
            if (pos < min) {
                min = pos;
                sel = i;
            }
        }
        return(sel);
    }

    // FIND ABUNDANT ELEMENT AND ABUNDANCE NUMBER
    function abundance(set) {
        if (set.length == 0) return ({elem: null, num: 0});
        var elem = set[0], max = 1, num = 1;
        for (var i = 1; i < set.length; i++) {
            if (set[i] != set[i - 1]) num = 1
            else if (++num > max) {
                max = num;
                elem = set[i];
            }
        }
        return ({elem: elem, num: 2 * max - set.length});
    }
}

seperatedPermutations(["a","b","r","a","c","a","d","a","b","r","a"]);


1
इसके लिए धन्यवाद! यह देखना होगा कि क्या इसे जावास्क्रिप्ट में थोड़ा छोटा किया जा सकता है।
stt106

4

यह विचार है कि तत्वों को सबसे सामान्य से कम से कम सामान्य रूप से क्रमबद्ध किया जाए, सबसे आम लिया जाए, इसकी गिनती को कम किया जाए और इसे अवरोही क्रम को ध्यान में रखते हुए सूची में डाला जाए (लेकिन संभव होने पर पुनरावृत्ति को रोकने के लिए पहले उपयोग किए गए तत्व को रखने से बचें) ।

इसका उपयोग करके लागू किया जा सकता है Counterऔर bisect:

from collections import Counter
from bisect import bisect

def unsorted(lst):
    # use elements (-count, item) so bisect will put biggest counts first
    items = [(-count, item) for item, count in Counter(lst).most_common()]
    result = []

    while items:
        count, item = items.pop(0)
        result.append(item)
        if count != -1:
            element = (count + 1, item)
            index = bisect(items, element)
            # prevent insertion in position 0 if there are other items
            items.insert(index or (1 if items else 0), element)

    return result

उदाहरण

>>> print unsorted([1, 1, 1, 2, 3, 3, 2, 2, 1])
[1, 2, 1, 2, 1, 3, 1, 2, 3]

>>> print unsorted([1, 2, 3, 2, 3, 2, 2])
[2, 3, 2, 1, 2, 3, 2]

उदाहरण के लिए, यह विफल रहता है: [1, 1, 2, 3]जहां समाधान हैं जैसे कि [1, 2, 1, 3]
बाकुरिउ

हां, मुझे बस एहसास हुआ कि, क्षमा करें
enrico.bacis

धन्यवाद! यह हमेशा इष्टतम परिणाम [1, 2, 3, 2, 3, 2, 2]नहीं देता है , उदाहरण के लिए, यह रिटर्न [2, 3, 1, 2, 3, 2, 2](1 गलती), जबकि आदर्श है (2, 1, 2, 3, 2, 3, 2)) - जिस्ट देखें।
जॉर्ज

@georg सच, अच्छी पकड़, मैंने इसे सरल सिद्धांत का उपयोग करते हुए अद्यतन किया है।
enrico.bacis

@ enrico.bacis: धन्यवाद! नया संस्करण निर्दोष रूप से काम करता है। मैंने जिस्ट को अपडेट किया है। बहुत बुरा मैं तुम्हें अब और नहीं बढ़ा सकता।
georg

2
  1. सूची को क्रमबद्ध करें।
  2. इस एल्गोरिथ्म का उपयोग करके सूची का "सर्वश्रेष्ठ फेरबदल" बनाएं

यह सूची से आइटमों को उनके मूल स्थान (आइटम मूल्य के आधार पर) से कम से कम देगा, इसलिए यह आपके उदाहरण के लिए, 1 के, 2 और 3 को उनके क्रमबद्ध पदों से दूर रखने का प्रयास करेगा।


मैंने कोशिश की है best_shuffleऔर यह उत्पन्न हुआ [1,1,1,2,3] -> [3, 1, 2, 1, 1]- आदर्श नहीं!
जॉर्ज 9

2

लंबाई n की सॉर्ट की गई सूची से प्रारंभ करें। M = n / 2 दें। मान 0 पर लें, फिर m, फिर 1, फिर m + 1, फिर 2, फिर m + 2, और इसी तरह। जब तक आपके पास संख्याओं के आधे से अधिक नहीं है, तब तक आपको लगातार क्रम में बराबर मूल्य नहीं मिलेंगे।


विचार के लिए धन्यवाद। मुझे लगता है कि यह @Heuster ने क्या लागू किया है।
georg

2

कृपया मेरी "मुझे भी" शैली का उत्तर माफ़ करें, लेकिन क्या कोइडी के जवाब को सरल नहीं किया जा सकता है?

from collections import Counter
from heapq import heapify, heappop, heapreplace
from itertools import repeat

def srgerg(data):
    heap = [(-freq+1, value) for value, freq in Counter(data).items()]
    heapify(heap)

    freq = 0
    while heap:
        freq, val = heapreplace(heap, (freq+1, val)) if freq else heappop(heap)
        yield val
    yield from repeat(val, -freq)

संपादित करें: यहां एक अजगर 2 संस्करण है जो एक सूची लौटाता है:

def srgergpy2(data):
    heap = [(-freq+1, value) for value, freq in Counter(data).items()]
    heapify(heap)

    freq = 0
    result = list()
    while heap:
        freq, val = heapreplace(heap, (freq+1, val)) if freq else heappop(heap)
        result.append(val)
    result.extend(repeat(val, -freq))
    return result

हां, यह ठीक काम करता है (सिवाय इसके कि मैं py2 पर हूं और फ़ंक्शन को एक सूची वापस करनी चाहिए)।
georg

@georg Ok, मैंने एक अजगर 2 संस्करण जोड़ा है जो एक सूची देता है।
srgerg

2
  1. प्रत्येक मान प्रकट होने की संख्या की संख्या
  2. सबसे अधिक लगातार कम से कम क्रम से मूल्यों का चयन करें
  3. अंतिम आउटपुट में चयनित मूल्य को जोड़ें, प्रत्येक बार सूचकांक में 2 की वृद्धि
  4. यदि सूचकांक सीमा से बाहर है, तो सूचकांक को 1 पर रीसेट करें
from heapq import heapify, heappop
def distribute(values):
    counts = defaultdict(int)
    for value in values:
        counts[value] += 1
    counts = [(-count, key) for key, count in counts.iteritems()]
    heapify(counts)
    index = 0
    length = len(values)
    distributed = [None] * length
    while counts:
        count, value = heappop(counts)
        for _ in xrange(-count):
            distributed[index] = value
            index = index + 2 if index + 2 < length else 1
    return distributed
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.