Мне интересно найти простую формулу для определения n-го раза, когда определенный бит_счет встречается в последовательности натуральных чисел. В частности, какова связь между K
и N
в таблице ниже. Так, например, что такое N
из K=123456789123456789123456789
, я могу вам сказать, что M
есть 50
, но что такое N
?
length = 5
for K in range(2**length):
bits = bin(K)[2:].zfill(length)
M = K.bit_count() # numbers of "1"s in the binary sequence
N = sum(1 for i in range(K) if M==i.bit_count())
print(f'{K: <2}',bits,M,N)
'''
K bits M N
0 00000 0 0
1 00001 1 0
2 00010 1 1
3 00011 2 0
4 00100 1 2
5 00101 2 1
6 00110 2 2
7 00111 3 0
8 01000 1 3
9 01001 2 3
10 01010 2 4
11 01011 3 1
12 01100 2 5
13 01101 3 2
14 01110 3 3
15 01111 4 0
16 10000 1 4
17 10001 2 6
18 10010 2 7
19 10011 3 4
20 10100 2 8
21 10101 3 5
22 10110 3 6
23 10111 4 1
24 11000 2 9
25 11001 3 7
26 11010 3 8
27 11011 4 2
28 11100 3 9
29 11101 4 3
30 11110 4 4
31 11111 5 0
...
'''
Итак, я, кажется, решил половину проблемы. Кажется, что
N = (K-sum(2**i for i in range(M)).bit_count()
когда угодно N<=M
. Это, по-видимому, потому, что
K = sum(2**i for i in range(M)) + sum(2**(M-1-i) for i in range(N))
опять же, когда угодно N<=M
. Похоже, что N<=M
встречается примерно в половине случаев.
length = 5
for K in range(2**length):
bits = bin(K)[2:].zfill(length)
M = K.bit_count() # numbers of "1"s in the binary sequence
N = sum(1 for i in range(K) if M==i.bit_count())
if N <= M:
A = sum(2**i for i in range(M)) + sum(2**(M-1-i) for i in range(N))
B = (K - sum(2**i for i in range(M))).bit_count()
else:
A = '-'
B = '-'
print(f'{K: <2}',bits,M,N,A,B)
Если я скажу «pop_count» на math.stackexchange, они немедленно удалят вопрос. Несколько недель назад я попытался использовать знак «%» для символа мода, но мне сразу отказали.
Попробуйте использовать терминологию теории чисел, а не компьютерного программирования. Вы не используете компьютерное программирование для разработки формулы, вы используете компьютерное программирование для реализации формулы после ее математического определения.
Поскольку для любого K N является суммой всех целых чисел < K, которые разделяют число битов M с K, вы можете либо перебирать ряд (что является простой, хотя и очень вычислительно интенсивной проблемой программирования), либо придумать некоторая закономерность в этой серии (которая кажется нетривиальной математической задачей). На пути программирования методом грубой силы вместо этого вы могли бы сгенерировать все числа < K с определенным количеством битов и суммировать их.
@Grismar Да, это определенно нетривиально. Просто кажется, что это кто-то видел раньше. Я попробовал построить график пар (M,N) в системе координат, и кажется, что есть закономерность, но опять же нетривиальная. pop_count — настолько распространенная функция в компьютерном программировании и обработке сигналов, что я надеюсь, что кто-то знает больше меня.
Кстати, я почему-то сказал «сумма», но это, конечно, счет.
@barmar Я не совсем согласен с вами в том, что это скорее вопрос по математике, но я также чувствую, что на самом деле он просто требует алгоритмического подхода к вопросу по математике. Справедливо назвать это вопросом информатики, и если бы он был сформулирован в терминах «как более эффективно решить эту проблему» с данным решением, это был бы скорее вопрос программирования. Но вы правы, это довольно маргинально. Однако я не согласен с мнением, что подобные вещи — это «математика заранее» — многие математические задачи решаются с помощью алгоритмической игры. Если сформулировать правильно, это подходит для SO.
@EricPostpischil, ты должен опубликовать это как ответ
Ваше значение N
начинается с 0
. Наверняка оно должно начинаться с 1
?
@Ник: Хорошо, готово.
Тривиальное решение:
def count_same0(k: int) -> int:
s = 0
m = k.bit_count()
for n in range(k):
if n.bit_count() == m:
s += n
return s
Однако вместо этого вы можете попытаться сгенерировать все числа < k
только с определенным количеством битов и посчитать их.
Вот простая попытка сделать это:
def count_same1(k: int) -> int:
from itertools import permutations
from math import log2, floor
m = k.bit_count()
positions = floor(log2(k)) + 1 # the total number of bit positions
s = 0
# start generating unique permutations of positions #bits
ps = set()
for p in permutations([0] * (positions-m) + [1] * m):
if p not in ps:
ps.add(p)
n = 0
for b in p:
n = (n << 1) | b
# stop when we first exceed k
if n >= k:
break
s += n
return s
Это работает, но очень неэффективно, поскольку все равно приходится генерировать все неуникальные перестановки. И, в конце концов, создание перестановки происходит намного медленнее, чем просто подсчет битов.
Вместо этого, возможно, было бы лучше сгенерировать уникальные комбинации позиций нулей для чисел, которые имеют определенное количество единиц, а затем вычислить эти числа:
def gen_int_n_ones(n: int) -> Generator[int, None, None]:
from itertools import combinations
z = 0
while True:
bits_cs = list(combinations(range(1, n), z))
yield from (sum(0 if i in bits else 2**(n-(i+1)) for i in range(n)) for bits in bits_cs)
z += 1
n += 1
def count_same2(k: int) -> int:
s = 0
for n in gen_int_n_ones(k.bit_count()):
if n >= k:
break
s += n
return s
Однако наивное решение использует операции, которые настолько эффективны, что эффективнее просто проверить все числа, а не решать, каких из них следует избегать.
Попробуйте запустить это:
def main():
from timeit import timeit
for x in range(1, 100):
print(x, count_same0(x), count_same1(x), count_same2(x))
print(
timeit(lambda: count_same0(x), number=10000),
timeit(lambda: count_same1(x), number=10000),
timeit(lambda: count_same2(x), number=10000)
)
Вы обнаружите, что count_same0
— самый быстрый (на самом деле, просто быстрый) и что count_same2
лучше, чем count_same1
, но не приближается к count_same0
.
Поэтому я думаю, что простой ответ на самом деле:
def n_for_k(k: int) -> int:
b = k.bit_count()
return sum(1 for i in range(k) if i.bit_count() == b)
Однако для вашего примера значения 123456789123456789123456789
это займет много времени. Однако я сомневаюсь, что существует известное более простое решение, учитывая, что (после решения, как описано выше) я также нашел последовательность в OEIS: https://oeis.org/A079071 и она по сути определяет то же самое. (и глядя на график, лучше не становится: https://oeis.org/A079071/graph)
Кто или что поручил вам решить эту задачу?
Это выглядит почти идентично предоставленному мной коду, и, как мы оба отмечаем, это решение не решает данное значение K
. Значение K
было выбрано большим. В общем, учитывая 100
-значные двоичные числа, мне хотелось бы знать их индекс в сгенерированном списке, поэтому я выбрал K
, который отражал бы эту трудность. Я согласен, что наивного подхода недостаточно. Однако обратите внимание: если K = 549755811839
, то решением будет сразу M = 38
и N = (K - sum(2**i for i in range(38))).bit_count() = 27
. Это потому что N<=M
. Итак, для некоторых K
существуют быстрые формулы.
Я не спорю, просто хотел поделиться в комментариях результатом того, что предложил - считаю это бесплодным направлением. Однако, если посмотреть на данные OEIS, это выглядит как проблема, решение которой потребует написания математической работы. То, что вы предлагаете относительно «некоторых K
», — это просто еще один способ сказать, что границы могут быть установлены для N
, что ясно из графика и, вероятно, приведет к аналогичной формулировке для крайних случаев. Но это не ведет к общему решению для любого N, о чем вы, кажется, спрашиваете.
Я бы предложил задать более широкий вопрос на доске по математике (как предложил пользователь @barmar), сослаться на последовательность OEIS и посмотреть, знают ли какие-нибудь математики о атаке на эту проблему, которая имеет решение для любого N.
Возможно, в этом вы правы. В своем редактировании я неправильно заявил, что решил «половину», на самом деле это гораздо меньший процент, например log(n!)
или что-то в этом роде, и поэтому, вероятно, соответствует какому-то краю или границе на графике. Хотя кажется, что K <-> (M,N)
— это спаривание 1-1, так что в этом смысле моя формула действительно решает «половину» пар (M,N)
.
floor(log2(k)) + 1
крайне неэффективно. log2 = b.bit_length()
было бы намного лучше
@phuclv конечно, купите, очевидно, что это не имеет значения, поскольку оно вычисляется только один раз и полностью уничтожается остальной частью алгоритма.
@phuclv Это тоже неверно, например для K= 562949953421311.
@nocomment Я просто пропускаю + 1
, K.bit_length() + 1
всегда работают правильно, потому что bit_length всегда является нижней частью log2
@phuclv Хм? Нет, не добавляйте 1. Я говорил о логовом способе. Всегда в какой-то момент ошибаюсь, я не знаю, почему люди продолжают этим пользоваться, особенно опытные люди, которые наверняка знают о проблемах с поплавками.
@nocomment, почему бы и нет? результат равен полу, поэтому без добавления 1 он эквивалентен полу (log2 (k)). Длина в битах должна быть равна дну log2.
@phuclv «без добавления 1 это эквивалентно полу(log2(k))» — Нет, это не так.
@nocomment в документации говорится. Аналогично, когда abs(x) достаточно мал, чтобы иметь правильно округленный логарифм, тогда k = 1 + int(log(abs(x), 2))
. И это также легко увидеть из базовой математики. На самом деле bit_length
всегда корректно для сколь угодно больших значений, тогда как log2
нет, поскольку он использует математические вычисления с плавающей запятой ограниченной точности. Просто докажите любое положительное число, в котором bit_length
неверно и не эквивалентно floor(log2(k))
. Если ты этого не понимаешь, то хватит спорить
@phuclv Как ты не видишь 1 +
, который ты туда скопировал? Вы привели доказательство моей правоты.
10
не считается вторым экземпляром одного установленного бита (первым является 1
), поскольку в нем есть 0
, а 1
нет. Однако в этом вопросе OP считает, что все числа имеют то же количество бит, что и значение K
.
Я не знаю Python, так что это Ruby. Алгоритм должен легко транслироваться. Идея состоит в том, чтобы для каждого установленного бита (от меньшего к большему) подсчитать все числа, которые одинаковы слева от этого бита, имеют одинаковое количество установленных битов в этом бите и справа от него, но не имеют одинакового количества установленных битов в этом бите и справа от него. этот бит установлен.
Код:
def g(k)
set_bits_seen = 0
all_bits_seen = 0
total = 0
while k > 0
all_bits_seen += 1
if k % 2 == 1 # cur bit is set
set_bits_seen += 1
if all_bits_seen > set_bits_seen
total += n_choose_k(all_bits_seen - 1, set_bits_seen)
end
end
k /= 2
end
return total
end
def n_choose_k(n, k)
return 1 if k == 0 or k == n
(k + 1 .. n).reduce(:*) / (1 .. n - k).reduce(:*)
end
Полученные результаты:
1.upto(20) do |i|
puts "#{i.to_s(2)} -- #{g(i)}"
end
1 -- 0
10 -- 1
11 -- 0
100 -- 2
101 -- 1
110 -- 2
111 -- 0
1000 -- 3
1001 -- 3
1010 -- 4
1011 -- 1
1100 -- 5
1101 -- 2
1110 -- 3
1111 -- 0
10000 -- 4
10001 -- 6
10010 -- 7
10011 -- 4
10100 -- 8
И для вашего большого примера:
> g(123456789123456789123456789)
=> 3594960708495168399327022
Это действительно делает то, что, по моему мнению, должно делать. А как насчет задом наперед? Если я дал тебе (N,M)=(50,3594960708495168399327022)
, как ты найдешь K
?
@BobbyOcean Я думаю, что есть простой подход, но у меня нет времени подтвердить это или написать; вероятно, завтра. Не могли бы вы опубликовать это как отдельный связанный вопрос? Кто-то другой может опередить меня, и это нормально.
Думаю, я мог бы увидеть, как сделать это задом наперед. Большое спасибо, я знал, что это должно быть что-то вроде суммы биномиальных коэффициентов для каждого из установленных битов.
Выражение числа в виде комбинации битов и вычисление индекса комбинации:
from more_itertools import combination_index
from math import comb
K = 123456789123456789123456789
M = K.bit_length()
c = [i for i, b in enumerate(f'{K:b}') if b == '1']
N = comb(M, len(c)) - 1 - combination_index(c, range(M))
print(N)
Вывод (тот же, что и у Дэйва):
3594960708495168399327022
Черт, я только что понял, что тебе нужна «простая формула». Ну что ж. Возможно, это все еще полезно для людей, которые приходят сюда в поисках простого кода.
Для вычислений в любом направлении (от битовой строки k до количества бит m и индекса n или от количества битов и индекса до битовой строки) требуется всего несколько арифметических операций на битовую позицию. Реализации Python и C приведены ниже.
Чтобы проиллюстрировать, что алгоритмы состоят из нескольких элементарных арифметических операций на битовую позицию, версия C показана здесь без комментариев. Полностью прокомментированный код приведен ниже:
static void BitStringToIndex(unsigned Bits, unsigned *NBits, unsigned *Index)
{
unsigned b = 0;
while (Bits>>b & 1)
++b;
unsigned m = b, n = 0, c = 1;
while (Bits>>++b)
if (Bits>>b & 1)
n += (c = c * b / ++m);
else
c = c * b / (b-m);
*NBits = m;
*Index = n;
}
static unsigned IndexToBitString(unsigned m, unsigned n)
{
unsigned b = m, c = 1;
for (unsigned t; (t = c * (b+1) / ((b+1)-m)) <= n; ++b)
c = t;
unsigned k = 0;
for (; n; --b)
{
if (c <= n)
{
n -= c;
k |= 1;
c = c * m-- / b;
}
else
c = c * (b-m) / b;
k <<= 1;
}
k <<= b;
k |= (1u << m) - 1;
return k;
}
Число способов расположить m 1 бит в b позициях с 0 битами в остальных m−b позициях равно bCm = b! / м! / (б-м)!.1
В этом ответе позиции битов нумеруются 0 для наименее значимого, затем 1 для следующего и так далее. Двоичное число 10000 имеет 1 в битовой позиции 4 и 4 нуля «справа» от него или «ниже».
Рассмотрим список двоичных чисел в порядке возрастания с фиксированным количеством 1 бит. Прежде чем бит в позиции b сможет стать 1 (если он не начинается с 1), либо с начала списка, либо с момента последнего изменения с 1 на 0, биты b справа от него должны пройти через все bC m расположение битов m 1 в битах b, где m — количество битов 1 в позиции b и ниже. Число с 1 битом в позициях b0, b1, b2,…, каждое из которых соответствует соответствующему m0, m1, m2,… , появляется после шагов b0Cm0 в списке, чтобы получить 1 в бите b0 плюс b1 Cm1 выполняет шаги в списке, чтобы получить 1 в бите b1, и так далее. Итак, все, что нам нужно сделать, чтобы найти позицию этого числа в списке, — это добавить bCm к каждому биту, кроме тех, что вместе справа.2 Это показывает процедура BitStringToIndex
ниже.
И наоборот, учитывая количество битов m и индекс n, мы находим наибольшее значение b до того, как bCm превысит n, и это говорит нам, где находится первый бит в битовой строке. Тогда строка, которую мы ищем, представляет собой n − bCm шагов в списке после того места, где впервые появляется этот ведущий 1 бит, поэтому мы можем обновить n, а затем определить позицию следующего 1 бита. Об этом свидетельствует рутина IndexToBitString
.
В этом описании алгоритмов bCm неоднократно упоминается. Однако нам не нужно каждый раз рассчитывать его с нуля. По мере перехода от bCm к b+1Cm или b+1Cm+1 значение C изменяется на один множитель в числителе и на один множитель в знаменателе. Это показано в комментариях в коде ниже. Таким образом, одного умножения и одного деления на позицию бита достаточно для поддержания постоянного значения этой функции.
# At several points, comments mention maintaining c = C(b, m). This means
# that, as the values of b and/or m change, the value of c is modified so
# that it remains equal to C(b, m) for the new values of b or m.
# Convert bit string Bits (a binary numeral) to the number of 1 bits it
# contains and the index of that string in ascending lexicographic order of the
# strings with that number of 1 bits.
def BitStringToIndex(Bits):
# Set b to the position number of the lowest 0 bit in Bits.
b = 0
while Bits>>b & 1:
b += 1
# Start with number of bits b and index 0. Maintain c = C(b, m) =
# b! / m! / (b-m)!, the number of ways to arrange m 1 bits and b-m 0 bits
# in b positions.
m = b
n = 0
c = 1
# Increment b and continue while there are further 1 bits at or above b.
b += 1
while Bits>>b:
# If bit b is 1, count another 1 bit (++m) and add to the index n
# the number of arrangements we have passed -- all the arrangements
# of m 1 bits in the b positions below position b, C(b, m).
#
# Update c to transition to the new C(b, m) from the prior
# C(b-1, m-1): C(b, m) / C(b-1, m-1) =
# b! / m! / (b-m)! / ((b-1)! / (m-1)! / ((b-1) - (m-1))!) = b / m.
if Bits>>b & 1:
m += 1
c = c * b // m
n += c
# If bit is 0, do not update the index. Merely update c:
# C(b, m) / C(b-1, m) =
# b! / m! / (b-m)! / ((b-1)! / m! / ((b-1)-m)!) = b / (b-m).
else:
c = c * b // (b-m)
b += 1
# Return the bit count and the index.
return [m, n]
# Convert a number of 1 bits, m, and an index, n, of a bit string in ascending
# lexicographic order of the strings with that number of 1 bits to the bit
# string (a binary numeral).
def IndexToBitString(m, n):
# Find highest bit and maintain c = C(b, m) = b! / m! / (b-m)!,
# the number of ways to arrange m 1 bits and b-m 0 bits in b positions.
b = m
c = 1
while 1:
t = c * (b+1) // ((b+1)-m)
if n < t:
break
c = t
b += 1
# Start the index k at zero. For each bit position b, in descending
# order, test whether the index n indicates we have passed C(b, m) prior
# arrangements. If so, there is a 1 at the current bit position, and we
# decrement m by 1 to account for the 1 bit here and n by C(b, m) to
# account for the number of prior arrangements.
k = 0
# Continue until base case, index n is zero, at which time there are m 1
# bits in the m low positions.
while 0 < n:
# If n equals or exceeds C(b, m), we passed all prior arrangements
# of m bits in the b lower positions, so there must be a 1 bit here.
if c <= n:
# Decrement n for the number of prior arrangements.
#
# Put a 1 bit at this position. (We shift k later.)
#
# Update c to C(b-1, m-1) from C(b, m): C(b-1, m-1) / C(b, m) =
# (b-1)! / (m-1)! / ((b-1)-(m-1))! / (b! / m! / (b-m)!) =
# m / b.
#
# Decrement m by 1 for this 1 bit.
n -= c
k |= 1
c = c * m // b
m -= 1
# Otherwise, merely update c to C(b-1, m) from C(b, m):
# C(b-1, m) / C(b, m) =
# (b-1)! / m! / ((b-1)-m)! / (b! / m! / (b-m)!) = (b-m) / b.
else:
c = c * (b-m) // b
# Shift k to work on next bit position.
k <<= 1;
b -= 1
# The loop above can be made sufficient to construct all of k by running
# it down to a final iteration in which b is zero (which requires some
# care in the loop limit and avoiding division by zero in updating c).
# That is mathematically aesthetic, but, once n reaches zero, we can
# handle the final bits directly with the code below.
# The bit at position 0 in the value under construction in k is destined
# to be bit b in the final value, so shift k.
k <<= b
# Here n is zero which is the index for all m 1 bits in the low
# positions. Add this to k, and we are done.
k |= (1 << m) - 1
return k;
for k in range (32):
m, n = BitStringToIndex(k)
print(k, m, n)
t = IndexToBitString(m, n)
if t != k:
print("Error, expected ({}, {}) -> {} but got {}.".format(m, n, t, k))
exit(1)
#include <stdio.h>
#include <stdlib.h>
/* At several points, comments mention maintaining c = C(b, m). This means
that, as the values of b and/or m change, the value of c is modified so
that it remains equal to C(b, m) for the new values of b or m.
*/
/* Convert bit string Bits (a binary numeral) to the number of 1 bits it
contains, *NBits, and the index, *Index, of that string in ascending
lexicographic order of the strings with that number of 1 bits.
*/
static void BitStringToIndex(unsigned Bits, unsigned *NBits, unsigned *Index)
{
// Set b to the position number of the lowest 0 bit in Bits.
unsigned b = 0;
while (Bits>>b & 1)
++b;
/* Start with number of bits b and index 0. Maintain c = C(b, m) =
b! / m! / (b-m)!, the number of ways to arrange m 1 bits and b-m 0 bits
in b positions.
*/
unsigned m = b, n = 0, c = 1;
// Increment b and continue while there are further 1 bits at or above b.
while (Bits>>++b)
/* If bit b is 1, count another 1 bit (++m) and add to the index n
the number of arrangements we have passed -- all the arrangements
of m 1 bits in the b positions below position b, C(b, m).
Update c to transition to the new C(b, m) from the prior
C(b-1, m-1): C(b, m) / C(b-1, m-1) =
b! / m! / (b-m)! / ((b-1)! / (m-1)! / ((b-1) - (m-1))!) = b / m.
*/
if (Bits>>b & 1)
n += (c = c * b / ++m);
/* If bit is 0, do not update the index. Merely update c:
C(b, m) / C(b-1, m) =
b! / m! / (b-m)! / ((b-1)! / m! / ((b-1)-m)!) = b / (b-m).
*/
else
c = c * b / (b-m);
// Return the bit count and the index.
*NBits = m;
*Index = n;
}
/* Convert a number of 1 bits, m, and an index, n, of a bit string in
ascending lexicographic order of the strings with that number of 1 bits to
the bit string (a binary numeral).
*/
static unsigned IndexToBitString(unsigned m, unsigned n)
{
/* Find highest bit and maintain c = C(b, m) = b! / m! / (b-m)!,
the number of ways to arrange m 1 bits and b-m 0 bits in b positions.
The unusual loop construction is due to the fact we want to test
whether increasing b would yield n < C(b, m), in which case we want to
use b before increasing it. So a temporary value t is assigned the
value of what C(b, m) would be if b were increased. If it does not
exceed n, c is updated and b is increased, and the loop continues.
*/
unsigned b = m, c = 1;
for (unsigned t; (t = c * (b+1) / ((b+1)-m)) <= n; ++b)
c = t;
/* Start the index k at zero. For each bit position b, in descending
order, test whether the index n indicates we have passed C(b, m) prior
arrangements. If so, there is a 1 at the current bit position, and we
decrement m by 1 to account for the 1 bit here and n by C(b, m) to
account for the number of prior arrangements.
*/
unsigned k = 0;
/* Continue until base case, index n is zero, at which time there are m 1
bits in the m low positions.
*/
for (; n; --b)
{
/* If n equals or exceeds C(b, m), we passed all prior arrangements
of m bits in the b lower positions, so there must be a 1 bit here.
*/
if (c <= n)
{
/* Decrement n for the number of prior arrangements.
Put a 1 bit at this position. (We shift k later.)
Update c to C(b-1, m-1) from C(b, m): C(b-1, m-1) / C(b, m) =
(b-1)! / (m-1)! / ((b-1)-(m-1))! / (b! / m! / (b-m)!) =
m / b.
Decrement m by 1 for this 1 bit.
*/
n -= c;
k |= 1;
c = c * m-- / b;
}
/* Otherwise, merely update c to C(b-1, m) from C(b, m):
C(b-1, m) / C(b, m) =
(b-1)! / m! / ((b-1)-m)! / (b! / m! / (b-m)!) = (b-m) / b.
*/
else
c = c * (b-m) / b;
// Shift k to work on next bit position.
k <<= 1;
}
/* The loop above can be made sufficient to construct all of k by running
it down to a final iteration in which b is zero (which requires some
care in the loop limit and avoiding division by zero in updating c).
That is mathematically aesthetic, but, once n reaches zero, we can
handle the final bits directly with the code below.
*/
/* The bit at position 0 in the value under construction in k is destined
to be bit b in the final value, so shift k.
*/
k <<= b;
/* Here n is zero which is the index for all m 1 bits in the low
positions. Add this to k, and we are done.
*/
k |= (1u << m) - 1;
return k;
}
int main(void)
{
for (unsigned k = 0; k < 32; ++k)
{
unsigned m, n;
BitStringToIndex(k, &m, &n);
printf("%u %u %u\n", k, m, n);
unsigned t = IndexToBitString(m, n);
if (t != k)
{
fprintf(stderr, "Error, expected (%u, %u) -> %u but got %u.\n",
m, n, k, t);
exit(EXIT_FAILURE);
}
}
}
1 Это известный элементарный результат комбинаторики. Иногда ее называют функцией «n выбирают k», потому что это также количество способов выбора k объектов из n и обычно пишется nCk. Фактически размещение m1 бит в b позициях означает выбор m позиций из b позиций. Однако, поскольку в вопросе n и k используются для других целей, я не смог использовать их для этого.
2 Крайние правые биты изначально содержат 1, поэтому для установки их в 1 не требуется никаких шагов по списку, поэтому они не вносят вклад в индекс.
Ваш IndexToBitString
похож на алгоритм Баклза; это правильно?
@BobbyOcean эти реализации должны быть быстрее, чем ваш текущий ответ.
@Ник: Может быть. Я просто написал это с нуля.
@Ник: В статье Баклза и Либанона все описывается по-разному, но я думаю, что основные идеи по сути одинаковы. Биномиальная функция обладает большой гибкостью. Например, сумма функции по диагональному отрезку равна разнице значений функции в начальной и конечной точках, и я думаю, что это объяснило бы некоторую разницу между моими выражениями и их выражениями. Обратите внимание, что их код неоднократно вызывает биномиальную функцию, чего я избегаю.
Согласованный; ваше упрощение за счет использования соотношений между различными значениями, а не вычисления их с нуля, определенно является хорошим.
Это больше похоже на вопрос по Математике, а не на вопрос по программированию.