【问题标题】:Way to efficiently calculate XOR(^) checksum based on a list of IDs基于 ID 列表有效计算 XOR(^) 校验和的方法
【发布时间】:2017-01-11 02:53:15
【问题描述】:

当我在谷歌上搜索有关 Python 列表理解的信息时,我收到了一个 google foobar 挑战,过去几天我一直在慢慢研究它以获得乐趣。最新挑战:

实际上要求生成一个 ID 列表,忽略每一新行中不断增加的数字,直到剩下一个 ID。然后你应该对 ID 进行 XOR(^) 以产生校验和。我创建了一个输出正确答案的工作程序,但是它不足以在分配的时间内通过所有测试用例(通过 6/10)。 50,000 的长度应该会在 20 秒内产生结果,但需要 320 秒。

有人可以引导我朝着正确的方向前进,但是请不要为我做这件事,我很高兴能在这个挑战中推动自己。也许我可以实现一种数据结构或算法来加快计算时间?

代码背后的逻辑:

  1. 首先取入起始ID和长度

  2. 会生成一个 ID 列表,忽略每个新行中越来越多的 ID,从忽略第一行的 0 开始。

  3. 使用 for 循环异或 IDS 列表中的所有数字

  4. 答案以 int 形式返回


import timeit
def answer(start,length):
    x = start
    lengthmodified = length
    answerlist = []
    for i in range (0,lengthmodified): #Outter for loop runs an amount of times equal to the variable "length".
        prestringresult = 0
        templist = []
        for y in range (x,x + length): #Fills list with ids for new line
            templist.append(y)
        for d in range (0,lengthmodified): #Ignores an id from each line, increasing by one with each line, and starting with 0 for the first
            answerlist.append(templist[d])
        lengthmodified -= 1
        x += length    
        for n in answerlist: #XORs all of the numbers in the list via a loop and saves to prestringresult
            prestringresult ^= n
        stringresult = str(prestringresult) 
        answerlist = [] #Emptys list
        answerlist.append(int(stringresult)) #Adds the result of XORing all of the numbers in the list to the answer list
    #print(answerlist[0]) #Print statement allows value that's being returned to be checked, just uncomment it
    return (answerlist[0]) #Returns Answer



#start = timeit.default_timer()
answer(17,4)
#stop = timeit.default_timer()
#print (stop - start) 

【问题讨论】:

  • 你有两个内部循环。尝试摆脱它们。

标签: python checksum xor


【解决方案1】:

您可能需要一种不同的方法,而不仅仅是像约翰那样的小改进。我刚刚写了一个解决方案,可以在我的 PC 上大约 2 秒内完成answer(0, 50000)。我仍然逐行进行,但不是对行范围内的所有数字进行异或运算,而是一点一点地进行。行中有多少个数字设置了 1 位?[*] 奇数个数字?然后我翻转我的答案的 1 位。然后同样适用于 2 位、4 位、8 位等,直到 230 位。因此,对于每一行,它只是 31 次小计算(而不是实际上对数万个数字进行异或运算)。

[*] 可以在恒定时间内从范围的开始/停止快速计算。

编辑:由于您要求另一个提示,以下是如何计算在某个范围(a,b)中设置 1 位的频率。计算它在范围(0,a)中设置的频率,并从它在范围(0,b)中设置的频率中减去它。如果范围从零开始会更容易。在某个范围(0,c)中设置 1 位的频率是多少?简单:c//2 次。那么在某个范围(a,b)中设置 1 位的频率是多少?只需b//2 - a//2 次。高位类似,只是稍微复杂一点。

编辑 2: 哦,等等,我刚想起来……有一种简单的方法可以计算某个范围(a,b)内所有数字的异或。再次将工作拆分为 range(0, a) 和 range(0, b)。某个范围(0,c)中所有数字的异或很容易,因为有一个很好的模式(如果你对从 0 到 30 的所有 c 做它,你会看到它)。使用它,我现在可以在 0.04 秒内解决 answer(0, 50000)

【讨论】:

  • 你是对的。我修剪了约翰提到的脂肪,它极大地减少了运行时间,但仍然不够好。您的解决方案听起来很有趣,而且 1.52 运行时听起来很棒。但是,我不熟悉您提到的“一点一点”异或的过程。是否有网站或 stackoverflow 帖子您可以引导我解释该过程?
  • @Mrcitrusboots 想不出好的教程,抱歉。但这真的就像我描述的那样,非常简单。结果有(最多)31 位,您可以轻松地独立计算它们。首先尝试解决一个更简单的问题:找出结果是偶数还是奇数。也就是说,找出它的0位。然后通过计算其他 30 位来完成完整的问题。
  • 哦,我很确定我现在明白了。最后一个问题。你如何能够计算“从愤怒的开始/停止开始的恒定时间内”?
  • @Mrcitrusboots 查看我的答案的新编辑。 (还有小的更正:在我之前的评论中,我当然指的是 1 位,而不是 0 位。)
  • 为什么只有31个计算?整数不是 32 位的吗?还是因为整数的符号使用了最高位,所以只有 31 次计算?
【解决方案2】:

我能够在不使用列表的情况下获得一些改进,但它仍然会在大量数据上失败。嵌套循环会降低速度。我认为你需要遵循波赫曼逻辑,因为蛮力很少能解决这类问题。

【讨论】:

    【解决方案3】:

    大多数人会在这个问题中得到超过时间限制。我做到了! 这个问题可以这样总结:“在恒定时间内找到位于某个范围内的所有数字的异或。”是的,恒定时间!

    所以从 3-6 开始,答案应该是 3^4^5^6 = 4 在 O(1) 时间内。

    解决方案: XOR 本质上是关联的。所以 A ^ B ^ C 可以写成 B ^ A ^ C。 此外,我们知道 XOR 的意思是:“与”相同的位结果为 True,即 1,不同的位结果为 2。

    从这两个性质我们可以写: 3-6所有数字之间的异或可以写成:

    3^4^5^6 = (0^1^2)^(0^1^2) ^ (3^4^5^6)
            = (0^1^2^3^4^5^6) ^ (0^1^2) (this comes from the associative nature of xor)
            = XOR betn all the numbers from (0-6) ^ XOR betn all the numbers from (0-2)...eq(1)
    

    所以现在如果我们能在一个恒定的时间内找到从 0 到某个整数的所有数字的异或,我们就会得到答案。

    幸运的是,我们有一个模式:

    看这个例子:

    (0-1): 0 ^ 1 = 1 (1)
    (0-2): 0 ^ 1 ^ 2 = 3 (2+1)
    (0-3): 0 ^ 1 ^ 2 ^ 3 = 0 (0)
    (0-4): 0 ^ 1 ^ 2 ^ 3 ^ 4 = 4 (4)
    
    (0-5): 0 ^ 1 ^ 2 ^ 3 ^ 4 ^ 5 = 1 (1)
    (0-6): 0 ^ 1 ^ 2 ^ 3 ^ 4 ^ 5 ^ 6 = 7 (6+1)
    (0-7): 0 ^ 1 ^ 2 ^ 3 ^ 4 ^ 5 ^ 6 ^  7 = 0 (0)
    (0-8): 0 ^ 1 ^ 2 ^ 3 ^ 4 ^ 5 ^ 6 ^ 7 ^ 8 = 8 (8)
    
    
    So the pattern for finding the xor between all the integers between 0 to n is:
    if n%4 == 1 then, answer = 1
    if n%4 == 2 then, answer = n+1
    if n%4 == 3 then, answer = 0
    if n%4 == 0 then answer = n 
    
    Therefore, XOR(0-6) becomes 7 (since 6%4 ==2) and XOR(0-2) becomes 3 (since 2%4 ==2)
    
    Therefore, the eq(1) now becomes:
    3^4^5^6 = 7 ^ 3 = 4
    

    现在这个问题很简单,我们大多数人都因为时间限制超出错误而陷入困境,因为我们尝试在每个循环中进行异或,如果输入/迭代次数增加,这将是巨大的。 这是我在 python 中的工作解决方案,所有测试用例都由谷歌通过:

    #Main Program
    def answer(start, length):
        checkSum = 0
        for l in range(length, 0, -1):
            checkSum = checkSum ^ (getXor(start + l-1) ^ getXor(start-1))
            start = start + length
        return checkSum
    
    def getXor(x):
        result = [x, 1, x+1, 0]
        return result[x % 4]
    

    【讨论】:

      【解决方案4】:

      templistanswerlist 都不是真正需要的。让我们对您的代码进行几次检查,看看如何消除它们。

      1. 首先,让我们将templist 的初始化设置为单行。这个:

        templist = []
        for y in range (x,x + length):
            templist.append(y)
        

        变成这样:

        templist = list(range(x, x + length))
        
      2. 然后让我们对answerlist 做同样的事情。这个:

        for d in range (0,lengthmodified):
            answerlist.append(templist[d])
        

        变成这样:

        answerlist.extend(templist[:lengthmodified])
        
      3. 现在让我们看看它们以后是如何使用的。如果我们暂时忽略lengthmodified -= 1x += length,我们有:

        templist = list(range(x, x + length))
        answerlist.extend(templist[:lengthmodified])
        
        for n in answerlist:
            prestringresult ^= n
        
        answerlist = []
        

        与其扩展 answerlist,迭代它,然后清除它,不如只迭代 templist 更快。

        templist = list(range(x, x + length))
        
        for n in templist[:lengthmodified]:
            prestringresult ^= n
        

        现在也不需要templist,所以我们也跳过构建它。

        for n in range(x, x + lengthmodified):
            prestringresult ^= n
        

        templistanswerlist 不见了。

      这里唯一缺少的部分是 answerlist.append(int(stringresult)) 回来工作。我会留给你弄清楚。

      总的来说,这里的教训是尽可能避免显式的for 循环。编写大量迭代容器的for 循环是一种 C 思维方式。在 Python 中,通常有很多方法可以一次性浏览所有集合。这样做可以让您利用该语言的快速内置操作。

      另外,惯用的 Python 也更容易阅读。

      【讨论】:

      • 这样能快多少?
      猜你喜欢
      • 1970-01-01
      • 2020-06-25
      • 1970-01-01
      • 2011-05-25
      • 2015-03-20
      • 2011-06-03
      • 1970-01-01
      • 2021-05-11
      • 1970-01-01
      相关资源
      最近更新 更多