13

学術的な質問だと思いますが、2番目の結果は私には意味がありません。最初のように完全に空にすべきではありませんか?この動作の理論的根拠は何ですか?

from itertools import product

one_empty = [ [1,2], [] ]
all_empty = []

print [ t for t in product(*one_empty) ]  # []
print [ t for t in product(*all_empty) ]  # [()]

更新

すべての回答に感謝します-非常に有益です。

ウィキペディアのヌルデカルト積に関する議論は、決定的なステートメントを提供します。

セットなしのデカルト積...は、空のタプルを含むシングルトンセットです。

そして、 sthからの洞察に満ちた答えを処理するために使用できるいくつかのコードがあります:

from itertools import product

def tproduct(*xss):
    return ( sum(rs, ()) for rs in product(*xss) )

def tup(x):
    return (x,)

xs = [ [1, 2],     [3, 4, 5]       ]
ys = [ ['a', 'b'], ['c', 'd', 'e'] ]

txs = [ map(tup, x) for x in xs ]  # [[(1,), (2,)], [(3,), (4,), (5,)]]
tys = [ map(tup, y) for y in ys ]  # [[('a',), ('b',)], [('c',), ('d',), ('e',)]]

a = [ p for p in tproduct( *(txs + tys) )                   ]
b = [ p for p in tproduct( tproduct(*txs), tproduct(*tys) ) ]

assert a == b
4

2 に答える 2

10

数学的な観点から、要素がない場合の積は、それが何であれ、操作の中立要素を生成する必要があります。

たとえば、整数の場合、すべての整数aに対して1⋅a= aであるため、乗算の中立要素は1です。したがって、整数の空積は1である必要があります。数値のリストの積を返すPython関数を実装する場合、これは自然に発生します。

def iproduct(lst):
  result = 1
  for i in lst:
    result *= i
  return result

このアルゴリズムで正しい結果を計算するには、resultをで初期化する必要があります11これにより、関数が空のリストで呼び出されたときの戻り値が得られます。

この戻り値は、関数の目的にとっても非常に合理的です。優れた製品関数を使用すると、最初に2つのリストを連結してから要素の製品を作成するか、最初に両方の個別のリストの製品を作成してから結果を乗算するかは重要ではありません。

iproduct(xs + ys) == iproduct(xs) * iproduct(ys)

xsまたはysが空の場合、。の場合にのみ機能しますiproduct([]) == 1

product()イテレータがより複雑になりました。ここでも、数学的な観点から、product([])それが何であれ、その操作の中立要素を返す必要があります。それ[]以来product([], xs) == []ではありませんが、中立的な要素product([], xs) == xsが保持されるべきです。ただし、それ[()]も中立的な要素ではないことがわかります。

>>> list(product([()], [1,2,3]))
[((), 1), ((), 2), ((), 3)]

実際、product()上記の方程式は成り立たないため、実際には非常に優れた数学製品ではありません。

product(*(xs + ys)) != product(product(*xs), product(*ys))

製品のアプリケーションごとにタプルの追加レイヤーが生成され、それを回避する方法がないため、真のニュートラル要素すら存在できません。[()]ただし、要素は追加または削除されず、それぞれに空のタプルが追加されるだけです。

[()]実際には、タプルのリストでのみ動作し、各アプリケーションにタプルレイヤーを追加しない、このわずかに適応された製品関数の中立的な要素になります。

def tproduct(*xss):
  # the parameters have to be lists of tuples
  return (sum(rs, ()) for rs in product(*xss))

この関数の場合、上記の積方程式が成り立ちます。

def tup(x): return (x,)
txs = [map(tup, x) for x in xs]
tys = [map(tup, y) for y in ys]
tproduct(*(txs + tys)) == tproduct(tproduct(*txs), tproduct(*tys))

入力リストをタプルにパックする追加の前処理ステップを使用tproduct()すると、と同じ結果が得られますproduct()が、数学的な観点からはより適切に動作します。また、その中立的な要素は[()]

したがって[()]、この種のリスト乗算の中立要素としては理にかなっています。正確に適合しない場合でもproduct()、この関数には適しています。たとえばtproduct()、空の入力に特別なケースを導入することなく定義できるためです。

于 2010-07-01T01:49:03.367 に答える
3

@sthがすでに示したように、この動作は数学的な観点からは正しいものです。list(itertools.product())その要素がどうあるべきかが明確になったら、(一貫性を保つために)長さ0のタプルでなければならず、そのうちの1つしかないため、実際に納得する必要があるのは、要素が1つだけである必要があるということだけです。

ただし、の要素の数は、、、、 ...itertools.product(l1, l2, l3, ...)の長さの積でなければなりません。したがって、の要素の数はの製品のサイズである必要があり、空の製品が1であることを説得するインターネットソースが不足することはありません。l1l2l3itertools.product()

これが正しい実用的な定義であり、正しい数学的定義であることを指摘したいと思います。つまり、境界の場合に「正しく機能する」可能性が最も高いのは定義です。たとえば、n最初の桁がゼロ以外の10進数で構成される長さのすべての文字列を生成するとします。あなたは次のようなことをするかもしれません:

import itertools

def decimal_strings(n):
    """Generate all digit strings of length n that don't start with 0."""
    for lead_digit in '123456789':
        for tail in itertools.product('0123456789', repeat=n-1):
            yield lead_digit + ''.join(tail)

これはいつ何を生成する必要がありn = 1ますか?ええと、その場合、あなたitertools.productは空の製品(repeat = 0)で呼び出すことになります。何も返されない場合、上記の内側のforループの本体は実行されないためdecimal_strings(1)、空のイテレータになります。ほぼ間違いなくあなたが望むものではありません。ただしitertools.product('0123456789', repeat=0)、単一のタプルが返されるため、期待される結果が得られます。

>>> list(decimal_strings(1))
['1', '2', '3', '4', '5', '6', '7', '8', '9']

n = 0もちろん、この関数が正しくValueErrorを発生させる場合。)

つまり、定義は数学的に正しいものであり、多くの場合、それはあなたが望むものでもありません。それは間違いなくPythonのバグではありません!

于 2010-07-01T08:58:40.427 に答える