【问题标题】:Why should these algorigthms behave any differently?为什么这些算法的行为会有所不同?
【发布时间】:2019-07-03 23:35:40
【问题描述】:

为了个人启蒙,我正在摆弄 Swift 的 .reduce(),但我遇到了我无法理解的异常行为。

我花了很多意想不到的时间(至少 6 小时)进行研究和实验,但我很难过。

我有两个算法(我写了第二个。)它们似乎做完全相同的事情,即计算出现在具有重复模式的无限字符串中的字符数(在本例中为“a”) .两种算法在大约 50% 的时间里表现相同。一个使用 .reduce() 两次,另一个 - 工作的 - 使用 for 循环。

适用于所有情况的算法是:

let str = readLine()!
let length = Int(readLine()!)!

let fullRepetitions = length / str.characters.count
let lengthLastRep = length % str.characters.count

var count = fullRepetitions * str.characters.reduce(0) { $1 == "a" ? $0 + 1 : $0 }

for i in 0..<lengthLastRep {
    if str[str.index(str.startIndex, offsetBy: i)] == "a" {
        count += 1
    }
}

print(count)

而不是,但应该,是:

let pattern = readLine()!
let length = Int(readLine()!)!
let fullyRepeatedPatterns = length / pattern.count
let lastPatternLength = pattern.count % length
var countOfAs = fullyRepeatedPatterns * pattern.reduce(0, { $1 == "a" ? $0 + 1 : $0 })
countOfAs += pattern
    .prefix(upTo: pattern.index(pattern.startIndex, offsetBy: lastPatternLength - 1))
    .reduce(0, { $1 == "a" ? $0 + 1 : $0 })
print(countOfAs)

至少从表面上看,它们看起来应该做的完全一样。但他们没有。

起初,我以为我在执行取模以获取最后一个模式的长度时发现了我的问题,然而,如果我将第二个算法更改为第一个,它不仅会停止执行边缘情况,但有一个严重的运行时错误。也就是说,改变这个:

let lastPatternLength = pattern.count % length

对此:

let lastPatternLength = length % pattern.count

导致具有更多 .reduce() 的第二个算法中断(运行时错误),所以我认为模数不会导致错误。

更有趣的是,错误并不一致。输入:

aab
882787

“aab”重复了 882787 次,看起来像是一个错误的错误......或者似乎是一个错误。不过……

let pattern = "aab"
let length = 882787

let fullyRepeatedPatterns = length / pattern.count
let lastPatternLength = pattern.count % length
var countOfAs = fullyRepeatedPatterns * pattern.reduce(0, { $1 == "a" ? $0 + 1 : $0 })
countOfAs += pattern
    .prefix(upTo: pattern.index(pattern.startIndex, offsetBy: lastPatternLength - 1))
    .reduce(0, { $1 == "a" ? $0 + 1 : $0 })
print("expected 588525 but got \(countOfAs)")

我提前知道正确答案,所以我可以说:

我预计是 588525,但得到了 588526。

但是输入时:

babbaabbabaababaaabbbbbbbababbbabbbababaabbbbaaaaabbaababaaabaabbabababaabaabbbababaabbabbbababbaabb
860622337747

27 点就关了!

最后,好像故意很讨厌,输入:

ababa
3

关闭 -1。

在很多情况下两种算法都有效,例如:

aba
10

但我不在乎那些。

有人可以解释一下我在这里缺少什么吗?

【问题讨论】:

    标签: swift algorithm reduce


    【解决方案1】:

    第一:

    let lastPatternLength = pattern.count % length
    

    应该是

    let lastPatternLength = length % pattern.count
    

    与原始算法一样。例如:

    pattern = "abab"
    length = 11
    
    length / pattern.count = 11 / 4 = 2
    length % pattern.count = 11 % 4 = 3
    

    意思是模式的两个副本,加上三个额外的字符才能得到总长度为 11。

    这有时会在您的测试中导致运行时错误是由于第二个错误:在

    .prefix(upTo: pattern.index(pattern.startIndex, offsetBy: lastPatternLength - 1))
    

    你不能从lastPatternLength中减去一个,它应该是

    .prefix(upTo: pattern.index(pattern.startIndex, offsetBy: lastPatternLength))
    

    因为prefix(upTo:) 返回一个子序列,但不包括给定的索引。您也可以将其简化为

    .prefix(lastPatternLength)
    

    【讨论】:

    • 你的答案是正确的。虽然我尝试了你的每一个建议,但我没有一起尝试过。我想到了关于单元测试而不是集成测试的老笑话......
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-04-04
    • 2022-08-14
    • 1970-01-01
    • 2016-06-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多