1

一度に6つの値を選択することにより、データセットの3つの値の組み合わせすべてを効率的に生成するアルゴリズムを探しています。

データセットのすべての可能な3タプルの組み合わせを累積的に表現する6タプルの小さなセットを効率的に生成するアルゴリズムを探しています。

たとえば、可能な3枚のカードのすべての組み合わせを表す6枚のカードのトランプの手を計​​算します。

たとえば、データセットが与えられた場合:

['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']

6つの値の最初の「選択」は次のようになります。

['a','b','c','d','e','f']

そして、これは3つの値の組み合わせをカバーしています。

('a', 'b', 'c'), ('a', 'b', 'd'), ('a', 'b', 'e'), ('a', 'b', 'f'), ('a', 'c', 'd'),
('a', 'c', 'e'), ('a', 'c', 'f'), ('a', 'd', 'e'), ('a', 'd', 'f'), ('a', 'e', 'f'),
('b', 'c', 'd'), ('b', 'c', 'e'), ('b', 'c', 'f'), ('b', 'd', 'e'), ('b', 'd', 'f'),
('b', 'e', 'f'), ('c', 'd', 'e'), ('c', 'd', 'f'), ('c', 'e', 'f'), ('d', 'e', 'f')

それは明らかに次の方法で可能です。

  • すべてのトリプレットの組み合わせを計算する
  • 6つの値を選択
  • これらの6つの値のすべてのトリプレットの組み合わせを計算する
  • これらの組み合わせを最初の計算から削除する
  • すべてが説明されるまで繰り返す

この例では、2600の可能なトリプレットの組み合わせが(26*25*24)/(3*2*1) == 2600あり、上記の「強引な」方法を使用すると、すべてのトリプレットの組み合わせを約301の6値グループで表すことができます。

しかし、これを達成するためのより効率的な方法があるはずだと感じています。

私の好みの言語はですがpython、これをで実装することを計画していC++ます。


アップデート

これが「ブルートフォース」するための私のPythonコードです。

from itertools import combinations
data_set = list('abcdefghijklmnopqrstuvwxyz')

def calculate(data_set):
  all_triplets = list(frozenset(x) for x in itertools.combinations(data_set,3))
  data = set(all_triplets)
  sextuples = []
  while data:
    sxt = set()
    for item in data:
      nxt = sxt | item
      if len(nxt) > 6:
        continue
      sxt = nxt
      if len(nxt) == 6:
        break
    sextuples.append(list(sxt))
    covers = set(frozenset(x) for x in combinations(list(sxt),3))
    data = data - covers
    print "%r\t%s" % (list(sxt),len(data))
  print "Completed %s triplets in %s sextuples" % (len(all_triplets),len(sextuples),)

calculate(data_set)

301六重奏で2600トリプレットを完了

これよりも計算効率の良いものを探しています。


アップデート

Senderleは、興味深いソリューションを提供しました。データセットをペアに分割してから、ペアの可能なすべてのトリプレットを生成します。これは私が思いついたものよりも間違いなく優れています。

すべてのトリプレットがカバーされているかどうかを確認し、トリプレットカバレッジの冗長性を評価するための簡単な機能は次のとおりです。

from itertools import combinations
def check_coverage(data_set,sextuplets):
  all_triplets = dict.fromkeys(combinations(data_set,3),0)
  sxt_count = 0
  for sxt in sextuplets:
    sxt_count += 1
    for triplet in combinations(sxt,3):
      all_triplets[triplet] += 1
  total = len(all_triplets)
  biggest_overlap = overlap = nohits = onehits = morehits = 0
  for k,v in all_triplets.iteritems():
    if v == 0:
      nohits += 1
    elif v == 1:
      onehits += 1
    else:
      morehits += 1
      overlap += v - 1
    if v > biggest_overlap:
      biggest_overlap = v
  print "All Triplets in dataset: %6d" % (total,)
  print "Total triplets from sxt: %6d" % (total + overlap,)
  print "Number of sextuples:     %6d\n" % (sxt_count,)
  print "Missed  %6d of %6d: %6.1f%%" % (nohits,total,100.0*nohits/total)
  print "HitOnce %6d of %6d: %6.1f%%" % (onehits,total,100.0*onehits/total)
  print "HitMore %6d of %6d: %6.1f%%" % (morehits,total,100.0*morehits/total)
  print "Overlap %6d of %6d: %6.1f%%" % (overlap,total,100.0*overlap/total)
  print "Biggest Overlap: %3d" % (biggest_overlap,)

Senderleのsextupletsジェネレーターを使用して、繰り返されるトリプレットがローカライズされ、データセットのサイズが大きくなるにつれて、リピートが比例してよりローカライズされ、ピークリピートが大きくなることを観察することに魅了されています。

>>> check_coverage(range(26)、sextuplets(range(26)))
データセット内のすべてのトリプレット:2600
sxtからのトリプレットの合計:5720
6人組の数:286

2600の0を逃した:0.0%
2600のHitOnce2288:88.0%
2600のHitMore312:12.0%
2600のオーバーラップ3120:120.0%
最大のオーバーラップ:11

>>> check_coverage(range(40)、sextuplets(range(40)))
データセット内のすべてのトリプレット:9880
sxtからのトリプレットの合計:22800
6人組の数:1140

9880の0を逃した:0.0%
9880のHitOnce9120:92.3%
9880のHitMore760:7.7%
9880の重複12920:130.8%
最大のオーバーラップ:18

>>> check_coverage(range(80)、sextuplets(range(80)))
データセット内のすべてのトリプレット:82160
sxtからのトリプレットの合計:197600
6人組の数:9880

82160の0を逃した:0.0%
82160のHitOnce79040:96.2%
82160のHitMore3120:3.8%
82160の115440のオーバーラップ:140.5%
最大のオーバーラップ:38
4

1 に答える 1

1

以下は正しい結果を生むと思います。必要なすべての六つ子を生成するために必要なのは、アイテムの任意のペアのすべての可能な組み合わせを生成することであるという直感に依存しています。これは、すべての可能なトリプレットが表されるのに十分なほど値を「混合」します。

若干のシワがあります。アイテムの数が奇数の場合、1つのペアはまったくペアではないため、6つ組を生成することはできませんが、値を表す必要があります。これは、その問題を回避するためにいくつかの体操を行います。もっと良い方法があるかもしれませんが、それが何であるかはわかりません。

from itertools import izip_longest, islice, combinations

def sextuplets(seq, _fillvalue=object()):
    if len(seq) < 6:
        yield [tuple(seq)]
        return
    it = iter(seq)
    pairs = izip_longest(it, it, fillvalue=_fillvalue)
    sextuplets = (a + b + c for a, b, c in combinations(pairs, 3))
    for st in sextuplets:
        if st[-1] == _fillvalue:
            # replace fill value with valid item not in sextuplet
            # while maintaining original order
            for i, (x, y) in enumerate(zip(st, seq)):
                if x != y:
                    st = st[0:i] + (y,) + st[i:-1]
                    break
        yield st

長さ10〜80のアイテムのシーケンスでテストしたところ、すべてのケースで正しい結果が生成されました。ただし、これですべてのシーケンスに正しい結果が得られるという証拠はありません。また、これが六つ子の最小限のセットであるという証拠もありません。しかし、誰かがそれを思い付くことができれば、私はどちらかの証拠を聞きたいです。

>>> def gen_triplets_from_sextuplets(st):
...     triplets = [combinations(s, 3) for s in st]
...     return set(t for trip in triplets for t in trip)
... 
>>> test_items = [xrange(n) for n in range(10, 80)]
>>> triplets = [set(combinations(i, 3)) for i in test_items]
>>> st_triplets = [gen_triplets_from_sextuplets(sextuplets(i)) 
                   for i in test_items]
>>> all(t == s for t, s in zip(triplets, st_triplets))
True

すでにそう言っていますが、これは重複を生成するため、実際にトリプレットを生成する非効率的な方法であることを再度指摘します。

>>> def gen_triplet_list_from_sextuplets(st):
...     triplets = [combinations(s, 3) for s in st]
...     return list(t for trip in triplets for t in trip)
... 
>>> tlist = gen_triplet_list_from_sextuplets(sextuplets(range(10)))
>>> len(tlist)
200
>>> len(set(tlist))
120
>>> tlist = gen_triplet_list_from_sextuplets(sextuplets(range(80)))
>>> len(tlist)
197600
>>> len(set(tlist))
82160

確かに、理論的にはスピードアップする必要があります...

>>> len(list(sextuplets(range(80))))
9880

...それでも小さなシーケンスitertools.combinationsよりも優れています:sextuplets

>>> %timeit list(sextuplets(range(20)))
10000 loops, best of 3: 68.4 us per loop
>>> %timeit list(combinations(range(20), 3))
10000 loops, best of 3: 55.1 us per loop

そして、それはsextuplets中規模のシーケンスに対してはまだ競争力があります:

>>> %timeit list(sextuplets(range(200)))
10 loops, best of 3: 96.6 ms per loop
>>> %timeit list(combinations(range(200), 3))
10 loops, best of 3: 167 ms per loop

非常に大きなシーケンスで作業しているのでない限り、これが問題の価値があるかどうかはわかりません。(それでも、それは興味深い問題でした。)

于 2012-08-30T00:19:08.830 に答える