python 计算一个列表中a XOR b大于a AND b的对的数量的快速方法是什么?

qco9c6ql  于 2023-03-16  发布在  Python
关注(0)|答案(3)|浏览(138)

我有一个数字数组,我想计算所有可能的对的组合,对于这些对,异或运算大于与运算。

示例:

4,3,5,2

可能的配对为:

(4,3) -> xor=7, and = 0
(4,5) -> xor=1, and = 4
(4,2) -> xor=6, and = 0
(3,5) -> xor=6, and = 1
(3,2) -> xor=1, and = 2
(5,2) -> xor=7, and = 0

Valid pairs for which xor > and are (4,3), (4,2), (3,5), (5,2) so result is 4.

这是我的程序:

def solve(array):
    n = len(array)
    ans = 0
    for i in range(0, n):
        p1 = array[i]
        for j in range(i, n):
            p2 = array[j]
            if p1 ^ p2 > p1 & p2:
                ans +=1
    return ans

时间复杂度是O(n^2),但是我的数组大小是1到10^5,数组中的每个元素是1到2^30,那么如何降低这个程序的时间复杂度呢?

q7solyqu

q7solyqu1#

这使用了和你一样的算法,所以它仍然是O(n^2),但是你可以使用numpy来加速这个操作:

  • np.bitwise_xor对两个数组执行按位异或运算
  • np.bitwise_and对两个数组执行按位与运算
  • 为这些函数提供一个行向量和一个列向量,numpy就可以将结果广播到一个方阵中。
  • 比较得到的矩阵,我们得到一个布尔数组,我们只需要这个矩阵的下三角形,因为我们知道a ^ a == 0,我们可以简单地将整个数组求和,然后将结果除以2得到答案。
import numpy as np

def npy(nums):
    xor_arr = np.bitwise_xor(nums, nums[:, None])
    and_arr = np.bitwise_and(nums, nums[:, None])

    return (xor_arr > and_arr).sum() // 2

您还可以完全跳过numpy,在运行代码之前使用numba对您自己的代码进行JIT编译。

import numba

@numba.njit
def nba(array):
    n = len(array)
    ans = 0
    for i in range(0, n):
        p1 = array[i]
        for j in range(i, n):
            p2 = array[j]
            if p1 ^ p2 > p1 & p2:
                ans +=1
    return ans

最后,下面是我对Dave's algorithm的实现:

from collections import defaultdict
def new_alg(array):
    msb_num_count = defaultdict(int)
    for num in array:
        msb = len(bin(num)) - 2 # This was faster than right-shifting until zero
        msb_num_count[msb] += 1 # Increment the count of numbers that have this MSB
    
    # Now, for each number, the count will be the sum of the numbers in all other groups
    cnt = 0
    len_all_groups = len(array)
    for group_len in msb_num_count.values():
        cnt += group_len * (len_all_groups - group_len)

    return cnt // 2

而且,作为一个数字兼容函数,我需要定义一个get_msb,因为numba.njit不能处理内置的python函数

@numba.njit
def get_msb(num):
    msb = 0
    while num:
        msb += 1
        num = num >> 1
    return msb

@numba.njit
def new_alg_numba(array):
    msb_num_count = {}
    for num in array:
        msb = get_msb(num)
        if msb not in msb_num_count:
            msb_num_count[msb] = 0
        msb_num_count[msb] += 1

    # Now, for each number, the count will be the sum of the numbers in all other groups
    cnt = 0
    len_all_groups = len(array)
        
    for grp_len in msb_num_count.values():
        cnt += grp_len * (len_all_groups - grp_len)

    return cnt // 2

比较运行时,我们看到numba方法明显比numpy方法快,后者本身比python中的循环快。
Dave给出的线性时间算法比numpy方法更快,当输入大于1000个元素时,它开始比numba编译的代码更快,numba编译版本的这种方法甚至更快--它在100个元素时就超过了numba编译的loopy
对于较大的输入,Dave算法的Kelly's excellent implementation与我的numba版本的实现相当

(Your实现被标记为“loopy”。图中的其他图例标记与我上面回答的函数名相同。Kelly的实现被标记为“kelly”)

c9x0cxw0

c9x0cxw02#

Dave算法的另一种实现:

from collections import Counter

def solve(array):
    ctr = Counter(map(int.bit_length, array))
    n = len(array)
    return sum(r * (n-r) for r in ctr.values()) // 2
fykwrbwg

fykwrbwg3#

假设ab是整数,则a^b > a&b当且仅当ab具有不同的最高置位。
解决方案:使用一个计数Map,其中键是最高的设置位。在线性时间内填充。
然后,处理键,假设总共有n个整数,某个键有r个整数(具有相同的最高设置位),然后该键将r (n-r)加到xor〉and的对的计数上,也就是说,r个整数中的每一个都可以与具有不同最高设置位的(n-r)中的每一个配对。
这会对所有内容进行双重计数,因此在最后除以2。
示例:
假设我们有8个整数,其中3个的第三位是最高置位,3个是第四位,2个是第五位。
因此,根据我的算法,我们有三个大小分别为3、3和2的桶,解为[3
(8-3)+ 3*(8 -3)+ 2*(8-2)] / 2 = 42 / 2 = 21。
下面是对该方法的更详细解释:
同一桶内的任何两个整数在AND运算下比在XOR运算下具有更高的值,因为AND运算保留最大设置位,而XOR运算将其变为0。
现在取两个不同桶中的整数,其中一个的最大设置位比另一个高,这意味着这两个数之间的最大设置位出现在一个中,而另一个中没有,因此在AND运算下变为0,但在XOR运算下被保留,因此XOR导致更高的最大设置位。
XOR比AND产生更高结果的对的数量正好是不在同一桶中的对的数量。
假设我们总共有n个整数,某个桶中有r个整数,这个桶中的r个整数都可以与其他桶中的任何一个(n-r)整数配对,但不能与同一个桶中的任何r-1个整数配对,这就为异或比与产生更大整数的对的计数贡献了r *(n-r)。
然而,这会对每一对计数两次,例如,当我们比较1001和110时,我们对第4和第3个最高设置位为1的桶的分析将为这一对增加1,因此最后我们必须除以2。
进一步举例:
以下是所有第三位为最高设置位的整数:4、5、6、7或100、101、110和111(以二进制表示)。
以下是第二位为最高设置位的所有器件:二进制的2、3或10、11。
取任意一对具有相同的最高置位,我会任意选择6和7。(110,111)= 110 = 6.异或运算(110,111)= 001。因此AND运算产生比XOR高的结果。在每种情况下,XOR将最高设置位从1转换为0,AND将保持其为1,因此在每种情况下,AND将产生比XOR更高的结果。
从单独的存储桶中取对,对中设置位最高的位仅在两个整数之一中设置(因为这是我们分组的依据),因此在AND下,该位变为0,而在XOR下,该位保持为1。由于XOR运算使输出具有比AND更大的设置位,因此在XOR下得到的整数更高。

相关问题