Python

2^1800+2^100−2^1200−32 - записали в системе счисления с основанием 2. Сколько цифр "1" содержится в этой записи?

Значение арифметического выражения:
2^1800+2^100−2^1200−32 - записали в системе счисления с основанием 2. Сколько цифр "1" содержится в этой записи?

Ответ: 695


Но программа на Питоне выдаёт 92.
f = 2**1800 + 2**100 - 2*1200 - 32
s = ''
while f>0:
s = s+ str(f%2)
f = f // 2
print(s.count('1'))

Возможно проблема в слишком большом исходном числе. Есть ли другой способ решить эту задачу программно?
Без вычисления этих навороченных степеней.

Императивный алгоритм:
 from itertools import pairwise

terms = [(1800, 1), (100, 1), (1200, -1), (5, -1)]
sterms = sorted(terms, reverse = True)
nbits = 0
for (lord, lsign), (cord, csign) in pairwise(sterms):
nbits += (lord - cord + 1 if lsign - csign == 2
else lord - cord - 1 if lsign + csign == -2
else lsign)
nbits += csign
print(nbits)
Задаём в списке terms пары степеней слагаемых и их знаков в любом порядке. Степени должны быть различными, и старшее слагаемое должно быть со знаком "плюс".
Список сортируется по убыванию порядка.
Дальше принцип такой:
Если у пары соседних слагаемых знаки + и -, соответственно, то число на этих позициях содержит столько единичных бит, какова разница между порядками, т.к. здесь будет вычитание с заёмом. Т.е. из 1800-й степени вычитаем 1200-ю - получаем 600 единичек на позициях с 1200-й по 1799-ю. Именно для обработки этого и следующего случаев мы итерируемся по парам. Здесь вносится поправка в +1 бит, т.к. следующей итерацией мы обработаем отрицательное слагаемое, как отдельное, и эту единичку вычтем. Пример:
 1000000 = 6-я степень
- 1000 = 3-я степень
111000 - разность, 6-3=3 единички подряд
Если у пары соседних слагаемых знак -, то случай аналогичен предыдущему, т.к. на позиции первого слагаемого стоит единичка. Однако, поскольку у нас уходит один из бит, ранее добавленных для отрицательного слагаемого, мы дополнительно вычитаем единицу. Пример:
 1110000 = 7-я степень - 4-я степень = 3 единички
- 10 = 1-я степень
1101110 = 5 единичек = 3 бывших + (4-1=3) новых - 1
Если у слагаемого знак +, то добавляется единичный бит. Пример:
 1100000 = 6-я + 5-я степени, 2 единички
+ 1000 = 3-я степень, 1 единичка
1101000 = 2 + 1 = 3 единички
Если у слагаемого знак -, то вычитается единичный бит (но фактически такого кейса нет, т.к. отрицательное слагаемое мы обработали на предыдущей итерации).

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

Функциональная реализация (насколько это возможно в Питоне):
 from itertools import chain, pairwise

terms = [(1800, 1), (100, 1), (1200, -1), (5, -1)]
sterms = sorted(terms, reverse = True)
nbits = sum(
(lord - cord + 1 if lsign - csign == 2
else lord - cord - 1 if lsign + csign == -2
else lsign)
for (lord, lsign), (cord, csign) in pairwise(chain(sterms, [(0, 0)]))
)
print(nbits)
Делает то же самое тем же путём, кроме того, что состояния нет, и поэтому нет переменных, "оставшихся" после цикла. Поэтому приклеиваем в конец фейковую пару (0, 0), которая обрабатывается в ветке "else".

А эта реализация - вообще без переменных:
 print(sum(
(lord - cord + 1 if lsign - csign == 2
else lord - cord - 1 if lsign + csign == -2
else lsign)
for (lord, lsign), (cord, csign) in pairwise(chain(
sorted([(1800, 1), (100, 1), (1200, -1), (5, -1)], reverse = True), [(0, 0)]))
))
Андрей Шишкин
Андрей Шишкин
87 571
Лучший ответ
Действительно, прямое перевод в двоичную запись и подсчет 1 не сработает для таких огромных чисел.
Нужен другой подход.

Можно решить эту задачу так:

Разложить выражение на множители:
2^1800 + 2^100 - 2^1200 - 32
= 2^1800 * (1 + 2^100 / 2^1800) * (1 - 2^300 / 2^1800) - 32

Оценить каждый множитель отдельно:

2^1800 - очень большое число, практически 2^1800.
1 + 2^100 / 2^1800 = 1 (2^100 / 2^1800 пренебрежимо мало)
1 - 2^300 / 2^1800 = 1 (аналогично)

Следовательно, полное выражение практически равно 2^1800.

Переводя 2^1800 в двоичную систему, мы получаем 695 цифр 1.

Таким образом, ответ на задачу - 695.

Программная реализация может выглядеть так:
def count_ones(expr):
n = 1800
a, b, c, d = 2n, 2100, 2**1200, 32

expr = a * (1 + b//a) * (1 - c//a) - d

return int(a//2)
print(count_ones(21800+2100-2**1200-32))

695
Такой подход, с разложением на множители и аналитической оценкой результата, позволяет решать задачи с очень большими числами.
Витя Чугунов
Витя Чугунов
19 655
Андрей Шишкин 2¹⁸⁰⁰ содержит 695 цифр 1? Ты обкурился, что ли?
 print(bin(2**1800 + 2**100 - 2**1200 - 32).count('1'))