隣接する等しい要素なしでリストのすべての順列を生成します


87

リストを並べ替えるとき

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

結果のリストでは、等しい要素が常に隣接しています。

どうすれば反対のタスクを実行できますか?リストをシャッフルして、等しい要素が決して(またはできるだけまれに)隣接しないようにしますか?

たとえば、上記のリストの場合、考えられる解決策の1つは次のとおりです。

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

より正式には、リストが与えられると、ペアの数を最小化するリストaの順列pを生成しp[i]==p[i+1]ます。

リストは大きいため、すべての順列を生成してフィルタリングすることはできません。

ボーナス質問:そのようなすべての順列を効率的に生成する方法は?

これは私がソリューションをテストするために使用しているコードです:https//gist.github.com/gebrkn/9f550094b3d24a35aebd

UPD:多くの人が優れた回答を投稿したため、ここで勝者を選ぶのは難しい選択でした。@VincentvanderWeele@デビッドEisenstat@Coady@ enrico.bacis@srgerg完璧に可能な限り最高の順列を生成する機能を提供します。@tobias_kとDavidもボーナスの質問に答えました(すべての順列を生成します)。正しさの証明のためのDavidへの追加のポイント。

@VincentvanderWeeleのコードが最速のようです。


1
だからあなたは平等だけを気にしますか?のようなもの[1, 2, 1, 3, 1, 4, 1, 5][1, 3, 1, 2, 1, 4, 1, 5]あなたの基準とまったく同じですか?
バクリウ2014

1
「効率的な」アルゴリズムはあり得ません。以下のようなリストを取る[1, 1, 1, ..., 2, 3, 4, ..., N]との2N要素を。n > 1連続1する各ペアの間に数字を入れて、適切な順列を取得できます。次に、N/2要素を並べ替えて、すべての有効な順列を取得します(つまり、悪いものはありませんが、もっとある可能性があります)。そのような順列の数はO(N ^ 2)であるため、O(N ^ 2)よりもうまくいくことはできません。ただし、単純なアプローチのO(N ^ 3)よりも優れています。
バクリウ2014

6
@Bakuriu:2つのこと:(1)明確にするために、あなたの例は、ボーナス質問のための効率的なアルゴリズムがあり得ないことを示しています。(2)例のすべての最適解を列挙するのはO((N / 2)!)で、これはO(N ^ 2)よりもはるかに悪いです(つまり、例はあなたが思っているよりもはるかに強力です:-)
j_random_hacker

11
@msw:私はウェブサイトを作っています、そして異なるプロバイダーからの広告ブロックの列があります。同じプロバイダーのブロックが並んでいないように配置したいと思います。
georg 2014

2
これが「重複にさえ近くない」とは言いませんが、同一の要素間の距離が考慮されているため、申し立てられた重複は別の質問です。WhyCryのコメントの後で閉じることに投票した人々:今後もっと注意を払ってください。
David Eisenstat 2014

回答:


30

これは、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の2つのアイテムタイプの場合、最適解には、k1 <k2の場合はk2-k1-1欠陥、k1 = k2の場合は0欠陥、k1> k2の場合はk1-k2-1欠陥があります。=の場合は明らかです。その他は対称です。少数派要素の各インスタンスは、可能な合計k1 + k2-1のうち最大2つの欠陥を防ぎます。

この欲張りアルゴリズムは、次のロジックによって最適解を返します。接頭辞(部分解)が最適解にまで及ぶ場合、それを安全と呼びます。明らかに、空のプレフィックスは安全であり、安全なプレフィックスがソリューション全体である場合、そのソリューションが最適です。貪欲な各ステップが安全性を維持していることを帰納的に示すだけで十分です。

貪欲なステップで欠陥が発生する唯一の方法は、アイテムタイプが1つだけ残っている場合です。この場合、続行する方法は1つだけであり、その方法は安全です。それ以外の場合は、検討中のステップの直前の(安全な)プレフィックスをPとし、直後のプレフィックスをP 'とし、Pを拡張する最適解をSとします。SがP'も拡張する場合は、これで完了です。それ以外の場合は、P '= PxおよびS = PQおよびQ = yQ'とします。ここで、xおよびyはアイテムであり、QおよびQ 'はシーケンスです。

まず、Pがyで終わっていないとします。アルゴリズムの選択により、xは少なくともQではyと同じ頻度です。xとyのみを含むQの最大部分文字列を考えてみましょう。最初の部分文字列に少なくともyと同じ数のxがある場合、xで始まる追加の欠陥を導入することなく書き換えることができます。最初の部分文字列のyがxよりも多い場合、他の部分文字列のxがyよりも多いため、追加の欠陥なしにこれらの部分文字列を書き換えて、xが最初になるようにすることができます。どちらの場合も、必要に応じて、P 'を拡張する最適解Tを見つけます。

ここで、Pがyで終わると仮定します。xの最初のオカレンスを前に移動してQを変更します。そうすることで、最大で1つの欠陥(xがあった場所)を導入し、1つの欠陥(yy)を排除します。

すべてのソリューションの生成

これはtobias_kの答えに加えて、現在検討中の選択が何らかの方法でグローバルに制約されている場合を検出するための効率的なテストです。生成のオーバーヘッドは出力の長さのオーダーであるため、漸近的な実行時間が最適です。残念ながら、最悪の場合の遅延は2次式です。より良いデータ構造で線形(最適)に減らすことができます。

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]入力の半分以上が同じ要素で構成されている場合にのみ使用できます。その場合、同じ要素を連続したスポットに配置する以外に選択肢はありません(鳩の巣原理によ​​る)。


コメントで指摘されているように、このアプローチでは、要素の1つが少なくとも複数n/2回発生する場合(またはn/2+1奇数の場合n、これ(n+1)/2)は偶数と奇数の両方に一般化されます)、1つの競合が多すぎる可能性があります。そのような要素は最大で2つあり、2つある場合、アルゴリズムは問題なく機能します。唯一の問題のあるケースは、少なくとも半分の時間で発生する要素が1つある場合です。要素を見つけて最初に処理することで、この問題を簡単に解決できます。

私はこれを正しく書くのに十分なPythonについて知らないので、自由に以前のバージョンのOPの実装をgithubからコピーしました。

# 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 2014

10
これは、0ベースまたは1ベースのインデックスを使用するかどうかに応じて、[0, 1, 1]またはのいずれかで失敗します[0, 0, 1]
flornquake 2014

@georg確かに、これは私の答えと同じアプローチです。(Heusterが私の前に答えたことに注意してください!)。ただし、私のコードでは、手順2と3が組み合わされているため、効率が最適化されます。
jojo 2014

3
@flornquakeグッドキャッチ!それは私が恐れている古き良き1つずつのエラーです。したがって、このアプローチは、1つの競合が多すぎる可能性があるため、最適ではありません。
Vincent van der Weele 2014

1
@Heuster:すべてのライトが緑色です!「0の障害」。
georg 2014

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]

Pythonでアルゴリズムを記述しない方法の良い例。シンプルですが、構文を消化するだけで30分ほどかかります。
alex904 2016年

8

再帰的なバックトラッキングアルゴリズムを使用して、すべての「完全にソートされていない」順列(隣接する位置に2つの等しい要素がない)を生成できます。実際、すべての順列を生成することとの唯一の違いは、最後の数を追跡し、それに応じていくつかのソリューションを除外することです。

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

この問題を回避するには、フォールバックとして他の回答で提案されているアルゴリズムの1つと一緒にこれを使用できます。これにより、完全にソートされていない順列があればそれを返すか、そうでない場合は適切な近似を返すことが保証されます。

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

これは、O(N ^ 2)メモリを使用します...再帰呼び出しのリストのコピーを実行している順列の各要素に対して。また、再帰的であるため、「短い」長さでは失敗します。
バクリウ2014

@Bakuriu同意しました、それは私が「効率のために最適化されていない」という意味でした...私はO(n ^ 2)スペースを除いていないことを認めなければなりませんが、あなたは正しいです...私はそれを改善しようとします。
tobias_k 2014

のような復活がある場合、O(N ^ 2)は常に後ろにありますT(n+1) = something + T(n)
バクリウ2014

@tobias_k:テスト用に1つのパーマの関数を投稿できますか?
georg 2014

@georg確かに:next(unsort2(collections.Counter(a)));-)しかし、このアルゴはすべての可能性を生み出すので、それらすべてをチェックしてみませんか?その7つの要素のテストリストの38だけです。
tobias_k 2014

5

Pythonでは、次のことができます。

ソートされたリストがあると考えてください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]ので、最終的な順序はランダムではないことに注意してください。しかし、私が質問を理解する方法から、それはあなたが探しているランダムではありません。

アイデアは、ソートされたリストを中央で分割し、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]、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))-要点を参照してください
georg 2014

@georg申し訳ありませんが、私の悪い私は忘れました+1。今すぐ再試行してください。
jojo 2014

まだ問題があります、[1, 3, 3, 3, 3, 1, 1]=>[3, 1, 3, 3, 1, 3, 1]
georg 2014

@georgが指摘したように、最も豊富なものがリストの半分未満の長さで存在する限り機能しますが、この例ではそうではありません。
jojo 2014

@georgそこで、of-by-oneエラーを処理する部分を追加しました。この部分は、まれなケースでのみ実行されますが、特に高速ではありません(Thijserによって提案されたアルゴリズムとほぼ同じです)。
jojo 2014

5

これが良いアルゴリズムです:

  1. まず第一に、それらが発生する頻度をすべての数で数えます。答えを地図に配置します。

  2. このマップを並べ替えて、最も頻繁に発生する番号が最初に来るようにします。

  3. あなたの答えの最初の番号は、ソートされたマップの最初の番号です。

  4. マップを再利用して、最初のマップを1つ小さくします。

効率を向上させたい場合は、ソートステップの効率を上げる方法を探してください。


はい、これは@tobias_kが行ったことです。うまくいくようです!
georg 2014

@georg少し違います...私はスペースの複雑さを減らすためだけにカウンターを使用しますが、特定の順序で数値をテストしません(これは別のスピードアップかもしれないと思いました)。違いは、私のソリューションは常にすべての「完全な」順列を生成しますが、これは最良の(?)ソリューション(完全かどうか)を生成するはずです。
tobias_k 2014

3
この擬似コードは完全には正しくありません。アイテム数が5x、2 y、2 zの場合、不必要にxをまとめます。修正については私の答えを参照しください。
David Eisenstat 2014

1
同意しました。たとえば[1,1,1,2,3]の場合、これにより[1,2,1,3,1]の代わりに[1,1,2,1,3]などが生成されます。
tobias_k 2014

ステップ3は実際には逆効果です。番号が一般的である場合(次に最も頻繁な番号より少なくとも2つ多いエントリ)、ステップ3は、正当化することなく、その番号を2回続けて使用します。
MSalters 2014

5

ボーナスの質問への回答:これは、隣接する要素が同一になることのないセットのすべての順列を見つけるアルゴリズムです。これは概念的に最も効率的なアルゴリズムであると私は信じています(ただし、他のアルゴリズムはより単純なコードに変換されるため、実際にはより高速である可能性があります)。それはブルートフォースを使用せず、固有の順列を生成するだけであり、解決策に至らないパスは最も早い時点で遮断されます。

セット内の要素のうち、他のすべての要素を組み合わせたものよりも頻繁に発生する要素には「豊富な要素」という用語を使用し、豊富な要素の数から他の要素の数を引いたものには「豊富な要素」という用語を使用します。
たとえば、セットにabacは豊富な要素がなく、セットabacaaabcaaa豊富な要素としてあり、それぞれ豊富な1と2があります。

  1. 次のようなセットから始めます。

aaabbcd

  1. 最初の発生を繰り返しから分離します。

最初:abcd
リピート:aab

  1. リピート内の豊富な要素がある場合はそれを見つけて、豊富さを計算します。

豊富な要素:
豊富:1

  1. 豊富な要素の後の要素の数が豊富さ以上である最初のすべての順列を生成します:(したがって、例では「a」を最後にすることはできません)

abcd、abdc、acbd、acdb、adbc、adcb、bacd、badc、bcad、bcda、bdac、bdca
cabd、cadb、cbad、cbda、cdab、cdba、dabc、dacb、abac、dbca、dcab、dcba

  1. 順列ごとに、次のルールに従って、繰り返される文字のセットを1つずつ挿入します。

5.1。セットの存在量が、これまでの順列での豊富な要素の最後の出現後の要素の数よりも多い場合は、次の順列にスキップします。
たとえば、これまでの順列がabcであるa場合、要素が豊富なセットは、存在比が2以下の場合にのみ挿入できます。したがってaaaabc、問題aaaaabcありません。

5.2。順列の最後の出現が最初に来るセットから要素を選択します。
たとえば、これまでの順列がでabcbaあり、セットがabである場合、を選択します。b

5.3。選択した要素を、順列の最後の出現の右側に少なくとも2つの位置に挿入します。
例えば挿入するときbの順列にbabca、結果があるbabcbababcab

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    -

このアルゴリズムは、一意の順列を生成します。順列の総数(abaaを切り替えることができるため2回カウントされる)を知りたい場合は、一意の順列の数に係数を掛けます。

F = N 1!* N 2!* ... * N n

ここで、Nはセット内の各要素の出現回数です。セットの場合、abcdabcabaこれは4になります!* 3!* 2!* 1!または288。これは、一意の順列だけでなく、すべての順列を生成するアルゴリズムがいかに非効率的であるかを示しています。この場合、すべての順列をリストするには、一意の順列を288回リストするだけです:-)

以下は、Javascriptでの(かなり不器用な)実装です。Pythonのような言語がこの種のことに適しているのではないかと思います。コードスニペットを実行して、「アブラカダブラ」の分離された順列を計算します。

// 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
これをありがとう!これがjavascriptで少し短縮できるかどうかがわかります。
stt106 2015

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]
バクリウ2014

はい、申し訳ありませんが、気づきました
enrico.bacis 2014

ありがとう!これは常に最適な結果をもたらすとは限りません。たとえば、(1つの障害)[1, 2, 3, 2, 3, 2, 2]が返されるの[2, 3, 1, 2, 3, 2, 2]に対し、理想は(2, 1, 2, 3, 2, 3, 2))です。要点を参照してください。
georg 2014

@georg確かに、いいキャッチです。使用する単純な原則を維持しながら更新しました。
enrico.bacis 2014

@ enrico.bacis:ありがとう!新しいバージョンは問題なく動作します。要旨を更新しました。残念ながら、もうあなたに賛成することはできません。
georg

2
  1. リストを並べ替えます。
  2. このアルゴリズムを使用し、リストの「最良のシャッフル」を生成します

リストの最小のアイテムを元の場所(アイテムの値で)に表示するため、たとえば、1、2、3を並べ替えられた位置から離して配置しようとします。


私が試したところbest_shuffle、生成されました[1,1,1,2,3] -> [3, 1, 2, 1, 1]-理想的ではありません!
georg 2014

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)

編集:リストを返すPython2バージョンは次のとおりです。

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わかりました、リストを返すpython2バージョンを追加しました。
srgerg 2014

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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.