Как мне перебрать список в порядке единиц в двоичном значении индекса? (Желательно Python) - PullRequest
1 голос
/ 04 августа 2020

Если у меня огромный список (например, 2 ^ 50 элементов), как мне перебрать этот список, но в порядке количества единиц в двоичном значении индекса, как можно быстрее в Python ?

Так, например, сначала 11111111, затем 11111110, 11111101, 11111011, 11110111, 11101111, 11011111, 10111111, 01111111, затем 11111100 и т. Д. c.

Мне это нужно, потому что Я занимаюсь динамическим c программированием, чтобы найти значение для каждого подмножества данного набора V, обозначенного значением (подмножеством), и поскольку существует так много возможных подмножеств, я решил обозначить подмножества в двоичном формате, так что подмножество {v1 , v2, v5} - это, например, 000010011. Мне нужно выполнить итерацию в указанном порядке, потому что рекурсивная функция, которую я должен использовать, имеет значение базового случая (V) = 0, то есть значение (1111111111111) = 0, и строится дальше с уменьшением размера в подмножестве, то есть количество единиц в двоичное значение.

Если есть совершенно другой подход, не использующий двоичный файл, пожалуйста, дайте мне знать! Большое спасибо за то, что вы это прочитали, Joost

EDIT: это рекурсивная функция: рекурсивная функция , где N (v) - соседи вершины v (это проблема графа). псевдокод будет выглядеть так, для набора V и подмножеств X:

value(V)=0
Forloop in described order:
    pick a vertex v from V that is not in X and denote it in binary
    subset, so that for example  v = 001000000, where X = 010111101
    #Use bitwise operator '|' to get intersections of binary values: 
    value(X) = value(X|v) + value(X|N(v)) + 1
    

PS: вы можете просто использовать обычные целые числа в python, и побитовый оператор все равно будет работать: 8 | 4 вернет 12 : 100 | 010 = 110

1 Ответ

0 голосов
/ 07 августа 2020

Два отдельных подхода предлагаются ниже.

Подход 1: Генератор на основе комбинаций

Один из способов реализовать это - использовать внешний l oop сверх количества битов, и внутренний l oop над позициями битов. Для внутреннего l oop, генератор комбинаций может использоваться для перебора позиций битов включения. Соответствующая последовательность будет начинаться с числа со всеми включенными битами, за которым следуют числа со всеми, кроме одного, бит, за которыми следуют числа со всеми, кроме двух битов, ... и, наконец, в конце 0 (без битов включения ). Комбинация битовых позиций может быть преобразована в число путем суммирования 2^position по позициям битовых битов.

Для варианта использования вопроса элементы последовательности будут использоваться в качестве индексов для обхода по соответствующему списку.

Генераторы комбинаций вращающихся дверей перебирают небольшие наборы элементов, которые нужно менять местами в комбинации при каждом посещении. Такой алгоритм, возможно, можно было бы использовать для уменьшения вычислительных требований подхода, предложенного выше, путем изменения текущего числа на каждой итерации в зависимости от того, какой бит будет включен, а какой - выключен.

Вот реализация в Python, которая не использует подход вращающейся двери, на основе встроенного itertools.combinations Python. В приведенном ниже примере использования функции используется всего восемь битов. Для каждого числа в сгенерированной последовательности выходные данные показывают 1) десятичное представление, 2) двоичное представление и 3) количество включенных битов.

from itertools import combinations

def generator(num_bits):
    powers = [2 ** exponent for exponent in range(num_bits - 1, -1, -1)]
    for num_on in range(num_bits, -1, -1):
        for positions in combinations(range(num_bits), num_on):
            yield sum(powers[position] for position in positions)

for x in generator(8):
    binary = f'{x:#010b}'
    bit_count = binary.count('1')
    print(f'{x:03} {binary} {bit_count}')

Выход:

255 0b11111111 8
254 0b11111110 7
253 0b11111101 7
251 0b11111011 7
247 0b11110111 7
239 0b11101111 7
223 0b11011111 7
191 0b10111111 7
127 0b01111111 7
252 0b11111100 6
250 0b11111010 6
249 0b11111001 6
...
006 0b00000110 2
005 0b00000101 2
003 0b00000011 2
128 0b10000000 1
064 0b01000000 1
032 0b00100000 1
016 0b00010000 1
008 0b00001000 1
004 0b00000100 1
002 0b00000010 1
001 0b00000001 1
000 0b00000000 0

Подход 2: упорядоченный список индексов

Альтернативный подход - с использованием большего объема памяти, чем предыдущий метод - заключается в вычислении количества битов включения в каждом числе (где «число» здесь - index), а затем создайте список чисел в порядке убывания их количества включенных битов.

Вот реализация в Python. Для букв от a до z вывод показывает символы, упорядоченные по количеству бит в их индексе, а также индекс каждого символа, его двоичное представление и количество включенных битов. bin(.).count('1') используется для подсчета количества знаков в каждом номере. Проблема 29882 на ошибках. python .org предполагает, что встроенный метод bit_count будет доступен в Python 3.10.

letters = [
    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
    'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
]

n = len(letters)
bit_counts = [bin(x).count('1') for x in range(n)]
indices_lookup = [[] for _ in range(len(bin(n)) - 2)]
for idx, bit_count in enumerate(bit_counts):
    indices_lookup[bit_count].append(idx)
ordered_indices = []
for idxs in reversed(indices_lookup):
    ordered_indices.extend(idxs)

for x in ordered_indices:
    print(f'{letters[x]} {x:02} {x:#07b}')

Выход:

p 15 0b01111 4
x 23 0b10111 4
h 07 0b00111 3
l 11 0b01011 3
n 13 0b01101 3
o 14 0b01110 3
t 19 0b10011 3
v 21 0b10101 3
w 22 0b10110 3
z 25 0b11001 3
d 03 0b00011 2
f 05 0b00101 2
g 06 0b00110 2
j 09 0b01001 2
k 10 0b01010 2
m 12 0b01100 2
r 17 0b10001 2
s 18 0b10010 2
u 20 0b10100 2
y 24 0b11000 2
b 01 0b00001 1
c 02 0b00010 1
e 04 0b00100 1
i 08 0b01000 1
q 16 0b10000 1
a 00 0b00000 0
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...