最大限度地减少群组数量,但将不同群组中的敌人分开

时间:2018-02-21 01:09:38

标签: r algorithm grouping

我有n=4名为ABCD的人。有些人可以属于同一组,有些则不能。此信息由以下内容给出(在R中编码)

comparisons =          c("A-B",   "A-C",  "A-D",   "B-C",    "B-D",  "C-D")
areEnemies =           c(FALSE,   FALSE,   TRUE,   FALSE,    FALSE,   TRUE)

根据这些数据,个人AD是敌人,不能属于同一群组。个人CD以及敌人也不能属于同一群组。所有其他人都是朋友(当你不是你的敌人时,那么他就是你的朋友)。

我的目标是创建群组

  1. 最小化组数
  2. 个人可以属于一个或多个组(但必须属于至少一个组)
  3. 如果两个人是敌人,那么他们绝不能属于同一个群体。如果两个人是朋友(而不是敌人),那么他们必须至少在同一组中一次。
  4. 如果一个人可以属于某个群体,那么它必须!
  5. 上述示例的解决方案(使用组名称的小写字母)是

    • A属于群组a
    • B属于群组a和群组b
    • C属于群组a
    • D属于群组b

    我无法解决这个问题。你能提供帮助吗?

    如果你想编写代码,我欢迎使用R,C,C ++,Java,Bash,Python,但对流程(或伪代码)的口头描述已经非常有用。请注意,性能不会太受关注,因为我通常只有5-10个人,并且不会经常运行此过程。

3 个答案:

答案 0 :(得分:2)

您所描述的内容基本上是图形问题

数据

library(tidyverse)
df <- tibble(A = c("A", "A", "A", "B", "B", "C"),
        B = c("B", "C", "D", "C", "D", "D"),
        lgl = c(FALSE, FALSE, TRUE, FALSE, FALSE, TRUE))

# A tibble: 6 x 3
  # A     B     lgl  
  # <chr> <chr> <lgl>
# 1 A     B     F    
# 2 A     C     F    
# 3 A     D     T    
# 4 B     C     F    
# 5 B     D     F    
# 6 C     D     T

1 - 从数据框中过滤掉敌人,2 - 然后制作无向图(绘制它以查看它)。 3 - 确定图表的max_cliques

library(igraph)
data <- filter(df, lgl == FALSE)   # friends
G <- graph_from_data_frame(data, directed=FALSE)
plot(G)
max_cliques(G)

# [[1]]
# + 2/4 vertices, named, from 5940a66:
# [1] D B

# [[2]]
# + 3/4 vertices, named, from 5940a66:
# [1] A B C

答案 1 :(得分:0)

#checks if two people are enemies 
def areEnimies(a, b, enemies):
    for x in enemies:
        if (x[0] is a and x[1] is b) or (x[1] is a and x[0] is b):
            return True
    return False

enemies = ["AD", "CD"]  #list of enemies side by side
people = "ABCD" #the list of people who are in the game
groups = []
ans = []
#for each unique enemy, give them there own group
for x in enemies:
    if not x[0] in groups:
        groups.append(x[0])
    if not x[1] in groups:
        groups.append(x[1])
#populate this group with everyone else who is not their enemy
for g in range(len(groups)):
    for p in people:
        if not areEnimies((groups[g])[0], p, enemies) and (groups[g])[0] != p:
            groups[g] += p

#sort each group
for g in range(len(groups)):
    groups[g] = sorted(groups[g])

#if any groups are duplicates of one another we can remove them 
for i in range(len(groups)):
    dup = False
    for j in range(i+1, len(groups)):
        if groups[i] == groups[j]:
            dup = True
            break
    if not dup:
        ans.append(groups[i])

print(ans)
  

输出= [['A','B','C'],['B','D']]

> This is what I came up with
> 1. Write down each unique person that is enemies with someone
> 2. Populate each of those lists with friends 
> 3. Remove duplicate groups if they exist

敌人由无序对给出,被放入群组的人用字符串表示,请注意我们不需要朋友列表,因为任何不是敌人的人都可以认为是朋友。可能有更有效的方法来做到这一点,但它适用于小组,并使用我试过的几个不同的输入

干杯!

答案 2 :(得分:0)

问题本质上是图论问题。以下是要采取的步骤

  1. 考虑每个朋友之间的边缘,但不要考虑敌人之间的边缘
  2. 列出所有cliques
  3. 删除较大派系中包含的所有派系。
  4. 确保没有朋友的顶点也会获得一个组
  5. 以下是R

    中的代码
    ### input data
    d <- tibble(A = c("A", "A", "A", "B", "B", "C"),
            B = c("B", "C", "D", "C", "D", "D"),
            friend = c(TRUE, TRUE, FALSE, TRUE, TRUE, FALSE))
    
    ### list vertices
    vertexes = unique(c(d$from,d$to))
    
    ## Create graphs of friends
    dFriend = d[d$friend,]
    gFriend = graph.data.frame(dFriend, directed=FALSE)
    
    ## List all cliques
    cliques = lapply(cliques(gFriend), function(x) {names(x)})
    
    ## Remove cliques that are contained within other cliques
    if (length(cliques))
    {
      cliqueIndicesToRemove = c()
      for (i in 1:length(cliques))
      {
        clique_sub = cliques[[i]]
        for (j in 1:length(cliques))
        {
          if (i!=j)
          {
            clique_sup = cliques[[j]]
            if (all(clique_sub %in% clique_sup))
            {
              cliqueIndicesToRemove = c(cliqueIndicesToRemove, i)
            }
          }
        }
      }
      cliques = cliques[-unique(cliqueIndicesToRemove)]
    }
    
    ## return object
    r = tibble(
      cliques   = "",
      vertex    = vertexes   
      )
    if (length(cliques))
    {
      for (i in 1:length(cliques))
      {
        clique = cliques[[i]]
        matchingVertexes = which(r$vertex %in% clique)
        for (match in matchingVertexes)
        {
          r$cliques[match] = paste0(r$cliques[match], letters[i]  )
        }
      }
    }
    
    ## Make sure that vertices with no friend still get assigned to a clique
    nextLetterIndex = length(cliques) + 1
    for (i in 1:nrow(r))
    {
      if (r$cliques[i] == "")
      {
        r$cliques[i] = letters[nextLetterIndex]
        nextLetterIndex = nextLetterIndex + 1 
      }
    }
    

    这个解决方案非常受@ CPak答案的启发。