Tortoise和Hare算法为一个案例返回空结果而对重复字符返回不正确

时间:2013-06-20 08:01:50

标签: algorithm

我正在经历Wikipedia implementation of Cycle detection using Tortoise-and-Hare algorithm。使用Ruby语言,这是我实现的:

def tortoise_and_hare(sequence)
  tortoise = 1
  hare = 2
  while sequence[tortoise] != sequence[hare]
    tortoise += 1
    hare += 2
  end

  # Find start index of first repetition
  idx = 0
  tortoise = 0
  while sequence[tortoise] != sequence[hare]
    tortoise += 1
    hare += 1
    idx += 1
  end

  # Find length of cycle starting from index idx
  length = 1
  hare = tortoise + 1
  while sequence[tortoise] != sequence[hare]
    hare += 1
    length += 1
  end

  [idx, length]
end

sequence = [2, 0, 6, 3, 1, 6, 3, 1, 6, 3, 1]
idx, length = tortoise_and_hare(sequence)
p sequence[idx, length]

这样做正常并返回[6, 3, 1]。现在,

  1. 如果我将序列修剪为[2, 0, 6, 3, 1, 6, 3, 1],它将返回 空集。
  2. 我可以看到问题出在二次循环中。如果周期有 重复字符,算法返回错误答案。例, [2, 0, 6, 3, 1, 6, 6, 3, 1, 6, 6, 3, 1, 6]返回[6, 3, 1], 但应该是[6, 3, 1, 6]。我可以看到问题排在第三位 循环。
  3. 所以我想我的问题是:

    1. 算法是否按维基百科标准发布?
    2. 我的第二个案例是不正确的?我知道循环检测意味着我的检查不是无限长的序列,但它仍然有一个循环。
    3. 如果情况正确,我们可以做些什么来改进算法并解决我上面提到的两个问题?
    4. 我尝试修改第二个循环以解决第一个问题(修剪序列足够小以使算法失败)并且它有效:

        # Find start index of first repetition
        idx = 0
        tortoise = 0
        while sequence[tortoise] != sequence[hare]
          tortoise += 1
          hare += 1
          hare = tortoise if hare > sequence.length - 1
          idx += 1
        end
      1. 在某些情况下看起来是错误还是可能失败?
      2. 我们可以为第二个问题(重复字符)做些什么?
      3. 虽然我提出了另一种优雅的基于Regex的解决方案,但我仍然想了解更多有关上述算法的信息。

        好奇的正则表达式解决方案:/(?<cycle>(\d+\s)+(\d+))\s\k<cycle>/

        编辑:我理解为什么它不可能检测到重复的字符。但在这种情况下是否还有其他可能有用的算法?

1 个答案:

答案 0 :(得分:0)

答案是你的代码很好,但你的样本集太小了。该算法没有声称在尽可能短的数据量中找到一个循环。

您链接的页面上数据集的定义定义了生成无限数据集的过程。这些数据最终必须重复,因为您的域名不受限制,但您的范围是有限的。

根据范围,此算法将需要更多或更少的数据来确定周期。你已经不再提供它了。

至于解决方案,我会选择吗?我将通过插入第一个数字来创建范围中每个数字的映射以及我找到它的位置。一旦发现重复,你就找到了你的周期。从第一个实例的位置到第二个实例之前的位置。这给出了线性运行时和N * M内存使用。 N =列表的大小M =值的范围

这是你想要的perl(生锈的perl)正则表达式:

$data = "1 2 3 4 5 3 4 5"; 
if ($data =~ /(?<c>\d).*?(\k<c>)/) {
    print substr($data, $-[1], $-[2]-$-[1])."\n";
} elsif {
    print "NO\n";
}

最糟糕的运行时间是n ^ 2,我想它只适用于单位数字(容易修复),但它更直接。