【问题标题】:Minimize the number of groups but separate the enemies in different groups尽量减少组的数量,但将不同组中的敌人分开
【发布时间】:2018-02-21 01:09:37
【问题描述】:

我有 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. 如果一个人可以属于一个群体,那么它必须!

上述示例的解决方案(使用小写字母作为组名)是

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

我无法解决这个问题。你能帮我一把吗?


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

【问题讨论】:

  • 这是一道作业题吗?如果是这样,把它放在那里让其他人知道可能会很好。 :)
  • 这不是家庭作业问题(我正在攻读遗传学博士学位,不参加任何课程)。我尝试了一些失败的方法,但在原始问题和此处提供的简化版本之间存在相当大的差距,我需要一些时间来重新格式化我失败的尝试。
  • 所以上面的编码中缺少 friends 吗?您的实际实例有多大?
  • @sasha 任何不是敌人和朋友的人。所以所有需要的输入数据都存在于上面的代码中。我通常有大约 5-10 个人。性能是先验的,不会很重要(我打算在R 中编写代码)。
  • 鉴于 我通常有大约 5-10 个人。性能是先验的,不会很重要:只需编写一个蛮力求解器。这是非常基本的编程。对于性能关键和更大的实例,我会推荐整数编程、约束编程、SAT 求解:经典的离散优化方法。 编辑 我也对这些规则持怀疑态度。 4的目的是什么?我觉得不够正式。也许有一种情况:给定 G=3 组的解决方案将只有 1 个分组;而另一个 G=3 解决方案可能有 > 1 个分组。

标签: r algorithm grouping


【解决方案1】:

你所描述的本质上是一个图形问题

数据

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

【讨论】:

    【解决方案2】:
    #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
    

    敌人是由无序的对给出的,被分组的人用一个字符串表示,注意我们不需要朋友列表,因为我们可以假设任何不是敌人的人都是朋友。可能有更有效的方法可以做到这一点,但它对于小团体来说是合理的,并且我尝试了几种不同的输入

    干杯!

    【讨论】:

      【解决方案3】:

      这个问题本质上是一个图论问题。以下是要走的步骤

      1. 考虑每个朋友之间的优势,而不是敌人之间的优势
      2. 列出所有cliques
      3. 删除更大派系中包含的所有派系。
      4. 确保没有朋友的顶点也有一个组

      这是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 的回答。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-12-13
        • 2018-04-15
        • 1970-01-01
        相关资源
        最近更新 更多