Как увеличивать два счетчика в зависимости от булевого выражения и от обратного ему
На вход логики откуда-то извне приходят какие-то значения, от которых зависит результат булевого выражения.
Нам надо посчитать, сколько раз случилось True, а сколько раз наоборот, False.
Как это сделать, проверив условие два раза - понятно:
import random
positive_counter = 0
negative_counter = 0
for _ in range(10):
input_var = random.random()
positive_counter += input_var > 0.5
negative_counter += input_var <= 0.5 # ну или not(input_var > 0.5)
print(positive_counter, negative_counter)
Вопрос в том, как это можно сделать не проверяя условие два раза и желательно в одном выражении.
Ответы (2 шт):
Считаем через словарь (или список), как уже предложили в комментариях:
import random
counter = {True: 0, False: 0}
for _ in range(10):
input_var = random.random()
counter[input_var > 0.5] += 1
print(*counter.values())
Суммируем только позитивные попытки, а негативные вычисляем как разницу числа попыток и число позитивных попыток, как уже тоже предложили в комментариях:
import random
n = 10
positive = sum(random.random() > 0.5 for _ in range(n))
print(positive, n - positive)
P.S. Список вместо словаря будет чуть-чуть быстрее, за счёт того, что словарю нужно ещё хэш элементов вычислять, чтобы узнать индекс доступа, а списку не нужно, у него сразу готовый индекс есть. Только нужно помнить, что в этом случае позитивный счётчик идёт вторым элементом:
import random
counter = [0, 0]
for _ in range(10):
input_var = random.random()
counter[input_var > 0.5] += 1
negative, positive = counter
print(positive, negative)
Сравним варианты выражений без двойной проверки с вариантом двойной проверки:
import random
n = 100_000
rnds = [random.random() for _ in range(n)]
%%timeit
sum(r > 0.2 for r in rnds)
5.2 ms ± 24.6 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%%timeit
sum(1000_000_000 + (r > 0.2) for r in rnds)
8.38 ms ± 51.1 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%%timeit max_n = 1000_000_000
sum(max_n + (r > 0.2) for r in rnds)
8.46 ms ± 20.6 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%%timeit lst = [0, 0]
for r in rnds:
lst[r > 0.2] += 1
8.69 ms ± 42 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%%timeit s, c = 0, 0
for r in rnds:
s += r > 0.2
c += 1
8.77 ms ± 172 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%%timeit dct = {True: 0, False: 0}
for r in rnds:
dct[r > 0.2] += 1
9.21 ms ± 202 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%%timeit a, b = 0, 0
for r in rnds:
cnd = r > 0.2
a += cnd
b += not cnd
10.4 ms ± 16.6 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%%timeit positive_counter, negative_counter = 0, 0
for r in rnds:
positive_counter += r > 0.2
negative_counter += r <= 0.2
12.6 ms ± 205 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Да, похоже, что овчинка стоит выделки. И, если есть бесплатная информация об общем числе значений, то её стоит использовать.
Варианты вида max_n + (r > 0.2) хороши для использования в sum() и прочих итераторах, но в операторах вида s += ... они такие же, как вариант списка (lst = [0, 0]).