Как мне найти все 32-битные двоичные числа, которые имеют ровно шесть 1 и остальные 0 - PullRequest
5 голосов
/ 14 апреля 2019

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

Итак, некоторые примеры чисел, которые я хочу:

00000000001111110000
11111100000000000000
01010101010100000000
10101010101000000000
00100100100100100100

Полная перестановка.За исключением результатов, которые имеют только шесть 1.Не больше.Не менее.64 или 32 бита было бы идеально.16 бит, если это дает ответ.

Ответы [ 4 ]

4 голосов
/ 14 апреля 2019

Я думаю, что вам нужно использовать модуль itertools .

ПЛОХОЕ РЕШЕНИЕ

Но вы должны быть осторожны, например, использование что-то вроде перестановок будет просто работать для очень маленьких входов. то есть:

Нечто подобное приведенное ниже даст вам двоичное представление:

>>> ["".join(v) for v in set(itertools.permutations(["1"]*2+["0"]*3))]
['11000', '01001', '00101', '00011', '10010', '01100', '01010', '10001', '00110', '10100']

тогда просто получим десятичное представление этого числа:

>>> [int("".join(v), 16) for v in set(itertools.permutations(["1"]*2+["0"]*3))]
[69632, 4097, 257, 17, 65552, 4352, 4112, 65537, 272, 65792]

если бы вы хотели 32 бита с 6 единицами и 26 нулями, вы бы использовали:

>>> [int("".join(v), 16) for v in set(itertools.permutations(["1"]*6+["0"]*26))]

но для этого вычисления потребуется суперкомпьютер (32! = 263130836933693530167218012160000000)

DECENT SOLUTION

Так что более умный способ сделать это - использовать комбинаций , может быть что-то вроде этого:

import itertools

num_bits = 32
num_ones = 6
lst = [
    f"{sum([2**vv for vv in v]):b}".zfill(num_bits)
    for v in list(itertools.combinations(range(num_bits), num_ones))
]
print(len(lst))

это говорит о том, что во всем спектре 32-битных чисел есть 906192 чисел с 6-ю.

КРЕДИТЫ:

Кредиты для этого ответа отправляются @Mark Dickinson, который указал, что использование permutations было невозможно, и предложил использовать combinations

1 голос
/ 15 апреля 2019

Вы можете создать массив счетчиков для позиций в 1 с в числе и собрать его, сдвинув биты в их соответствующие позиции. Я создал пример ниже. Он работает довольно быстро (меньше 32 секунд на моем ноутбуке):

bitCount = 32
oneCount = 6
maxBit   = 1<<(bitCount-1)
ones     = [1<<b for b in reversed(range(oneCount)) ] # start with bits on low end
ones[0] >>= 1  # shift back 1st one because it will be incremented at start of loop
index    = 0
result   = []
while index < len(ones):
    ones[index] <<= 1                # shift one at current position
    if index == 0:
        number = sum(ones)           # build output number
        result.append(number)
    if ones[index] == maxBit:    
        index += 1                   # go to next position when bit reaches max
    elif index > 0:               
        index -= 1                   # return to previous position
        ones[index] = ones[index+1]  # and prepare it to move up (relative to next)

64 бита занимает около минуты, примерно пропорционально количеству выводимых значений. O (п)

Тот же подход можно выразить более кратко в рекурсивной функции генератора, которая позволит более эффективно использовать битовые комбинации:

def genOneBits(bitcount=32,onecount=6):
    for bitPos in range(onecount-1,bitcount):
        value = 1<<bitPos
        if onecount == 1: yield value; continue
        for otherBits in genOneBits(bitPos,onecount-1):
            yield value + otherBits

result = [ n for n in genOneBits(32,6) ] 

Это не быстрее, когда вы получаете все числа, но разрешает частичный доступ к списку, не просматривая все значения.

Если вам нужен прямой доступ к N-ой битовой комбинации (например, для получения случайной однобитовой комбинации), вы можете использовать следующую функцию. Это работает как индексирование списка, но без необходимости генерировать список шаблонов.

def numOneBits(bitcount=32,onecount=6):
    def factorial(X): return 1 if X < 2 else X * factorial(X-1)
    return factorial(bitcount)//factorial(onecount)//factorial(bitcount-onecount)

def nthOneBits(N,bitcount=32,onecount=6):
    if onecount == 1: return 1<<N
    bitPos = 0
    while bitPos<=bitcount-onecount:
        group = numOneBits(bitcount-bitPos-1,onecount-1)
        if N < group: break
        N -= group
        bitPos += 1
    if bitPos>bitcount-onecount: return None
    result  = 1<<bitPos
    result |= nthOneBits(N,bitcount-bitPos-1,onecount-1)<<(bitPos+1)
    return result


 # bit pattern at position 1000:
 nthOneBit(1000) # --> 10485799 (00000000101000000000000000100111)

Это позволяет получить битовые комбинации для очень больших целых чисел, которые было бы невозможно сгенерировать полностью:

 nthOneBits(10000, bitcount=256, onecount=9) 

 # 77371252457588066994880639
 # 100000000000000000000000000000000001000000000000000000000000000000000000000000001111111

Стоит отметить, что порядок шаблонов не соответствует порядку номеров соответствующих чисел

Хотя nthOneBits () может создавать любой шаблон мгновенно, он намного медленнее других функций при массовом создании шаблонов. Если вам нужно манипулировать ими последовательно, вы должны перейти к функции генератора, а не к циклу nthOneBits ().

Кроме того, достаточно легко настроить генератор, чтобы он запускался по определенному шаблону, чтобы вы могли получить лучшее от обоих подходов.

Наконец, может быть полезно получить следующий битовый шаблон с учетом известного шаблона. Вот что делает следующая функция:

def nextOneBits(N=0,bitcount=32,onecount=6):
     if N == 0: return (1<<onecount)-1
     bitPositions = []
     for pos in range(bitcount):
         bit = N%2
         N //= 2
         if bit==1: bitPositions.insert(0,pos)         
     index = 0
     result = None
     while index < onecount:
         bitPositions[index] += 1
         if bitPositions[index] == bitcount:
             index += 1
             continue
         if index == 0:
             result = sum( 1<<bp for bp in bitPositions )
             break
         if index > 0:
             index -= 1
             bitPositions[index] = bitPositions[index+1]
     return result    

nthOneBits(12)      #--> 131103 00000000000000100000000000011111 
nextOneBits(131103) #--> 262175 00000000000001000000000000011111  5.7ns
nthOneBits(13)      #--> 262175 00000000000001000000000000011111 49.2ns

Как и nthOneBits (), этот не требует времени на установку. Его можно использовать в сочетании с nthOneBits () для получения последующих шаблонов после получения исходного в заданной позиции. nextOneBits () намного быстрее, чем nthOneBits (i + 1), но все же медленнее, чем функция генератора.

Для очень больших целых чисел использование nthOneBits () и nextOneBits () может быть единственным практическим вариантом.

1 голос
/ 15 апреля 2019

Ну, я не программист Python, поэтому я не могу опубликовать правильный код для вас. Вместо этого я могу сделать C ++ one ...

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

Что-то вроде:

 for (i0=   0;i0<32-5;i0++)
  for (i1=i0+1;i1<32-4;i1++)
   for (i2=i1+1;i2<32-3;i2++)
    for (i3=i2+1;i3<32-2;i3++)
     for (i4=i3+1;i4<32-1;i4++)
      for (i5=i4+1;i5<32-0;i5++)
       // here i0,...,i5 marks the set bits positions

Таким образом, O(2^32) становится меньше, чем `~ O (26.25.24.23.22.21 / 16), и вы не можете идти быстрее, чем это, поскольку это будет означать, что вы пропустите действительные решения ...

Я предполагаю, что вы хотите напечатать число, чтобы для ускорения вы могли вычислить число как строку двоичного числа с самого начала, чтобы избежать медленного преобразования между строкой и числом ...

Вложенные циклы for могут быть закодированы как операция приращения массива (аналогично арифметике bignum)

Когда я собрал все вместе, я получил C ++ код:

int generate()
    {
    const int n1=6;     // number of set bits
    const int n=32;     // number of bits
    char x[n+2]; // output number string
    int i[n1],j,cnt; // nested for loops iterator variables and found solutions count
    for (j=0;j<n;j++) x[j]='0'; x[j]='b'; j++; x[j]=0;  // x = 0
    for (j=0;j<n1;j++){ i[j]=j; x[i[j]]='1'; } // first solution
    for (cnt=0;;)
        {
//      Form1->mm_log->Lines->Add(x);   // here x is the valid answer to print
        cnt++;
        for (j=n1-1;j>=0;j--) // this emulates n1 nested for loops
            {
            x[i[j]]='0'; i[j]++;
            if (i[j]<n-n1+j+1){ x[i[j]]='1'; break; }
            }
        if (j<0) break;
        for (j++;j<n1;j++){ i[j]=i[j-1]+1; x[i[j]]='1'; }
        }
    return cnt;         // found valid answers
    };

Когда я использую это с n1=6,n=32, я получил этот вывод (без печати чисел):

cnt = 906192

и он был закончен в 4.246 ms на AMD A8-5500 3.2GHz (32-битное приложение win7 x64 без потоков), что достаточно быстро для меня ...

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

Вдобавок ко всему, как только вы поиграете с переменной n1,n, вы можете сделать то же самое для нулей вместо единиц и использовать более быстрый подход (если нулей меньше, то используют вложенные циклы для пометки нулей вместо единиц).

Если требуемые номера решений требуются в качестве числа (а не строки), то можно переписать это так, чтобы i[] или i0,..i5 содержали битовую маску вместо битовых позиций ... вместо inc / dec вы просто сдвиг влево / вправо ... и больше не нужен массив x, так как число будет x = i0|...|i5 ...

0 голосов
/ 15 апреля 2019

Вы имеете дело с перестановками мультимножеств . Есть много способов добиться этого, и, как указывает @BPL, эффективно это сделать нетривиально. Здесь упоминается много замечательных методов: перестановок с уникальными значениями . Самый чистый (не уверен, что он самый эффективный), это использовать multiset_permutations из sympy модуля.

import time
from sympy.utilities.iterables import multiset_permutations

t = time.process_time()

## Credit to @BPL for the general setup
multiPerms = ["".join(v) for v in multiset_permutations(["1"]*6+["0"]*26)]  

elapsed_time = time.process_time() - t

print(elapsed_time)

На моей машине вышеупомянутое вычисляется всего за 8 секунд. Он генерирует чуть менее миллиона результатов:

len(multiPerms)
906192
...