14

次のマトリックスがあるとします。

元のマトリックス

マトリックスは、すべての行について、各チャンクが同じ数の列を持つ必要があるように、チャンクに分割できます。この場合、その行の値は true とマークされます。

たとえば、次のチャンクは有効です。

有効なチャンクの例 1

これは、行が連続している必要がないことを意味します。

以下は有効なチャンクであるため、列が連続している必要はありません。

有効なチャンクの例 2

ただし、以下は無効です。

無効なチャンクの例

とはいえ、すべてのチャンクを見つけるときに最小数のチャンクが使用されるように、チャンクを選択するために使用できるアルゴリズムは何ですか?

上記の例を考えると、適切な解決策は次のとおりです (同じ色のアイテムは有効なチャンクを表します)。

チャンキング問題の解決策 1

上記の例では、これを分割できるチャンクの最小数は 3 です。

以下も有効な解決策であることに注意してください。

チャンキング問題 2 の解決策

実際には、最小数のチャンクを取得するためだけに、ソリューションを優先する必要はありません。

隣接するセルを使用してカウントすることを考えましたが、それは列の値が連続している必要がないという事実を説明していません。

重要なのは、制約が与えられたときに最大の面積を持つチャンクを見つけ、それらのアイテムを削除してから繰り返すことにあると思います。

そのアプローチを取ると、解決策は次のとおりです。

チャンキング問題 3 の解決策

しかし、マトリックスをトラバースして最大の領域を見つける方法は、私にはわかりません。

また、操作中に行や列を再シャッフルしたい場合、それは有効な操作です(最大の領域を見つけるため)が、最大の領域を削除した後にのみ実行できると思いますマトリックス (1 つの領域が見つかり、次の領域に移動した後)。

4

3 に答える 3

2

真理値表で回路の最小化を行っています。4x4 真理値表の場合、K マップを使用できます。Quine-McCluskey アルゴリズムは、より大きな真理値表を処理できる一般化です。

この問題は NP 困難であるため、真理値表のサイズによっては、この問題がすぐに手に負えないほど大きくなる可能性があることに注意してください。

于 2013-06-18T21:59:16.483 に答える
0

私が提案する解決策はかなり簡単ですが、非常に時間がかかります。

それは4つの主要なステップで分解することができます:

  1. マトリックス内の既存のパターンをすべて見つけ、
  2. これらのパターンのすべての可能な組み合わせを見つけ、
  3. 不完全なパターンセットをすべて削除し、
  4. 残りのリストをスキャンして、最小数の要素を持つセットを取得します

まず、以下のアルゴリズムは、列または行の主要な行列のいずれかで機能します。説明のために列を選択しましたが、プロセス全体で一貫している限り、都合の良いときに行と入れ替えることができます。

答えに付随するサンプルコードはOCamlにありますが、言語の特定の機能を使用していないため、他のML方言に簡単に移植できるはずです。

ステップ1:

各列はビットベクトルとして見ることができます。パターン(質問でチャンクと呼ぶもの)は、すべての列、またはそれを構成するすべての行、あるいはそれらの組み合わせを交差させる(つまり、 ingする)ことによって構築できることに注意してください。したがって、最初のステップは、実際には、行と列のすべての組み合わせ(必要に応じて、行列の行と列のべき集合)を生成し、それらを同時に交差させ、重複を除外することです。

行列データ型については、次のインターフェイスを検討します。

 module type MATRIX = sig
    type t
    val w : int (* the width of the matrix *)
    val h : int  (* the height ........             *)
    val get : t -> int -> int -> bool  (* cell value getter *)

end

それでは、このステップのコードを見てみましょう。

let clength = M.h
let rlength = M.w

(* the vector datatype used throughought the algorithm
   operator on this type are in the module V *)
type vector = V.t

(* a pattern description and comparison operators *)
module Pattern = struct
    type t = {
        w : int; (* width of thd pattern *)
        h : int; (* height of the pattern *)
        rows : vector; (* which rows of the matrix are used *)
        cols : vector; (* which columns... *)
    }
    let compare a b = Pervasives.compare a b
    let equal a b = compare a b = 0
end
(* pattern set : let us store patterns without duplicates *)
module PS = Set.Make(Pattern)

(* a simple recursive loop on @f @k times *)
let rec fold f acc k =
    if k < 0 
    then acc
    else fold f (f acc k) (pred k)

(* extract a column/row of the given matrix *)
let cr_extract mget len =
    fold (fun v j -> if mget j then V.set v j else v) (V.null len) (pred len)

let col_extract m i = cr_extract (fun j -> M.get m i j) clength
let row_extract m i = cr_extract (fun j -> M.get m j i) rlength

(* encode a single column as a pattern *)
let col_encode c i =
    { w = 1; h = count c; rows = V.set (V.null clength) i; cols = c }

let row_encode r i =
    { h = 1; w = count r; cols = V.set (V.null rlength) i; rows = r }

(* try to add a column to a pattern *)
let col_intersect p c i =
    let col = V.l_and p.cols c in
    let h = V.count col in
    if h > 0 
    then
        let row = V.set (V.copy p.rows) i in
        Some {w = V.count row; h = h; rows = row; clos = col}
    else None

let row_intersect p r i =
    let row = V.l_and p.rows r in
    let w = V.count row in
    if w > 0
    then
        let col = V.set (V.copy p.cols) i in
        Some { w = w; h = V.count col; rows = row; cols = col }
    else None

let build_patterns m =
    let bp k ps extract encode intersect =
        let build (l,k) =
            let c = extract m k in
            let u = encode c k in
            let fld p ps =
                match intersect p c k with
                      None         -> l
                    | Some npc -> PS.add npc ps
             in
             PS.fold fld (PS.add u q) q, succ k
        in
        fst (fold (fun res _ -> build res) (ps, 0) k)
    in
    let ps = bp (pred rlength) PS.empty col_extract col_encode col_intersect in
    let ps = bp (pred clength) ps row_extract row_encode row_intersect in
    PS.elements ps

Vモジュールは、アルゴリズム全体で次の署名に準拠する必要があります。

module type V = sig
    type t
    val null : int -> t  (* the null vector, ie. with all entries equal to false *)
    val copy : t -> t (* copy operator *)
    val get : t -> int -> bool (* get the nth element *)
    val set : t -> int -> t (* set the nth element to true *)
    val l_and : t -> t -> t (* intersection operator, ie. logical and *)
    val l_or : t -> t -> t (* logical or *)
    val count : t -> int (* number of elements set to true *)
    val equal : t -> t -> bool (* equality predicate *)
end

ステップ2:

パターンの組み合わせは、いくつかの制限がありますが、パワーセット構造と見なすこともできます。有効なパターンセットには、重複しないパターンのみを含めることができます。後者は、両方に少なくとも1つの共通のマトリックスセルが含まれている場合、2つのパターンに対してtrueとして定義できます。上記で使用したパターンデータ構造を使用すると、オーバーラップ述語は非常に単純になります。

let overlap p1 p2 =
    let nullc = V.null h
    and nullr = V.null w in
    let o v1 v2 n = not (V.equal (V.l_and v1 v2) n) in
    o p1.rows p2.rows nullr && o p1.cols p2.cols nullc

パターンレコードのcolsとは、マトリックス内のどの座標がパターンに含まれるかを示します。rowsしたがって、論理と両方のフィールドで、パターンが重複しているかどうかがわかります。

パターンセットにパターンを含めるには、そのパターンがセットのどのパターンとも重ならないようにする必要があります。

type pset = {
    n : int; (* number of patterns in the set *)
    pats : pattern list;
}

let overlap sp p =
    List.exists (fun x -> overlap x p) sp.pats

let scombine sp p =
    if overlap sp p
    then None
    else Some {
        n = sp.n + 1;
        pats = p::sp.pats;
    }

let build_pattern_sets l =
    let pset l p = 
        let sp = { n = 1; pats = [p] } in
        List.fold_left (fun l spx -> 
            match scombine spx p with
                None -> l
             | Some nsp -> nsp::l
        ) (sp::l) l
    in List.fold_left pset [] l

このステップでは多くのセットが生成されるため、メモリと計算に非常に負荷がかかります。これは確かにこのソリューションの弱点ですが、フォールドを減らす方法はまだわかりません。

ステップ3:

パターンセットを使用してマトリックスを再構築するときに、元のパターンを取得できない場合、パターンセットは不完全です。したがって、プロセスはかなり単純です。

let build_matrix ps w =
    let add m p =
        let rec add_col p i = function
            | []     -> []
            | c::cs -> 
                let c = 
                    if V.get p.rows i
                    then V.l_or c p.cols
                    else c
                in c::(add_col p (succ i) cs)
        in add_col p 0 m
    in
    (* null matrix as a list of null vectors *)
    let m = fold (fun l _ -> V.null clength::l) [] (pred rlength) in
    List.fold_left add m ps.pats

let drop_incomplete_sets m l =
    (* convert the matrix to a list of columns *)
    let m' = fold (fun l k -> col_extract m k ::l) [] (pred rlength) in
    let complete m sp =
        let m' = build_matrix sp in
        m = m'
    in List.filter (fun x -> complete m' x) l

ステップ4:

最後のステップは、要素の数が最も少ないセットを選択することです。

let smallest_set l =
    let smallest ps1 ps2 = if ps1.n < ps2.n then ps1 else ps2 in
    match l with
        | []    -> assert false (* there should be at least 1 solution *)
        | h::t  -> List.fold_left smallest h t

全体の計算は、各ステップの連鎖にすぎません。

let compute m =
   let (|>) f g = g f in
   build_patterns m |> build_pattern_sets |> drop_incomplete_sets m |> smallest_set

ノート

上記のアルゴリズムは、いくつかの制限されたフィルタリングを使用して、べき集合のべき集合を構築します。検索を減らす方法を私が知っている限りではありません(コメントで述べられているように、これがNP困難な問題である場合、何もありません)。

このアルゴリズムは、考えられるすべての解決策をチェックし、最適な解決策を正しく返します(問題の説明に記載されているものを含む多くの行列でテストされています。

質問で提案するヒューリスティックに関する簡単なコメント:

最初のステップを使用して、見つかった最大のパターンを削除し、繰り返し実行することで、簡単に実装できます。それは私のアルゴリズムよりもはるかに迅速に解決策を生み出すでしょう。ただし、見つかった解決策は最適ではない可能性があります。

たとえば、次のマトリックスについて考えてみます。

.x...
.xxx
xxx.
...x.

中央の4セルチャンクは最も大きいものですが、それを使用したセットは合計5つのパターンで構成されます。

.1...
.223
422.
...5.

しかし、このソリューションは4つしか使用しません。

.1...
.122
334.
...4.

アップデート:

この回答のために私が書いた完全なコードへのリンク。

于 2013-01-19T15:30:59.457 に答える