【问题标题】:Number of even XOR-ed subarrays偶数 XOR 子阵列的数量
【发布时间】:2021-04-23 20:36:01
【问题描述】:

求偶数异或的子数组个数(子数组的异或表示其元素的异或)。 例如:

l=[1,2,3,4]   # ----> Answer of this is 4

(解释:[2],[4],[1,2,3],[1,2,3,4]---> 这些是偶数异或的子数组,因此子数组的数量=4 )

这是我的代码:

def odd_int(lst):
    odd=0
    for i in lst:
        if i%2!=0:
            odd+=1 
    return odd    
l=list(map(int,input().split()))
x=0 # Denotes number of even xor arrays
for i in range(len(l)):
    for j in range(i,len(l)):
        l1=l[i:j+1]
        y=odd_int(l1)
        if y%2==0:
            x+=1
print(x)

上面的代码超过了时间限制,所以有什么建议可以优化到O(n)??? 谢谢你的时间!!!

【问题讨论】:

  • 您能分享一下input array 的大小吗?也许考虑使用dict 将时间减少到 O(n)?而是。
  • 输入数组可以大到100000,所以现在你可以决定
  • 您的解决方案是 O(n^3)。请注意,ij 上的前两个循环在 O(n^2) 时间内计算子数组。但是,您的 odd_int 函数接受一个列表并重新计算 XOR,它本身是 O(n)。您应该首先专注于首先减少到 O(n^2)(不太困难),然后在需要时尝试进一步优化。稳步减少使用的计算量应该可以帮助您获得所需的结果。
  • 有一个O(n)的解决方案,我相信带前缀计算的hashmap可以做到。

标签: python python-3.x algorithm performance dynamic-programming


【解决方案1】:

有一个简单的循环允许O(n) 时间,O(1) 空间算法:

def f(A):
  total, odd, even = 0, 0, 0
  for a in A:
    even, odd = (odd, 1 + even) if a % 2 else (1 + even, odd)
    total += even
  return total


# Test

import random

# https://stackoverflow.com/a/65801048/2034787
def eisenstat(l):
    prefixXOR = 0
    evens = 1
    odds = 0
    for x in l:
        prefixXOR ^= x
        if prefixXOR % 2 == 0:
            evens += 1
        else:
            odds += 1
    return evens * (evens - 1) // 2 + odds * (odds - 1) // 2

num_tests = 100

for _ in range(num_tests):
  A = random.sample(range(10, 100), 10)
  if f(A) != eisenstat(A):
    print(A)

【讨论】:

  • @OliverQueen 没有。它们都是 O(n) 时间和 O(1) 空间复杂度。这只是另一种思考方式。
【解决方案2】:

思路:求和为偶数(能被2整除)的子数组个数。

思考过程:

这里是异或运算的一些性质

  • 偶数 ^ 偶数 = 偶数
  • 奇数 ^ 奇数 = 偶数
  • 偶数 ^ 奇数 = 奇数
  • 奇数 ^ 偶数 = 奇数
  1. 现在从上面的性质,我们可以观察到,在一个子数组中,如果偶数元素的数量是偶数,奇数元素的数量是偶数,那么我们可以说该子数组的 XOR 是偶数。 例如; (奇 ^ 偶 ^ 奇 ^ 偶 ) => (偶 ^ 偶 ^ 奇 ^ 奇) => (偶 ^ 偶) ^ (奇 ^ 奇) => (偶 ^ 偶) => 偶。

  2. 现在由于子数组中奇数元素和偶数元素的个数是偶数,所以子数组的和也是偶数(可被2整除)。

  3. 如第二点所述,您必须找到总和可被 2 整除的子数组的数量。您可以将此作为参考(https://leetcode.com/problems/subarray-sums-divisible-by-k/)这是查找总和可被整除的子数组数量的问题由 k,但在我们的例子中 k = 2。

  • 时间复杂度:O(n)
  • 空间复杂度:O(1)
  • 有关方法和复杂性,请参阅上面的链接。

【讨论】:

  • 感谢您的宝贵时间
【解决方案3】:

XOR 有一些很好的特性,允许使用额外空间的常量字进行线性时间解。

第一个属性(正式地:可交换的、关联的,每个元素都是自逆的)提供了一种快速计算任意子数组 XOR 的方法。如果我们采用前缀 XORs

prefixXORs[0] = 0
prefixXORs[1] = prefixXORs[0] ^ l[0] = 1
prefixXORs[2] = prefixXORs[1] ^ l[1] = 3
prefixXORs[3] = prefixXORs[2] ^ l[2] = 0
prefixXORs[4] = prefixXORs[3] ^ l[3] = 4

然后我们可以计算

l[i] ^ ... ^ l[j] == prefixXORs[i] ^ prefixXORs[j + 1]

因此问题变成了确定有多少对前缀 XOR 具有偶数异或。

第二个属性是

even ^ even is even
even ^ odd is odd
odd ^ even is odd
odd ^ odd is even

因此,我们可以计算两个都是偶数或都是奇数的前缀 XOR 对的数量。在 Python 中:

def count_even_subarrays(l):
    prefixXOR = 0
    evens = 1
    odds = 0
    for x in l:
        prefixXOR ^= x
        if prefixXOR % 2 == 0:
            evens += 1
        else:
            odds += 1
    return evens * (evens - 1) // 2 + odds * (odds - 1) // 2


print(count_even_subarrays([1, 2, 3, 4]))

【讨论】:

  • 很好的解释。竖起大拇指。
  • 我已经理解了大部分问题,但请解释两件事:i.) 上面的偶数和奇数是什么,它们是指偶数和奇数异或的数量吗? ii.) 为什么我们最后做偶数(偶数-1//2)并加上奇数(奇数-1//2)?那是为了什么??
  • @OliverQueen evensodds 分别是偶数和奇数的前缀 XOR 的数量。 n*(n-1)//2 是与 n 项目组成无序对的多种方法的公式。
【解决方案4】:

您可以尝试使用memoization 模式,使用functools.lru_cache

from functools import lru_cache

@lru_cache(maxsize=None)
def odd_int(lst):
    odd=0
    for i in lst:
        if i%2!=0:
            odd+=1 
    return odd    

def xor_list(input_list):
    #l=list(map(int,input_list.split()))
    l = tuple(input_list)  # required for LRU cache to work (hashable input to the odd_int function needed)
    x=0 # Denotes number of even xor arrays
    for i in range(len(l)):
        for j in range(i,len(l)):
            l1=l[i:j+1]
            y=odd_int(l1)
            if y%2==0:
                x+=1
    #print(x)

时间安排是:

你的代码:

每个循环 15.8 毫秒 ± 2.57 毫秒(7 次运行的平均值 ± 标准偏差,每次 10 次循环)

带记忆:

每个循环 3.35 ms ± 210 µs(平均值 ± 标准偏差,7 次运行,每次 100 个循环)

您可能还可以通过将 y%2 检查移至该函数来进一步改进。

您可以完全消除对odd_int 的调用,方法是预处理整个数组并以恒定时间从附加列表中获取值,而不是在两个for 循环体中执行此操作。但我将把它留给你实施和检查。

【讨论】:

  • 任何消除for循环的方法,因为我认为这个问题不需要使用外部库!!!!
  • 你可以自己实现记忆模式。而functools 在标准库中。
  • 这一直是我的问题,消除两个 for 循环并降低时间复杂度。几天以来我一直在尝试这样做,但我无法提出 O(n) 解决方案,所以如果你能提供帮助,请做!这个我已经尽力了!!!
  • 另外,它现在显示超出内存限制
猜你喜欢
  • 2013-07-05
  • 2019-02-15
  • 1970-01-01
  • 2020-07-20
  • 2021-08-29
  • 1970-01-01
  • 2020-03-31
  • 1970-01-01
相关资源
最近更新 更多