各列が1つのTrueになる行を見つけます(以前はKnuthのアルゴリズムX)。


8

仕事

ブール行列を指定して、各列にTrueが1つだけある行の1つ(またはオプションで複数)のサブセットを見つけます。任意のアルゴリズムを使用できますが、前の例のように非常に大きな行列をサポートする必要があります。

可能なアルゴリズムの1つ(KnuthのアルゴリズムX

このアルゴリズムを使用する必要はありませんが、これが最善のオプションになる場合があります。

  1. 行列Aに列がない場合、現在の部分解は有効な解です。正常に終了します。
  2. それ以外の場合は、列cを選択します。
  3. A rc = 1となるような行rを選択します。
  4. rを部分解に含めます。
  5. 各列のためのJようにAのRJ = 1、
     行ごとにIようにA 、IJ = 1、
      削除行I行列からA。行列Aから
     列jを削除します。
  6. 削減された行列Aに対してこのアルゴリズムを再帰的に繰り返します。

*ステップ3は非決定的であり、後でステップ3を呼び出して行を見つけられなかった場合に、バックトラックする必要があります。

入力

最小の2×2行列Aの任意の表現、たとえば数値またはブール配列

1 0 0 1 0 0 1
1 0 0 1 0 0 0
0 0 0 1 1 0 1
0 0 1 0 1 1 0
0 1 1 0 0 1 1
0 1 0 0 0 0 1

またはユニバース+セットコレクションとして

U = {1, 2, 3, 4, 5, 6, 7}
S = {
    A = [1, 4, 7],
    B = [1, 4],
    C = [4, 5, 7],
    D = [3, 5, 6],
    E = [2, 3, 6, 7],
    F = [2, 7]
    }

または0または1のインデックス付きセット。 {{1, 4, 7}, {1, 4}, {4, 5, 7}, {3, 5, 6}, {2, 3, 6, 7}, {2, 7}}

出力

選択された行の数値またはブール配列など、1つ(またはオプションで複数/すべて)の解の任意の表現

1 0 0 1 0 0 0
0 0 1 0 1 1 0
0 1 0 0 0 0 1

または、選択された行を示すブールリスト、{0, 1, 0, 1, 0, 1}または選択された行の数値(0または1インデックス付き)リスト、{2, 4, 6}またはセット名のリストとして['B', 'D', 'F']

その他の例

に:

1 0 1
0 1 1
0 1 0
1 1 1

Out: 1 3または4or 1 0 1 0または0 0 0 1or [[1,3],[4]など。


に:

1 0 1 0 1
0 1 0 1 0
1 1 0 0 1
0 1 0 1 1

アウト: 1 1 0 0など


に:

0 1 0 1 1 0 1
1 1 0 0 1 1 1
0 1 0 0 1 0 0
1 1 1 0 0 0 1
0 0 0 1 1 1 0

アウト: 0 0 0 1 1など


に:

0 1 1
1 1 0

Out:何もないか、エラーまたは不完全なソリューション。つまり、ソリューションなしで入力を処理する必要はありません。


に: http : //pastebin.com/raw/3GAup0fr

アウト: 0 10 18 28 32 38 48 61 62 63 68 86 90 97 103 114 120 136 148 157 162 174 177 185 186 194 209 210 218 221 228 243 252 255 263 270 271 272 273 280 291 294 295 309 310 320 323 327 339 345 350 353 355 367 372 373 375 377 382 385 386 389 397 411 417 418 431 433 441 451 457 458 459 466 473 479 488 491 498 514 517


に: https : //gist.github.com/angs/e24ac11a7d7c63d267a2279d416bc694

アウト: 553 2162 2710 5460 7027 9534 10901 12281 12855 13590 14489 16883 19026 19592 19834 22578 25565 27230 28356 29148 29708 30818 31044 34016 34604 36806 36918 39178 43329 43562 45246 46307 47128 47906 48792 50615 51709 53911 55523 57423 59915 61293 62087 62956 64322 65094 65419 68076 70212 70845 71384 74615 76508 78688 79469 80067 81954 82255 84412 85227


4
このアルゴリズムを使用するソリューションのみが勝つ資格があります」が、「このアルゴリズム」と正確に何がカウントされるのですか どのように文字通りには「取る必要がある、削除行」と「削除列を」?そして、アルゴリズムは、アルゴリズムのKnuthのプレゼンテーションの重要な部分ですが、説明でまったく言及されていないヒューリスティックを使用する必要がありますか?
Peter Taylor

6
正確なセットカバーのみを要求するが、単純なブルートフォースでは処理できず、Knuthのアルゴリズムでは処理できない高額なテストケースがある質問をする方がよいでしょう。
Peter Taylor、


1
すべてのアルゴリズムが等しく許可されます。
アダム・

1
「非常に大きなマトリックスをサポートする必要があります」は、あいまいです。特に、Knuthのアルゴリズムは、列選択ヒューリスティックなしでは大きなテストケースを処理できないためです。たぶん、この質問は純粋なコードゴルフとして、別の質問は最速コードですか?
Angs

回答:


5

Haskell、100 93 92 87 83 80バイト

クヌースのアルゴリズム:

g&c=filter(g.any(`elem`c))
(u:v)%a=[c:s|c<-id&u$a,s<-(not&c)v%(not&c$a)]
x%_=[x]

リストモナドのすべてのカバーを非決定的に深さ優先で計算します。headHaskellは怠惰なので、1つだけを計算するために使用します。使用法:

*Main> [[1],[2],[3],[4],[5],[6],[7]]%[[1, 4, 7], [1, 4], [4, 5, 7], [3, 5, 6], [2, 3, 6, 7], [2, 7]]
[[[1,4],[2,7],[3,5,6]]]

Knuthが提案する最小の列を選択するという提案を使用して速度を上げるには、これを使用します(115バイト、ユニバースのフラットリスト)。コンパイルすると、1分以内に大きなペントミノカバー問題の最初の解決策を見つけます。

import Data.List
[]%_=[[]]
w%a=[c:s|c<-a,c\\(head.sortOn length.group.sort$w:a>>=id)/=c,s<-(w\\c)%[b|b<-a,c\\b==c]]

1 + 3バイトを節約してくれた@Zgarbに感謝!

賢明なアドバイスをしてくれて5バイトとそれを節約してくれた@ChristianSieversに感謝します。

Ungolfed(フラットリストユニバースを使用):

knuthX [] _ = [[]]
knuthX (u:niverse) availableRows = --u chosen deterministically
  [ chosen:solution
  | let rows = filter (elem u) availableRows
  , chosen <- rows  --row chosen nondeterministically in list monad
  , solution <- knuthX
                  (filter (not.(`elem`chosen)) niverse)
                  (filter (not.any(`elem`chosen)) availableRows)
  ]

Haskellに不慣れな人にとって、それがどのように機能するかについての説明を追加する価値があるかもしれません。非決定論を処理するためにListモナドを使用していると思いますか?

filterリスト内包表記に変更し、補助関数を定義することで、3バイトを節約できますh!x=h(`elem`(x>>=id))
Zgarb 2016年

1
私は単相性制限に実行しているよ@ChristianSievers (!)=elem、それゆえaさん。そして、はい、fは間違いなくそこで使用できます。ありがとう!filter … elem統合する
頼み

1
フラットユニバースに戻り、一般的に高速であるはずのバージョンを使用しますが、大きな例では違いがなく、を使用していくつかのバイトを節約できますw%a=[c:s|(u:_)<-[sortOn length.group.sort$w++concat a],c<-id&u$a,s<-(w\\c)%(not&c$a)]。これuは、選択された列を繰り返し含む可能性のあるリストですが、正確さには関係ありません。
Christian Sievers

1
@ChristianSieversフーレイ、遅いから速いまで+ 50%未満の長さ!でu要素ごとに1回計算されるため、インライン化すると速度にわずかな後退がありましたが、a最適速度ではなくゴルフ速度を目指しています。c\\b==cゆっくりと止まってしまうので、おそらくそれほど悪くはありません。インライン化せず、最も希少な要素を見つけるためにu使用Data.Map.Strictすることは、まったく異なるレベルになります。
Angs '26 / 11/16

1

Python、482バイト

r=lambda X,Y:u({j:set(filter(lambda i:j in Y[i],Y))for j in X},Y)
def u(X,Y,S=[]):
 if X:
  for r in list(X[min(X,key=lambda c:len(X[c]))]):
   S.append(r);C=v(X,Y,r)
   for s in u(X,Y,S):yield s
   w(X,Y,r,C);S.pop()
 else:yield list(S)
def v(X,Y,r):
 C=[]
 for j in Y[r]:
  for i in X[j]:
   for k in Y[i]:
    if k!=j:X[k].remove(i)
  C.append(X.pop(j))
 return C
def w(X,Y,r,C):
 for j in reversed(Y[r]):
  X[j]=C.pop()
  for i in X[j]:
   for k in Y[i]:
    if k!=j:X[k].add(i)

r Universe + Setコレクションで呼び出す関数です。

このページから少しゴルフをした

使用法:

X = {1, 2, 3, 4, 5, 6, 7}
Y = {
    'A': [1, 4, 7],
    'B': [1, 4],
    'C': [4, 5, 7],
    'D': [3, 5, 6],
    'E': [2, 3, 6, 7],
    'F': [2, 7]}

for a in r(X,Y): print a

あなたはにキャストを取り除くことができるはずlistループや歩留まりのための最初の、そして変化reversed(...)...[::-1]
ブルー

使用S.append(x)するたびにS+=x,(末尾のコンマを使用して)次のように実行できますC+=X.pop(j),
FlipTack 2016年

1

R、124 117 115113バイト

非常に非効率的ですが、コードはそれほど長くありません。行のすべての可能なサブセットを試行し、すべての行の合計が1かどうかを確認します。

f=function(x,n=nrow(x))for(i in 1:n)for(j in 1:ncol(z<-combn(n,i)))if(all(colSums(x[k<-z[,j],,drop=F])==1))cat(k)

入力として行列を受け取ります。最初に見つかったソリューションの行番号を返します。解がない場合は何も返しませんが、入力が大きい場合は終了に時間がかかる場合があります。

未ゴルフ:

f=function(x, n=nrow(x)){


    for(i in 1:n){
        z=combn(n,i)

        for(j in 1:ncol(z)){
            k=x[z[,j],,drop=F]

            if(all(colSums(k)==1)){
                print(z[,j])
            }
        }
    }
}

Billywobのおかげで7バイト節約

Billywobのおかげでさらに2バイト節約


ありがとう、そのようにインラインで変数を割り当てることができるとは知りませんでした。また、drop=Fステートメントを削除しても、1行のみのサブセットでは機能しません。私はこれまで一緒scanに仕事をしたことはありませんでした。名前付き関数に変更します。
JAD、2016年

また、出力をソリューションのインデックスを返すように変更し、さらに2バイトを節約しました。
JAD、2016年

一般に、名前付き関数を使用する必要はありません(ネストされている場合を除く)。あなたが関数の暗黙の引数として行カウンタを割り当てる場合も、あなたは再び中括弧を省略することができます:function(x,n=nrow(x))for(i in 1:n)for(j in 1:ncol(z<-combn(n,i)))if(all(colSums(x[k<-z[,j],,drop=F])==1))cat(k)
Billywob

ああ、そうしようと思ったのですがn、どういうわけか頭がおかしくなりました。ありがとうございました:)
JAD 2016年

0

APL(Dyalog)、81バイト

{×≢⍉⍵:⍵∇{~1∊⍵:00s←⍺⍺c/⍺⌿⍨r←~∨/⍺/⍨~c←~,⍺⌿⍨f←<\⍵:⍺∇f<⍵⋄fr\s},⍵/⍨<\n=⌊/n←+⌿⍵⋄∧/⍵}

オンラインでお試しください!

{ 無名関数:

: もし…

   引数が

   転置したとき

   行数があります

  × それはポジティブです

 その後

  ⍵∇{…次に、この関数を右の引数を左の引数(制約行列)として適用しますが、次の無名演算子(高次関数)によって変更された後のみ: 右の引数に1が存在する
   : 場合 NOT    …  ゼロを返します(つまり、失敗する) else: if… 右の引数の 最初の(lit.累積より小さい、最初の行) をfに割り当て、それを 使用して左の引数の行をフィルタリングします ravel(flatten) negate  assign it to c  negate  それを使用して、左の引数の列をフィルタリングし、 どの行がtrueですか?(または削減)それを 否定する
    1∊⍵
    ~

    0
   
   :
    <\⍵
    f←
    ⍺⌿⍨
    ,
    ~
    c←
    ~
    ⍺/⍨
    ∨/
    ~
    ⍺⌿⍨ これを使用して左引数の行をフィルター処理し、c
    c/ 使用し て元の外部関数(左オペランド;サブマトリックスカバー)を適用する列をフィルター処理します。 これをsに割り当て ます…はゼロと同じ(失敗)で、次に(a異なる行): 右引数AND NOT F  再帰その(元の左引数を保存)の 他: で使用するゼロRにゼロで満たされた列を挿入するため  リターンF ORこと(成功;行F付属)... ...上
    ⍺⍺
    s←
    0≡
    f<⍵
    ⍺∇
   
    r\s
    f∨
  }

  +⌿⍵ 引数の合計

  n← それをnに割り当てる

  ⌊/ その最小

  n= ブール値。nはそれに等しい

  <\ それらの最初の1つ(lit.累積以下)

  ⍵/⍨ これを使用して、引数の列をフィルタリングします(最初の列に最も少ない列を与えます)。

  , ラヴェル(平坦化)

 そうしないと

  ∧/⍵ すべて1である行(なし、したがって、これは各行にゼロを与えます)

} 匿名関数の終わり

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