6

サイズ1の次元の立方体のすべての頂点を反復処理したいと思います。次のようnに実行できることはわかってitertools.productいます。

>>> n = 3
>>> for j in it.product((0,1), repeat=n) :
...     print j
... 
(0, 0, 0)
(0, 0, 1)
(0, 1, 0)
(0, 1, 1)
(1, 0, 0)
(1, 0, 1)
(1, 1, 0)
(1, 1, 1)

1ただし、座標で見つかったsの数に応じて、各頂点を異なる方法で処理する必要があります。 つまり(0, 1, 1)、すべての頂点が2つあるため、すべて同じ処理を受けます。上記のイテレータを使用してsの数を数えるのではなく、sの数で並べ替えられたデカルト積を生成したいと思います。(1, 0, 1)(1, 1, 0)111

>>> for ones in xrange(n) :
...     for seq in magic_expression(ones, n) :
...         print ones, seq
... 
0 (0, 0, 0)
1 (0, 0, 1)
1 (0, 1, 0)
1 (1, 0, 0)
2 (0, 1, 1)
2 (1, 0, 1)
2 (1, 1, 0)
3 (1, 1, 1)

私の高校の数学の先生は、これらを一度に2つの要素の順列のように呼んでいたでしょう。最初の要素は何度も繰り返され、2番目の要素nn - onesonesそれらが存在することを簡単に示すことがn! / ones! / (n - ones)!できます。

ウィキペディアによると、次のような辞書式順序でそれらを生成できます。

def lexicographical(ones, n) :
    perm = [0] * (n - ones) + [1] * ones
    yield tuple(perm)
    while True :
        k = None
        for j in xrange(n - 1) :
            if perm[j] < perm[j + 1] :
                k = j
        if k is None :
            break
        l = k + 1
        for j in xrange(k + 2, n) :
            if perm[k] < perm[j] :
                l = j
        perm[k], perm[l] = perm[l], perm[k]
        perm[k+1:] = perm[-1:k:-1]
        yield tuple(perm)

しかし、タイミングを合わせると、これは、の完全なデカルト積を数えることに対してのみ利益をもたらし始めn >= 10、次にones < 2、一般的な使用例ではない場合にのみ効果を発揮します。おそらくいくつかの強力なitertoolsブードゥーを使用して、またはまったく別のアルゴリズムを使用して、上記のコードを高速化するエレガントな方法はありますか?それが何か違いを生むのであれば、私は生成された順列の順序についてあまり気にすることはできませんでした。それとも私は数えることに自分自身を辞任する必要がありますか?


編集

私は提案された解決策についていくつかのタイミングを取りました。頂点を順番に消費すると、頂点がitertools.product生成され、カウントが常に最速になります。しかし、それらを1の数順に生成するには、リストを並べ替えるEeveeのソリューションが最速でn <= 6あり、それ以降、Camのソリューションは2つのうちで最速になります。

私はCamの解決策を受け入れました。なぜなら、それは求められていたものによりよく答えたものだからです。しかし、私が自分のコードに実装しようとしていることに関しては、私は自分自身を数えることに辞任するつもりです。

4

5 に答える 5

5

8行を超えるコードを記述して8つの定数値を生成した場合、問題が発生しています。

私が欲しいリストを埋め込むだけでなく、私はそれをばかげた方法で行います:

vertices = (
    (v.count(1), v)
    for v in itertools.product((0, 1), repeat=3)
)
for count, vertex in sorted(vertices):
    print vertex

1000ハイパーキューブを使用している場合を除いて、パフォーマンスに関する大きな心配はありません。

于 2013-01-15T02:10:13.513 に答える
3

(非効率的な)代替方法:

>>> ['{0:03b}'.format(x) for x in range(8)]
['000', '001', '010', '011', '100', '101', '110', '111']

またはタプルとして:

>>> [tuple(int(j) for j in list('{0:03b}'.format(x))) for x in range(8)]

[(0, 0, 0),
 (0, 0, 1),
 (0, 1, 0),
 (0, 1, 1),
 (1, 0, 0),
 (1, 0, 1),
 (1, 1, 0),
 (1, 1, 1)]

頂点の数でソート:

>>> sorted(_, key=lambda x: sum(x))

[(0, 0, 0),
 (0, 0, 1),
 (0, 1, 0),
 (1, 0, 0),
 (0, 1, 1),
 (1, 0, 1),
 (1, 1, 0),
 (1, 1, 1)]

またはitertoolsを使用する:

>>> sorted(itertools.product((0, 1), repeat=3), key=lambda x: sum(x))

[(0, 0, 0),
 (0, 0, 1),
 (0, 1, 0),
 (1, 0, 0),
 (0, 1, 1),
 (1, 0, 1),
 (1, 1, 0),
 (1, 1, 1)]
于 2013-01-15T02:37:36.900 に答える
2

3Dキューブのユースケースでは、イーブイのソリューションが正しいソリューションです。

ただし、楽しみのために、そしてitertoolsの力を実証するために、より高い次元に一般化する線形時間ソリューションを次に示します。

from itertools import combinations

# n is the number of dimensions of the cube (3 for a 3d cube)
def generate_vertices(n):
    for number_of_ones in xrange(0, n + 1):
        for location_of_ones in combinations(xrange(0, n), number_of_ones):
            result = [0] * n
            for location in location_of_ones:
                result[location] = 1
            yield result

for vertex in generate_vertices(3):
    print vertex


# result:
# [0, 0, 0]
# [1, 0, 0]
# [0, 1, 0]
# [0, 0, 1]
# [1, 1, 0]
# [1, 0, 1]
# [0, 1, 1]
# [1, 1, 1]
于 2013-01-15T02:18:09.827 に答える
1

すべての頂点を反復処理する必要がある場合、O(f(n))は少なくともO(f(n)* 2 n)であるため、頂点をどのように処理するかに応じてカウントすることは悪い考えではありません。それらのソートはO(n * 2 n)です。したがって、基本的にf(n)専攻がnかどうかによって異なります。

それとは別に、可能な魔法の表現があります:

def magic_expression(ones, n):
    a = (0,) * (n - ones) + (1,) * ones
    previous = tuple()
    for p in itertools.permutations(a):
        if p > previous:
            previous = p
            yield p

一意の値を持つ順列の助けを借りて。

itertools.permutationsはソートされた結果を生成するため、これは機能します。ゼロが最初に来るため、aは最初にソートされることに注意してください。

于 2013-01-15T01:53:03.990 に答える
1

以下は、CamまたはEeveeよりも高速(中程度の場合n)および数倍高速(大規模の場合)で実行されるコードです。n時間の比較は次のとおりです。

def cornersjc (n):   # Re: jw code
    from itertools import product
    m = (n+1)/2
    k = n-m
    # produce list g of lists of tuples on k bits
    g = [[] for i in range(k+1)]
    for j in product((0,1), repeat=k):
        g[sum(j)].append(tuple(j))
    # produce list h of lists of tuples on m bits
    if k==m:
        h = g
    else:
        h = [[] for i in range(m+1)]
        for j in product((0,1), repeat=m):
            h[sum(j)].append(tuple(j))
    # Now deliver n-tuples in proper order
    for b in range(n+1):  # Deliver tuples with b bits set
        for lb in range(max(0, b-m), min(b+1,k+1)):
            for l in g[lb]:
                for r in h[b-lb]:
                    yield l+r

以下に示すタイミング結果%timeitは、ipythonでの一連の呼び出しからのものです。各呼び出しは、(私のコード、Camのコード、Eeveeのコード、およびEeveeのメソッドの私のバージョンを表す)の代わりに
%timeit [x for x in cube1s.f(n)]
名前cornersjc, cornerscc, cornersec, cornersesを使用し、の代わりに番号を使用したような形式でした。fn

n    cornersjc    cornerscc    cornersec    cornerses

5      40.3 us      45.1 us      36.4 us      25.2 us    
6      51.3 us      85.2 us      77.6 us      46.9 us    
7      87.8 us      163 us       156 us       88.4 us    
8     132 us       349 us       327 us       178 us    
9     250 us       701 us       688 us       376 us    
10    437 us      1.43 ms      1.45 ms       783 us
11    873 us      3 ms         3.26 ms      1.63 ms
12   1.87 ms      6.66 ms      8.34 ms      4.9 ms

のコードcornersjcは上記のとおりです。cornerscc、、 cornersecの コードcornersesは次のとおりです。cornersjcこれらは、Camのコードがタプルのリストではなくリストのリストを生成し、各ビットカウントグループ内で逆に生成されることを除いて、と同じ出力を生成します。

def cornerscc(n):   # Re: Cam's code
    from itertools import combinations
    for number_of_ones in xrange(0, n + 1):
        for location_of_ones in combinations(xrange(0, n), number_of_ones):
            result = [0] * n
            for location in location_of_ones:
                result[location] = 1
            yield result

def cornersec (n):   # Re:  Eevee's code
    from itertools import product
    vertices = ((v.count(1), v)
                for v in product((0, 1), repeat=n))
    for count, vertex in sorted(vertices):
        yield vertex

def cornerses (n):   # jw mod. of Eevee's code
    from itertools import product
    for vertex in sorted(product((0, 1), repeat=n), key=sum):
        yield vertex

の最後の3行は次のcornersjcように置き換えることができます。

            for v in product(g[lb], h[b-lb]):
                yield v[0]+v[1]

これはよりクリーンですが遅いです。yield vの代わりにを使用するとyield v[0]+v[1]、コードはより高速に実行されますがcornersjc、(at n=5)は((1、0)、(1、1、0));のようなタプルのペアの結果を生成します。を yield v[0]+v[1]使用すると、コードの実行速度は遅くなりますcornersjcが、同じ結果、(1、0、1、1、0)のようなタプルのリストが生成されます。タイミングの例を次に示しcornersjpますcornersjc

In [93]: for n in range(5,13):
    %timeit [x for x in cube1s.cornersjp(n)]
   ....:     
10000 loops, best of 3: 49.3 us per loop
10000 loops, best of 3: 64.9 us per loop
10000 loops, best of 3: 117 us per loop
10000 loops, best of 3: 178 us per loop
1000 loops, best of 3: 351 us per loop
1000 loops, best of 3: 606 us per loop
1000 loops, best of 3: 1.28 ms per loop
100 loops, best of 3: 2.74 ms per loop
于 2013-01-18T21:29:01.453 に答える