在python中对很多二进制数组进行异或的最快方法是什么?

时间:2018-05-31 01:03:45

标签: python arrays hamming-distance

我的任务是计算两组中1D二进制数组之间的汉明距离 - 一组3000个数组和一组10000个数组,每个数组长100个项目(位)。这就是100x长对象的3000x10000高清计算。所有这些都必须在最多十几分钟内完成

这是我提出的最好的

#X - 3000 by 100 bool np.array 
#Y - 10000 by 100 bool np.array
hd = []
i=1
for x in X:
    print("object nr " + str(i) + "/" + str(len(X)))
    arr = np.array([x] * len(Y)) 
    C = Y^arr # just xor this array by all the arrays in the other group simultainously
    hd.append([sum(c) for c in C]) #add up all the bits to get the hamming distance
    i+=1

return np.array(hd)

它还需要1-1.5小时才能完成。我该如何更快地完成这项工作?

3 个答案:

答案 0 :(得分:5)

您应该能够通过使用numpy来执行它来显着提高求和速度,而不是使用列表推导和内置sum函数(它不会占用{{1}矢量化操作)。

只需替换:

numpy

使用:

hd.append([sum(c) for c in C])

对于2D数组,将返回一个新的一维数组,其中每个值都是相应行的总和(由于指定它应该在# Explicitly use uint16 to reduce memory cost; if array sizes might increase # you can use uint32 to leave some wiggle room hd.append(C.sum(1, dtype=np.uint16)) axis上运行)。例如:

1

由于它在没有类型转换的单个操作中执行C层的所有工作(而不是原始方法需要在每一行上运行的Python级别循环,然后是一个隐式循环,而在C层,必须仍然隐式地将每个>>> arr = np.array([[True,False,True], [False,False,True], [True, True,True]], dtype=np.bool) >>> arr.sum(1, np.uint16) array([ 2, 1, 3], dtype=uint16) 值从numpy逐个转换为Python级别np.bool只是为了对它们进行求和),对于您所描述的数组扩展,这应该大大加快

旁注:虽然不是性能问题的根源,但没有理由手动维护索引值; enumerate can do that更快更轻松。只需更换:

int

使用:

i=1
for x in X:
    ... rest of loop ...
    i+=1

并且你会得到相同的行为,但一般来说会更快,更简洁,更清洁。

答案 1 :(得分:1)

IIUC,您可以使用np.logical_xor和列表理解:

result = np.array([[np.logical_xor(X[a], Y[b].T).sum() for b in range(len(Y))] 
                                                       for a in range(len(X))])

整个操作在我的机器中运行7秒钟。

0:00:07.226645

答案 2 :(得分:1)

如果你不仅限于使用Python,这是使用bitset的C ++解决方案:

#include <iostream>
#include <bitset>
#include <vector>
#include <random>
#include <chrono>

using real = double;

std::mt19937_64 rng;
std::uniform_real_distribution<real> bitset_dist(0, 1);
real prob(0.75);

std::bitset<100> rand_bitset()
{
    std::bitset<100> bitset;
    for (size_t idx = 0; idx < bitset.size(); ++idx)
    {
         bitset[idx] = (bitset_dist(rng) < prob) ? true : false;
    }
    return std::move(bitset);
}

int main()
{
    rng.seed(std::chrono::high_resolution_clock::now().time_since_epoch().count());

    size_t v1_size(3000);
    size_t v2_size(10000);

    std::vector<size_t> hd;
    std::vector<std::bitset<100>> vec1;
    std::vector<std::bitset<100>> vec2;
    vec1.reserve(v1_size);
    vec2.reserve(v2_size);
    hd.reserve(v1_size * v2_size); /// Edited from hd.reserve(v1_size);

    for (size_t i = 0; i < v1_size; ++i)
    {
        vec1.emplace_back(rand_bitset());
    }
    for (size_t i = 0; i < v2_size; ++i)
    {
        vec2.emplace_back(rand_bitset());
    }

    std::cout << "vec1 size: " << vec1.size() << '\n'
              << "vec2 size: " << vec2.size() << '\n';

    auto start(std::chrono::high_resolution_clock::now());
    for (const auto& b1 : vec1)
    {
        for (const auto& b2 : vec2)
        {
            /// Count only the bits that are set and store them
            hd.emplace_back((b1 ^ b2).count());
        }
    }
    auto time(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - start).count());

    std::cout << vec1.size() << " x " << vec2.size()
              << " xor operations on 100 bits took " << time << " ms\n";
    return 0;
}

在我的机器上,整个操作(3000 x 10000)大约需要300毫秒。

你可以把它放到一个函数中,将它编译成一个库并从Python中调用它。另一种选择是将距离存储到文件中,然后用Python读取它们。

编辑:我的高清矢量大小错误。保留适当的内存量可将操作减少到大约190 ms,因为可以避免重定位。