【问题标题】:Seeking Pseudo-code for Calculating the Smith and Schwartz Set寻找计算 Smith 和 Schwartz 集的伪代码
【发布时间】:2019-04-04 14:47:07
【问题描述】:

我在Smith SetSchwartz SetKosaraju's AlgorithmTarjan's Algorithmpath-based strongly component algorithms 上阅读过维基百科;但是,我对此类算法的经验……缺乏。维基百科还说您可以使用 Kosaraju 算法的版本来生成 Schwartz 集,并且这些算法可以计算 Smith 集。

维基百科也有一些 Tarjan 算法的伪代码,但没有其他的;它并不特定于这个相对敏感的应用程序。我也不是 100% 确定哪个是最容易实现的——哪个具有实现中出错可能性最小的特性。

我想要一些更直接的伪代码来涵盖从其中一种算法计算 Smith 和 Schwartz 集,给定一组排名选票。当我有一个可以行走的实际过程时,我发现更容易掌握概念。我会自己把它变成实际的代码。

考虑以下数据结构:

Type Ballot {
  Array Votes[] {
    Candidate Candidate; // We do this in C#
    int Rank;
  }
}

对于选票集合,每个单独的选票将包含一个数组 Votes,如下所示:

Ballot b.Votes[] =
  [ Vote(Alex.ID, 1),
    Vote(Chris.ID, 3),
    Vote(Sam.ID, 2) ];

这对应于投票Alex>Sam>Chris 的选民,并且可能还有其他候选人都比克里斯更不受欢迎。

我认为第一步是统计个人选票并绘制胜利图。例如:如果 100 位选民将 Alex 排在 Sam 之上(Alex = 1,Sam >= 2),并且 50 位选民将 Sam 排在 Alex 之上,则 Alex 击败 Sam。因此我猜会有这样的数据结构:

Type GraphNode {
  Candidate Candidate;
  GraphNode Array Defeats[];
  GraphNode Array Ties[];
  GraphNode Array DefeatedBy[];
}

因此,Alex 的 GraphNode 将在 Defeats[] 中有一个元素指向 Sam 的 GraphNode,反之亦然。

鉴于这些 GraphNodes,我如何用它来识别 Smith 和 Schwartz 集?

提前致谢。

【问题讨论】:

  • 这必须有多高效?维基百科的文章还列出了 Floyd-Warshall 算法,该算法会更慢但更容易实现。
  • 如果它的时间复杂度相同,那很好。图形的最大大小很可能永远只有 9,因此重复重新计算任一集合的上限将是 7 次运行。很可能这些算法中的任何一种都很好,即使是 O(n^3) 算法。
  • 有了这么小的图,你甚至可以尝试所有的子集,只有 2^9 = 512 种可能性。仍然 O(n^3) Floyd-Warshall 方法可能更容易。
  • 我不确定我是否了解 Floyd-Warshall 如何适用。它确定了最短路径,但我正在研究强连接的组件。如果 A 击败 B,B 击败 C,C 击败 A,并且 {A,B,C} 中的每一个都击败了所有 {D,E,F,G},则史密斯集是 {A,B,C}。我相信如果 C 只与 A联系,史密斯集是 {A,B,C},施瓦茨集是 {A}。

标签: algorithm pseudocode voting


【解决方案1】:

我猜 python 已经足够接近伪代码了。

假设我们有n 候选人编号从0n - 1

首先,如果候选 i 击败候选 jFalse,则首先您可以计算矩阵 beats[i][j] 等于 True

现在使用 Floyd-Warshall 算法计算矩阵的传递闭包:

for k in range(n):
    for i in range(n):
        for j in range(n):
            beats[i][j] = beats[i][j] or (beats[i][k] and beats[k][j])

之后矩阵的含义略有不同:beats[i][j] 表示有一条“跳动路径”i -> c1 -> c2 -> ... -> j 使得i 击败c1c1 击败c2 等等直到@ 987654336@.

Schwartz 组件是其中所有对 ij 都有双向运行路径的组件,并且没有其他候选组件能够击败它们中的任何一个(请参阅 Wikipedia 上提到的属性部分 top循环)。

基本上对于每个候选人i 尝试围绕它构建一个组件,如下所示:

schwartz_components = []

for i in range(n):
    schwartz_component = {i}
    is_schwartz = True
    for j in range(n):
        if beats[j][i]:
            if beats[i][j]:
                schwartz_component.add(j)
            else:
                is_schwartz = False
    if is_schwartz:
         schwartz_components.append(schwartz_component)

schwartz_set = set.union(*schwartz_components)
print(schwartz_set)

对于 Smith 集,它会有点不同,您需要从 cannot_beat[i][j] = not beats[i][j] 开始,在其上使用 Floyd-Warshall,然后围绕每个 i 构建集,将所有候选者添加到它通过cannot_beat 关系。

我猜它会变成这样(在 Floyd-Warshall 步骤之后):

smith_candidates = []

for i in range(n):
    smith_candidate = {i}
    for j in range(n):
        if cannot_beat[i][j]:
            smith_candidate.add(j)
    smith_candidates.append(smith_candidate)

# Take the smallest candidate
smith_set = min(smith_candidates, key=len)

某处可能存在错误,但大致就是这样。

【讨论】:

  • 啊哈!这有帮助。所以,如果我没看错的话,k 的第一次迭代只会找到立即击败对方的东西(如果 A 击败 B 击败 C,我们不知道 A 击败 C);但是对于 (n) 个节点,它会重新遍历图形 (n) 次,并且在通过 (k) 时,我们已经确定了所有的节拍路径 (k) 长。是的,好的,这会起作用,而且非常简单。谢谢!
猜你喜欢
  • 2011-06-11
  • 1970-01-01
  • 2020-09-05
  • 1970-01-01
  • 1970-01-01
  • 2011-10-04
  • 2022-01-19
  • 1970-01-01
  • 2019-01-07
相关资源
最近更新 更多