Равномерная рекомбинация двоичного представления двух целых чисел

#python #performance #numpy #numba

#python #Производительность #numpy #numba

Вопрос:

Мне нужна быстрая реализация следующей задачи, предпочтительно в виде функции numba. Я беру два случайных целых числа a amp; b из списка с именем integerlist и рассматриваю их двоичное представление длины l , например a=10->1010 , b=6->0110 . После этого я выполняю равномерный переход между обоими двоичными представлениями, и целочисленное значение результирующего двоичного числа сохраняется в случайной позиции в integerlist . Равномерная рекомбинация означает, что каждая запись двоичного представления целого числа c либо берется из записи двоичного представления a , либо b с равной вероятностью, например

 a=10->1010
b=6 ->0110
      1110 ->c=14
  

Для этого я придумал следующий код, который не очень быстрый. На данный момент я пытаюсь получить numba-версию этой функции, но пока безуспешно. Не могли бы вы помочь?

 def recombination(integerlist, l):
    N = len(integerlist)
    for x1 in range(N):
        a = integerlist[random.randint(0, N-1)]
        b = integerlist[random.randint(0, N-1)]
        binary_a = list(map(int, numpy.binary_repr(a, width=l)))
        binary_b = list(map(int, numpy.binary_repr(b, width=l)))
        binary_c = [0]*l
        for x2 in range(l):
            if random.random() <= 0.5:
                binary_c[x2] = binary_a[x2]
            else:
                binary_c[x2] = binary_b[x2]
        c = 0
        for bit in binary_c:
            c = (c << 1) | bit
        integerlist[random.randint(0, N-1)] = c
  

Редактировать: Если я заменю list(map(int, numpy.binary_repr(a, width=l))) следующей функцией

 @nb.njit
def dec_to_binary_fct(a, l):
    bin_temp = []
    for i in range(l):
        i = l-i-1
        k = a >> i
        if (k amp; 1):
            bin_temp.append(1)
        else:
            bin_temp.append(0)
    return bin_temp
  

Я могу поместить @nb.njit перед def recombination(integerlist, l): , которые уже значительно повышают производительность. Мне все еще любопытно, можно ли увеличить производительность.

Ответ №1:

Вот способ вычисления пересечения, который, я уверен, быстрее:

 def xover(a, b):
    l = max(a.bit_length(), b.bit_length())
    return a^((a^b)amp;random.randint(0, (1<<l)-1))
  

Объяснение:

  • сначала мы используем побитовое исключение or, чтобы найти биты, которые отличаются (для других битов не имеет значения, откуда мы их берем, поэтому мы также можем взять их все из a)
  • затем мы используем побитовую и случайную маску для удаления в среднем половины из них
  • и, наконец, используйте побитовое исключение или снова, чтобы перевернуть оставшиеся биты в a (мы знаем, что в этих позициях перевернутое a равно b)

Комментарии:

1. Это действительно хорошая идея, но немного незавершенная, поскольку она применима только к l<32 . По крайней мере, l<64 было бы неплохо.

2. @HighwayJohn просто измените случайный диапазон. Верхний предел должен быть степенью двойки (минус единица, если вы решите использовать random вместо np.random ). Если вы выходите за рамки 64, я думаю, что random модуль, безусловно, лучший выбор, если np.random он вообще работает. Если вы используете ровно 64 с numpy, обязательно используйте uint64

3. Вау, отлично, большое спасибо! Я, вероятно, могу это выяснить, но max (), похоже, не поддерживается в numba. Вы случайно не знаете обходной путь?

4. @HighwayJohn Не совсем, нет. Может быть, и я действительно просто предполагаю, что он примет один аргумент последовательности вместо двух отдельных аргументов?

5. Если вы знаете, что все ваши числа меньше некоторого числа, X тогда используйте фиксированное l . Поскольку вы вычисляете это только один раз, вы можете использовать для этого дорогостоящие функции, подобные log2(X) .