“有序电源”/“图色”集。

时间:2022-10-23 00:21:04

I want the following to be done in Ocaml but an answer in ex F# could give me enough insight to do the conversion myself.

我希望在Ocaml中完成以下任务,但在前f#中给出的答案可以让我有足够的洞察力来完成转换。

An ordered power set (from biggest set to the smallest) would make me one step further the problem below which is want I ideally want to be solved.

一个有序的幂集(从最大的集合到最小的集合)会使我更进一步,下面的问题是希望我理想的解决。

For a inefficient graph coloring, I need a function which gives me the following:

对于一个低效率的图着色,我需要一个函数,它给出如下:

f({a,b,c,d}):

{{a,b,c,d}}
{{a,b,c},{d}}
{{a,b,d},{c}}
{{a,c,d},{b}}
{{b,c,d},{a}}
{{a,b},{c,d}}
{{a,c},{b,d}}
{{a,d},{b,c}}
{{a},{b,c},{d}}
{{a},{b},{c,d}}
{{a},{b,d},{c}}
...
{{a},{b},{c},{d}}

as a list of sets (or better, as a lazy list/enum of sets)

作为集合的列表(或者更好,作为一个延迟列表/集合)

So I want all variables to be represented in some set. But I want it ordered so I get the one with fewest sets first and the one where all variables is in a set last.

我希望所有的变量都能在某个集合中表示出来,但我想要它是有序的,所以我先得到最少的集合,然后所有的变量都在最后一个集合中。

I've one solution which is something like this: f: Take powerset -> iterate -> apply f on the rest <- sort the whole list of possibilities

我有一个解决方案,它是这样的:f:取powerset ->迭代->在rest中应用f -对所有可能的列表进行排序。

But I would like to avoid sorting an exponential list. And hopefully I can do it with a lazy list so I avoid iterating all possibilities.

但我想避免对指数列表进行排序。希望我可以用一个懒惰的列表来做,所以我避免重复所有的可能性。

3 个解决方案

#1


2  

let rec comb n l =
  match n, l with
  | 0, _  -> [[]]
  | _, [] -> []
  | n, x::xs -> List.map (fun l -> x ::l) (comb (n - 1) xs) @ (comb n xs)

let listToSingleSetSet xs = List.map (Set.singleton) xs |> set

let set_2Item_merge (set_set:Set<Set<'T>>) =
  seq {
    let arX = Set.toArray set_set
    let choice_list = comb 2 [0..(arX.Length-1)]
    for [x; y] in choice_list do
      yield begin
        set_set
        |> Set.remove arX.[x]
        |> Set.remove arX.[y]
        |> Set.add (arX.[x] + arX.[y])
      end
  }

let partitions xs =
  let set_set = listToSingleSetSet xs
  let rec aux sq =
    let x = Seq.head sq
    if Set.count x = 1
    then
      Seq.singleton x
    else
      Seq.append sq (Seq.map set_2Item_merge sq |> Seq.concat |> Seq.distinct |> aux)
  aux <| Seq.singleton set_set

DEMO

演示

> partitions ['a'..'d'] |> Seq.iter (printfn "%A");;
set [set ['a']; set ['b']; set ['c']; set ['d']]
set [set ['a'; 'b']; set ['c']; set ['d']]
set [set ['a'; 'c']; set ['b']; set ['d']]
set [set ['a'; 'd']; set ['b']; set ['c']]
set [set ['a']; set ['b'; 'c']; set ['d']]
set [set ['a']; set ['b'; 'd']; set ['c']]
set [set ['a']; set ['b']; set ['c'; 'd']]
set [set ['a'; 'b'; 'c']; set ['d']]
set [set ['a'; 'b'; 'd']; set ['c']]
set [set ['a'; 'b']; set ['c'; 'd']]
set [set ['a'; 'c'; 'd']; set ['b']]
set [set ['a'; 'c']; set ['b'; 'd']]
set [set ['a'; 'd']; set ['b'; 'c']]
set [set ['a']; set ['b'; 'c'; 'd']]
set [set ['a'; 'b'; 'c'; 'd']]
val it : unit = ()

If you want reverse seq then ...

如果你想要反向的seq,那么…

Seq.append sq (Seq.map set_2Item_merge sq |> Seq.concat |> Seq.distinct |> aux)

change to

改变

Seq.append (Seq.map set_2Item_merge sq |> Seq.concat |> Seq.distinct |> aux) sq

result:

结果:

set [set ['a'; 'b'; 'c'; 'd']]
set [set ['a'; 'b'; 'c']; set ['d']]
set [set ['a'; 'b'; 'd']; set ['c']]
set [set ['a'; 'b']; set ['c'; 'd']]
set [set ['a'; 'c'; 'd']; set ['b']]
set [set ['a'; 'c']; set ['b'; 'd']]
set [set ['a'; 'd']; set ['b'; 'c']]
set [set ['a']; set ['b'; 'c'; 'd']]
set [set ['a'; 'b']; set ['c']; set ['d']]
set [set ['a'; 'c']; set ['b']; set ['d']]
set [set ['a'; 'd']; set ['b']; set ['c']]
set [set ['a']; set ['b'; 'c']; set ['d']]
set [set ['a']; set ['b'; 'd']; set ['c']]
set [set ['a']; set ['b']; set ['c'; 'd']]
set [set ['a']; set ['b']; set ['c']; set ['d']]

#2


6  

Here's an updated solution, given that the order of subsets is unimportant:

这里有一个更新的解决方案,因为子集的顺序不重要:

let rec splits = function
| [] -> Seq.singleton([],[])
| x::xs ->
    seq { 
        for l1,l2 in splits xs do
            yield x::l1,l2
            yield l1,x::l2
    }

let parts =
    let rec parts' = function
    | 0,[] -> Seq.singleton []
    | _,[] -> Seq.empty
    | 1,l -> Seq.singleton [l]
    | n,x::xs ->
        seq {
            for l1,l2 in splits xs do
            for p in parts'(n-1, l2) do
                yield (x::l1)::p
        }
    fun l -> seq { 
        for k = 1 to List.length l do 
            yield! parts'(k,l) 
    }

The idea here is pretty straightforward. The splits function gives all ways to partition a list into two sets. Then to calculate the set of partitions of a list x::xs we could go through each split of xs into l1,l2, and for each partition of l2, add x::l1 in front.

这里的想法很简单。拆分函数提供了将列表划分为两组的所有方法。然后计算一个列表x::xs的一组分区,我们可以把xs的每一个分割成l1、l2和l2的每个分区,在前面加x: l1。

However, this won't meet your ordering requirement, so we break the problem down just a little bit further, and use the nested function part' to calculate the partitions of a list l into exactly n pieces. Then we just need to iterate through these lists of partitions in order.

但是,这不会满足您的订购需求,因此我们将问题进一步细分,并使用嵌套的函数部分来计算列表l的分区。然后我们只需要依次遍历这些分区列表。

#3


2  

Here is a quick idea.

这里有一个快速的想法。

One well-known trick to lazily generate the powerset of a set of size N is to iterate each number from 0 to (2^N)-1, considering its binary representation. Each iteration produces a partition : you put the i-th element of the set in the current partition if the i-th digit of the current number is 1.

一个众所周知的方法是延迟生成一组大小为N的powerset,这是为了迭代每个数字从0到(2)-1,考虑到它的二进制表示。每个迭代产生一个分区:如果当前数字的第i个数字为1,则将设置的第i个元素放在当前分区中。

You can do the same in your case: a partition of P is given not by a binary number below 2^N, but by a number below N^N, in base N. Now the trick to get the partitions with the fewest component first is:

你可以做同样的例子:一个分区的P不是由一个二进制数低于2 ^ N,但很多低于N ^ N,N进制下现在的技巧先分区最少的组件是:

  • first iterate upto 2^N (this gives you the partitions of 2 components)
  • 第一次迭代高达2 ^ N(这给你的分区2组件)
  • then iterate upto 3^N, rejecting the numbers that don't have both a 0, a 1 and a 2 in their representation (this rules out the previously produced partitions)
  • 然后迭代到3 ^ N,拒绝不都一个0的数字,1和2表示(这排除了以前生产的分区)
  • then iterate upto 4^N, taking only numbers with all 4 distinct digits
  • 然后迭代upto 4,只取所有4个不同数字的数字。
  • etc... upto N^N
  • 等等……到N ^

Obviously this makes you manipulate quite large numbers. You do not need to encode them as integers/bignum. For example in the powerset case, a list of booleans is just as good. The same idea could be reused in the partition case. Moreover, the K-ary representation of two consecutive numbers are close, so you can probably cache some information to get a more efficient algorithm:

很明显,这会使你操作相当大的数字。您不需要将它们编码为整数/bignum。例如在powerset的例子中,布尔值的列表也一样好。同样的想法可以在分区案例中重用。另外,两个连续数字的K-ary表示是接近的,所以你可能可以缓存一些信息来得到一个更有效的算法:

  • you can cache a part of your current partitioning (say, you could represent the current partition as an array of lists, and just destructively update the few digits that change)
  • 您可以缓存当前分区的一部分(比如,您可以将当前分区表示为列表的数组,并对更改的几个数字进行破坏性的更新)
  • you can cache the information of which digits are currently present in the number (to know quickly if you have already seen such a number in a previous iteration)
  • 您可以缓存当前数字中当前数字的信息(如果您在以前的迭代中已经看到过这样的数字,可以快速地知道)

Quite naive and no code to propose yet, sorry, but I hope my idea is clear and you can make something useful out of it. People That Know certainly have more direct ideas.

很天真,没有代码可以提出,抱歉,但我希望我的想法很清楚,你可以从中做出一些有用的东西。知道的人肯定有更直接的想法。

In particular there may be a clever way to know if a number below K^N uses all K digits its K-ary representation. For example, you know that no number below K^(K-1) does (they have less than K distinct digits).

特别是有一个聪明的办法知道如果数量低于K ^ N使用其所有K-ary K数字表示。例如,你知道没有低于^ K(K - 1)(他们不到K不同的位数)。

#1


2  

let rec comb n l =
  match n, l with
  | 0, _  -> [[]]
  | _, [] -> []
  | n, x::xs -> List.map (fun l -> x ::l) (comb (n - 1) xs) @ (comb n xs)

let listToSingleSetSet xs = List.map (Set.singleton) xs |> set

let set_2Item_merge (set_set:Set<Set<'T>>) =
  seq {
    let arX = Set.toArray set_set
    let choice_list = comb 2 [0..(arX.Length-1)]
    for [x; y] in choice_list do
      yield begin
        set_set
        |> Set.remove arX.[x]
        |> Set.remove arX.[y]
        |> Set.add (arX.[x] + arX.[y])
      end
  }

let partitions xs =
  let set_set = listToSingleSetSet xs
  let rec aux sq =
    let x = Seq.head sq
    if Set.count x = 1
    then
      Seq.singleton x
    else
      Seq.append sq (Seq.map set_2Item_merge sq |> Seq.concat |> Seq.distinct |> aux)
  aux <| Seq.singleton set_set

DEMO

演示

> partitions ['a'..'d'] |> Seq.iter (printfn "%A");;
set [set ['a']; set ['b']; set ['c']; set ['d']]
set [set ['a'; 'b']; set ['c']; set ['d']]
set [set ['a'; 'c']; set ['b']; set ['d']]
set [set ['a'; 'd']; set ['b']; set ['c']]
set [set ['a']; set ['b'; 'c']; set ['d']]
set [set ['a']; set ['b'; 'd']; set ['c']]
set [set ['a']; set ['b']; set ['c'; 'd']]
set [set ['a'; 'b'; 'c']; set ['d']]
set [set ['a'; 'b'; 'd']; set ['c']]
set [set ['a'; 'b']; set ['c'; 'd']]
set [set ['a'; 'c'; 'd']; set ['b']]
set [set ['a'; 'c']; set ['b'; 'd']]
set [set ['a'; 'd']; set ['b'; 'c']]
set [set ['a']; set ['b'; 'c'; 'd']]
set [set ['a'; 'b'; 'c'; 'd']]
val it : unit = ()

If you want reverse seq then ...

如果你想要反向的seq,那么…

Seq.append sq (Seq.map set_2Item_merge sq |> Seq.concat |> Seq.distinct |> aux)

change to

改变

Seq.append (Seq.map set_2Item_merge sq |> Seq.concat |> Seq.distinct |> aux) sq

result:

结果:

set [set ['a'; 'b'; 'c'; 'd']]
set [set ['a'; 'b'; 'c']; set ['d']]
set [set ['a'; 'b'; 'd']; set ['c']]
set [set ['a'; 'b']; set ['c'; 'd']]
set [set ['a'; 'c'; 'd']; set ['b']]
set [set ['a'; 'c']; set ['b'; 'd']]
set [set ['a'; 'd']; set ['b'; 'c']]
set [set ['a']; set ['b'; 'c'; 'd']]
set [set ['a'; 'b']; set ['c']; set ['d']]
set [set ['a'; 'c']; set ['b']; set ['d']]
set [set ['a'; 'd']; set ['b']; set ['c']]
set [set ['a']; set ['b'; 'c']; set ['d']]
set [set ['a']; set ['b'; 'd']; set ['c']]
set [set ['a']; set ['b']; set ['c'; 'd']]
set [set ['a']; set ['b']; set ['c']; set ['d']]

#2


6  

Here's an updated solution, given that the order of subsets is unimportant:

这里有一个更新的解决方案,因为子集的顺序不重要:

let rec splits = function
| [] -> Seq.singleton([],[])
| x::xs ->
    seq { 
        for l1,l2 in splits xs do
            yield x::l1,l2
            yield l1,x::l2
    }

let parts =
    let rec parts' = function
    | 0,[] -> Seq.singleton []
    | _,[] -> Seq.empty
    | 1,l -> Seq.singleton [l]
    | n,x::xs ->
        seq {
            for l1,l2 in splits xs do
            for p in parts'(n-1, l2) do
                yield (x::l1)::p
        }
    fun l -> seq { 
        for k = 1 to List.length l do 
            yield! parts'(k,l) 
    }

The idea here is pretty straightforward. The splits function gives all ways to partition a list into two sets. Then to calculate the set of partitions of a list x::xs we could go through each split of xs into l1,l2, and for each partition of l2, add x::l1 in front.

这里的想法很简单。拆分函数提供了将列表划分为两组的所有方法。然后计算一个列表x::xs的一组分区,我们可以把xs的每一个分割成l1、l2和l2的每个分区,在前面加x: l1。

However, this won't meet your ordering requirement, so we break the problem down just a little bit further, and use the nested function part' to calculate the partitions of a list l into exactly n pieces. Then we just need to iterate through these lists of partitions in order.

但是,这不会满足您的订购需求,因此我们将问题进一步细分,并使用嵌套的函数部分来计算列表l的分区。然后我们只需要依次遍历这些分区列表。

#3


2  

Here is a quick idea.

这里有一个快速的想法。

One well-known trick to lazily generate the powerset of a set of size N is to iterate each number from 0 to (2^N)-1, considering its binary representation. Each iteration produces a partition : you put the i-th element of the set in the current partition if the i-th digit of the current number is 1.

一个众所周知的方法是延迟生成一组大小为N的powerset,这是为了迭代每个数字从0到(2)-1,考虑到它的二进制表示。每个迭代产生一个分区:如果当前数字的第i个数字为1,则将设置的第i个元素放在当前分区中。

You can do the same in your case: a partition of P is given not by a binary number below 2^N, but by a number below N^N, in base N. Now the trick to get the partitions with the fewest component first is:

你可以做同样的例子:一个分区的P不是由一个二进制数低于2 ^ N,但很多低于N ^ N,N进制下现在的技巧先分区最少的组件是:

  • first iterate upto 2^N (this gives you the partitions of 2 components)
  • 第一次迭代高达2 ^ N(这给你的分区2组件)
  • then iterate upto 3^N, rejecting the numbers that don't have both a 0, a 1 and a 2 in their representation (this rules out the previously produced partitions)
  • 然后迭代到3 ^ N,拒绝不都一个0的数字,1和2表示(这排除了以前生产的分区)
  • then iterate upto 4^N, taking only numbers with all 4 distinct digits
  • 然后迭代upto 4,只取所有4个不同数字的数字。
  • etc... upto N^N
  • 等等……到N ^

Obviously this makes you manipulate quite large numbers. You do not need to encode them as integers/bignum. For example in the powerset case, a list of booleans is just as good. The same idea could be reused in the partition case. Moreover, the K-ary representation of two consecutive numbers are close, so you can probably cache some information to get a more efficient algorithm:

很明显,这会使你操作相当大的数字。您不需要将它们编码为整数/bignum。例如在powerset的例子中,布尔值的列表也一样好。同样的想法可以在分区案例中重用。另外,两个连续数字的K-ary表示是接近的,所以你可能可以缓存一些信息来得到一个更有效的算法:

  • you can cache a part of your current partitioning (say, you could represent the current partition as an array of lists, and just destructively update the few digits that change)
  • 您可以缓存当前分区的一部分(比如,您可以将当前分区表示为列表的数组,并对更改的几个数字进行破坏性的更新)
  • you can cache the information of which digits are currently present in the number (to know quickly if you have already seen such a number in a previous iteration)
  • 您可以缓存当前数字中当前数字的信息(如果您在以前的迭代中已经看到过这样的数字,可以快速地知道)

Quite naive and no code to propose yet, sorry, but I hope my idea is clear and you can make something useful out of it. People That Know certainly have more direct ideas.

很天真,没有代码可以提出,抱歉,但我希望我的想法很清楚,你可以从中做出一些有用的东西。知道的人肯定有更直接的想法。

In particular there may be a clever way to know if a number below K^N uses all K digits its K-ary representation. For example, you know that no number below K^(K-1) does (they have less than K distinct digits).

特别是有一个聪明的办法知道如果数量低于K ^ N使用其所有K-ary K数字表示。例如,你知道没有低于^ K(K - 1)(他们不到K不同的位数)。