web-dev-qa-db-ja.com

リストは(潜在的に)他のユーザーによって割り切れますか?

問題

整数のA = [a_1, a_2, ..., a_n]B = [b_1, b_2, ..., b_n]の2つのリストがあるとします。すべてのAについてa_ib_iで割り切れるBの順列がある場合、Biによってpotentially-divisibleと言います。問題は次のとおりです。すべてのBに対してa_ib_iで割り切れるように、iを並べ替える(つまり、並べ替える)ことは可能ですか?たとえば、あなたが持っている場合

A = [6, 12, 8]
B = [3, 4, 6]

TrueB = [3, 6, 4]に並べ替えることができるので、答えはBになります。そしてa_1 / b_1 = 2a_2 / b_2 = 2、およびa_3 / b_3 = 2があります。これらはすべて整数であるため、ABによって割り切れる可能性があります。

Falseを出力する必要がある例として、次のものがあります。

A = [10, 12, 6, 5, 21, 25]
B = [2, 7, 5, 3, 12, 3]

これがFalseである理由は、25と5がBにあるため、Aを並べ替えることができないためですが、Bの唯一の除数は5なので、1つは省略されます。

アプローチ

明らかに、簡単なアプローチは、Bのすべての順列を取得し、potential-divisibilityを満たすかどうかを確認することです。

import itertools
def is_potentially_divisible(A, B):
  perms = itertools.permutations(B)
  divisible = lambda ls: all( x % y == 0 for x, y in Zip(A, ls))
  return any(divisible(perm) for perm in perms)

質問

リストが別のリストで潜在的に分割可能かどうかを知るための最速の方法は何ですか?何かご意見は? primesでこれを行う巧妙な方法があるかと思っていましたが、解決策を思い付くことができませんでした。

とても有難い!


Edit:それはおそらくあなたのほとんどとは無関係でしょうが、完全を期すために、私は私の動機を説明します。グループ理論では、すべての文字の程度が対応するクラスサイズを分割するように、グループの既約文字と共役クラスから全単射があるかどうかに関する有限の単純グループに関する推測があります。たとえば、U6(4)ABは次のようになります。 かなり大きなリストです。

67
McGuire

二部グラフ構造の構築-接続a[i]からのすべての除数b[]enter image description here

次に maximum matching を見つけて、それが完全な一致であるかどうかを確認します(一致するエッジの数はペアの数(グラフが指示されている場合)または2倍の数に)。

任意の選択 ここでのKuhnアルゴリズムの実装

更新:
@ Eric Duminilが簡潔に作成しました Pythonの実装はこちら

このアプローチは、ブルートフォースアルゴリズムの階乗の複雑さに対する選択されたマッチングアルゴリズムとエッジ(分割ペア)の数に応じて、O(n ^ 2)からO(n ^ 3)までの多項式の複雑さを持ちます。

69
MBo

コード

@MBoの優れた answer に基づいて、ここに networkx を使用した2部グラフマッチングの実装があります。

_import networkx as nx

def is_potentially_divisible(multiples, divisors):
    if len(multiples) != len(divisors):
        return False

    g = nx.Graph()
    g.add_nodes_from([('A', a, i) for i, a in enumerate(multiples)], bipartite=0)
    g.add_nodes_from([('B', b, j) for j, b in enumerate(divisors)], bipartite=1)

    edges = [(('A', a, i), ('B', b, j)) for i, a in enumerate(multiples)
             for j, b in enumerate(divisors) if a % b == 0]
    g.add_edges_from(edges)
    m = nx.bipartite.maximum_matching(g)
    return len(m) // 2 == len(multiples)

print(is_potentially_divisible([6, 12, 8], [3, 4, 6]))
# True
print(is_potentially_divisible([6, 12, 8], [3, 4, 3]))
# True
print(is_potentially_divisible([10, 12, 6, 5, 21, 25], [2, 7, 5, 3, 12, 3]))
# False
_

ノート

ドキュメント によると:

Maximum_matching()によって返される辞書には、左右の頂点セットの両方の頂点のマッピングが含まれます。

つまり、返される辞書はAおよびBの2倍の大きさでなければなりません。

ノードはから変換されます

_[10, 12, 6, 5, 21, 25]
_

に:

_[('A', 10, 0), ('A', 12, 1), ('A', 6, 2), ('A', 5, 3), ('A', 21, 4), ('A', 25, 5)]
_

ABのノード間の衝突を避けるため。 IDは、重複した場合にノードを区別できるようにするためにも追加されます。

効率

_maximum_matching_メソッドは Hopcroft-Karpアルゴリズム を使用します。これは最悪の場合O(n**2.5)で実行されます。グラフ生成はO(n**2)なので、メソッド全体がO(n**2.5)で実行されます。大きな配列でも問題なく動作するはずです。置換ソリューションはO(n!)であり、20個の要素を持つ配列を処理できません。

図付き

最適な一致を示す図に興味がある場合は、matplotlibとnetworkxを混在させることができます。

_import networkx as nx
import matplotlib.pyplot as plt

def is_potentially_divisible(multiples, divisors):
    if len(multiples) != len(divisors):
        return False

    g = nx.Graph()

    l = [('l', a, i) for i, a in enumerate(multiples)]
    r = [('r', b, j) for j, b in enumerate(divisors)]

    g.add_nodes_from(l, bipartite=0)
    g.add_nodes_from(r, bipartite=1)

    edges = [(a,b) for a in l for b in r if a[1] % b[1]== 0]
    g.add_edges_from(edges)

    pos = {}
    pos.update((node, (1, index)) for index, node in enumerate(l))
    pos.update((node, (2, index)) for index, node in enumerate(r))

    m = nx.bipartite.maximum_matching(g)
    colors = ['blue' if m.get(a) == b else 'gray' for a,b in edges]

    nx.draw_networkx(g, pos=pos, arrows=False, labels = {n:n[1] for n in g.nodes()}, Edge_color=colors)
    plt.axis('off')
    plt.show()

    return len(m) // 2 == len(multiples)

print(is_potentially_divisible([6, 12, 8], [3, 4, 6]))
# True
print(is_potentially_divisible([6, 12, 8], [3, 4, 3]))
# True
print(is_potentially_divisible([10, 12, 6, 5, 21, 25], [2, 7, 5, 3, 12, 3]))
# False
_

対応する図は次のとおりです。

enter image description hereenter image description hereenter image description here

30
Eric Duminil

あなたは数学に慣れているので、他の答えに光沢を加えたいだけです。検索する用語はboldに示されています。

問題は、制限された位置の順列のインスタンスであり、それらについて言えることはたくさんあります。一般に、最初に位置NxNが要素に許可されている場合に限り、ゼロ(1)Mマトリックスjを構築できます。ここで_M[i][j]_は1です。 i。すべての制限を満たす個別の順列のnumberは、Mpermanentです(ただし、行列式と同じ方法で定義されますが、すべての項は負ではありません)。

悲しいかな-行列式とは異なり-Nには指数関数よりも速く恒久的に計算する一般的な方法はありません。ただし、パーマネントが0であるかどうかを判断するための多項式時間アルゴリズムがあります。

そして、それがあなたが得た答えですstart ;-)ここに、「パーマネント0ですか?」の良い説明があります。質問は、2部グラフで完全な一致を考慮することにより効率的に回答されます。

https://cstheory.stackexchange.com/questions/32885/matrix-permanent-is-

そのため、実際には、@ Eric Duminilが答えで与えたよりも一般的なアプローチが早く見つかることはまずありません。

注、後で追加:最後の部分を明確にする必要があります。 「制限付き順列」行列Mがあれば、それに対応する整数の「分割リスト」を簡単に作成できます。したがって、特定の問題は一般的な問題よりも簡単ではありません-リストにどの整数が表示されるかについて特別な何かがある場合を除きます。

たとえば、M

_0 1 1 1
1 0 1 1
1 1 0 1
1 1 1 0
_

最初の4つの素数を表す行を表示します。これらは、Bの値でもあります。

_B = [2, 3, 5, 7]
_

最初の行は、B[0] (= 2)は_A[0]_を分割できないが、_A[1]_、_A[2]_、および_A[3]_を分割する必要があることを「言います」。等々。建設により、

_A = [3*5*7, 2*5*7, 2*3*7, 2*3*5]
B = [2,     3,     5,     7]
_

Mに対応します。また、Bの各要素が、置換されたAの対応する要素で割り切れるように、Bを並べ替えるpermanent(M) = 9の方法があります。

17
Tim Peters

これは究極の答えではありませんが、これは価値のあることだと思います。最初にリスト[(1,2,5,10),(1,2,3,6,12),(1,2,3,6),(1,5),(1,3,7,21),(1,5,25)]内のすべての要素の要因(1とそれ自体を含む)をリストできます。私たちが探しているリストには、(均等に分割するための)要因の1つが必要です。リストにはいくつかの要素がないため、(_[2,7,5,3,12,3]_)に対してチェックを行います。このリストは、さらに次のようにフィルタリングできます。

[(2,5),(2,3,12),(2,3),(5),(3,7),(5)]

ここでは、5つの場所が2つ必要です(オプションはまったくありません)が、5つしかないので、ここでほとんど停止して、ここでケースがfalseであると言えます。

代わりに_[2,7,5,3,5,3]_があったとしましょう:

次に、そのようなオプションがあります:

[(2,5),(2,3),(2,3),(5),(3,7),(5)]

5は2つの場所で必要です。

[(2),(2,3),(2,3),{5},(3,7),{5}]ここで、_{}_は確保された位置を示します。

また、2が保証されます。

[{2},(2,3),(2,3),{5},(3,7),{5}] 2が使用されるため、3の2つの場所が確保されます。

[{2},{3},{3},{5},(3,7),{5}]コース3が採用され、7が保証されます。

_[{2},{3},{3},{5},{7},{5}]_。これはまだリストと一致しているため、ケースは正しいです。繰り返しを行うたびに、リストの一貫性を確認しなければならないことを忘れないでください。

3
officialaimm

これを試すことができます:

import itertools

def potentially_divisible(A, B):
    A = itertools.permutations(A, len(A))
   return len([i for i in A if all(c%d == 0 for c, d in Zip(i, B))]) > 0

l1 = [6, 12, 8]
l2 = [3, 4, 6]

print(potentially_divisible(l1, l2))

出力:

True

もう一つの例:

l1 = [10, 12, 6, 5, 21, 25]
l2 = [2, 7, 5, 3, 12, 3]

print(potentially_divisible(l1, l2))

出力:

False
2
Ajax1234