Pull to refresh
2
0.6
Send message

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

Играть в игры "у меня где-то там спрятана хитрая под2.7бка", а, тем более "угадайте, что я имел ввиду, а вот и не угадаете, бе-бе-бе" желания нет совершенно, извините. Но могу анекдот в тему рассказать:
Учительница: "По небу летят два крокодила, один зелёный, другой коричневый, один со скоростью 50 км/ч, другой со скоростью 80 км/ч. Вопрос: сколько мне лет?"
Вовочка (тянет руку): "Вам 24 года."
"Правильно, Вовочка. Как ты узнал?"
"Мой папа называет меня полудурком, а мне 12 лет."

Т. к. все трое ответили одно и то же, то либо с вероятностью 1/9 все трое соврали, либо с вероятностью 8/9 все трое сказали правду.

В общем, понятно: дело в том, что в данном случае, по-сути, производятся две последовательных выборки, и первая перекашивает результат второй. В исходной популяции у нас поровну случаев "М, М", "Д, Д", "М, Д", "Д, М".
В первую выборку для первой задачи идут "Д, Д" и "Д, М", из них выбираются случаи "Д, Д", их половина.
В первую выборку для второй задачи идут "М, М", "М, Д" и "Д, М", из них выбираются случаи "М, М", их треть.
Какая-то мутнота присутствует в формулировке задачи, раз приходится делать предположения, выходящие за рамки этой формулировки. Пояснения выше, что "становится понятнее, если бросать монеты" меня что-то, наоборот, запутали.

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

Но компьютер видит
from random import choice

GIRL, BOY = 0, 1

def outcome0():
    # случайная пара детей
    return (choice([GIRL, BOY]), choice([GIRL, BOY]))

# проверка исхода на "оба - девочки" и "оба - мальчики"
def both_girls(outcome):
    return sum(outcome) == 0

def both_boys(outcome):
    return sum(outcome) == 2

N = 1000000 # миллион случайных исходов
population = [outcome0() for _ in range(N)]

outcomes1 = [outcome for outcome in population if outcome[0] == GIRL]
girls_only = [1 for outcome in outcomes1 if both_girls(outcome)]
probability1 = len(girls_only) / len(outcomes1)

outcomes2 = [outcome for outcome in population if BOY in outcome]
boys_only = [1 for outcome in outcomes2 if both_boys(outcome)]
probability2 = len(boys_only) / len(outcomes2)

print(f"Задача №1: {probability1}")
print(f"Задача №2: {probability2}")

Вывод:

Задача №1: 0.5007268254500726
Задача №2: 0.3322241975783202

Однако. Несколько лет назад я так же замоделировал "парадокс Монти-Холла", там было всё прямее и проще, понимание приходит ещё до запуска программы, где-то на этапе формализации исходов. Тут как-то более хитровыделанно...

Замоделировал численно
from random import choice, shuffle

GIRL, BOY = 0, 1

# исходы для обеих задач
# (дети в парах по убыванию старшинства)
def outcome1():
   # для первой задачи:
   # первый - девочка, второй - ?
   return [GIRL, choice([GIRL, BOY])]

def outcome2():
   # для второй задачи:
   # один - мальчик, другой - ?, порядок случайный
   tmp = [BOY, choice([GIRL, BOY])]
   shuffle(tmp) # перемешиваем, следуя букве условия (но что это меняет?)
   return tmp

# проверка исхода на "оба - девочки" и "оба - мальчики"
def both_girls(outcome):
   return sum(outcome) == 0

def both_boys(outcome):
   return sum(outcome) == 2

N = 1000000 # миллион случайных исходов

outcomes1 = [outcome1() for _ in range(N)]
girls_only = [1 for outcome in outcomes1 if both_girls(outcome)]
probability1 = len(girls_only) / N

outcomes2 = [outcome2() for _ in range(N)]
boys_only = [1 for outcome in outcomes2 if both_boys(outcome)]
probability2 = len(boys_only) / N

print(f"Задача №1: {probability1}")
print(f"Задача №2: {probability2}")

Вывод:

Задача №1: 0.499951
Задача №2: 0.50002

Ну, понятно, чуть туда, чуть сюда, закон больших чисел, ответ для обеих задач стремится к 0.5. Если действительно для второй задачи ответ должен быть 0.33333..., то, значит, с определением функции outcome2 я просчитался, но где? Вроде, всё сделал самым наитупейшим образом, строго "в лоб", следуя букве условия вопреки здравому смыслу.

Таких сразу на улицу - он будет так же делать при любой проблеме.

Да не будет он так же делать при любой проблеме. Просто человек интеллектуально травмирован младшей, средней и высшей школой, где материал ограничен и ты должен знать его весь, тебя спрашивают - ты должен ответить, единственно правильным образом, не можешь - садись, два. Ну, отсеете вы его - остальные 99.999...% - такие же. С возрастом и с опытом, даст бог, оклемаются, заматереют, оборзеют...

По Питону раньше спрашивал как выключить GIL.

Правильный ответ же "никак", а не "не знаю"? Если не считать каких-то неофициальных фанатских сборок, где люди упоролись и таки убрали GIL, сделав вместо этого отдельный lock для каждого объекта, или как-то так, но оно почему-то хуже работало, забыл почему. Мне ещё когда-то интервьюер доказывал, что в Stackless Python нету GIL, но, насколько я помню, это ложьпиздёжь.

Билять, сраный ИИ. Загуглил "Stackless Python", чтобы проверить. В гугловском "AI Overview" один из пунктов: "No Global Interpreter Lock (GIL): Unlike standard CPython, Stackless Python avoids the GIL, allowing for true parallelism in CPU-bound tasks." Думаю, фигассе, таки тот интервьюер был прав? Но далее, по первой же ссылке на Википедию: "Although microthreads make it easier to deal with running subtasks on a single core, Stackless Python does not remove CPython's global interpreter lock (GIL), nor does it use multiple threads and/or processes." И вот эта конина уже всех программистов заменит ещё вчера.

UPD.: хотя ChatGPT правильно отвечает, что GIL там есть.

UPD2.: а, ну и ещё всякие IronPython/Jython, вроде, без GIL, на .NET/Java-овских примитивах синхронизации, хотя Питон без своих библиотек, а вместо этого с библиотеками от .NET или Java чё-то счастье сомнительное.

Так output получается чёрный и пустой, потому что np.nonzero пакует результат в tuple. Надо либо во второй строчке взять area_indices[0][1:], либо в первой использовать не nonzero, а flatnonzero, ну либо там же от обычного nonzero взять только 0-й элемент, уж не знаю, что эффективнее.

Так нормально? В смысле, цикл со всем содержимым на это заменить, результат идентичен:

areas = values[:, cv2.CC_STAT_AREA] - area_threshold
areas[0] = 0 # шоб не мешала
area_indices = np.nonzero(areas > 0)
output = np.isin(label_ids, area_indices).astype(np.uint8) * 255

В "Peopleware" (Tom DeMarco, Timothy Lister) был пассаж, этот пост как оттуда списан:

Заголовок спойлера

I was teaching an in-house design course some years
ago, when one of the upper managers buttonholed me to
request that I assess some of the people in the course
(his project staff). He was particularly curious about
one woman. It was obvious he had his doubts about her:
"I don't quite see what she adds to a project^she's not a
great developer or tester or much of anything." With a
little investigation, I turned up this intriguing fact:
During her twelve years at the company, the woman in
question had never worked on a project that had been
anything other than a huge success. It wasn't obvious
what she was adding, but projects always succeeded
when she was around. After watching her in class for a
week and talking to some of her co-workers, I came to
the conclusion that she was a superb catalyst. Teams
naturally jelled better when she was there. She helped
people communicate with each other and get along.
Projects were more fun when she was part of them.
When I tried to explain this idea to the manager, I
struck out. He just didn't recognize the role of catalyst
as essential to a project.

Фиг знает, по-моему, как минимум, не хуже последней картинки из поста. Но в обложке цвета присутствуют, её, вероятно, логичнее в HSV разложить (или ещё во что-нибудь) и по H пофильтровать (или по ещё чему-нибудь). Для чёрно-белых иллюстраций из тонких линий, которые, вероятно, и встречаются в тексте, понятно, медианный фильтр не сработает, как и для самого текста.

А мне как-то живой препод на экзамене, не ЕГЭ, старорежимном, снизил оценку на 1 балл из 5 за то, что прощёлкал клювом и не вынес константу за знак интеграла. Сука. Даже на результат же не влияло!

"ой, дети так волнуются, давайте не будем их мучить"

Попадалась советская периодика конца 80-х, когда никаким ЕГЭ ещё и не пахло, где подобное писали про обычные школьные экзамены. Помню карикатуру, не помню точно, где - "Огонёк"? "Крокодил"? "Пионерская правда"? - изображён сидящий на диванчике в депрессии подросток, а в прихожей мама встречает врача фразой: "Доктор, у сына завтра экзамен, помогите!"

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

Медианный фильтр, понятно, не будет работать - слишком тонкие буквы, и по той же причине не будет работать dilate/erode. Какого-нибудь фильтра на connected components нет в GIMP-е? Типа (не GIMP, конечно, но чем могу):

import cv2
import numpy as np

img_gs = cv2.imread("original.png", cv2.IMREAD_GRAYSCALE)

# чем выше - тем больше шума останется, но нам пофиг - 
# шум мелкий, удалим через фильтр по площади
threshold_value = 200
_, img_bw = cv2.threshold(img_gs, threshold_value, 255, cv2.THRESH_BINARY_INV)

analysis = cv2.connectedComponentsWithStats(img_bw, 4, cv2.CV_32S)
n_labels, label_ids, values, _ = analysis
output = np.zeros(img_gs.shape, dtype = np.uint8)

# 1 - объединяем все компоненты с площадью больше пороговой
area_threshold = 10
for i in range(1, n_labels):
    area = values[i, cv2.CC_STAT_AREA]
    if area > area_threshold:
        mask = (label_ids == i).astype(np.uint8) * 255
        output = cv2.bitwise_or(output, mask)

result = cv2.bitwise_not(output)
cv2.imwrite("output.png", result)

# 2 - чуть растопырив полученный выше результат, используем его
# как маску для исходного изображения, получая "антиалиасинг"
img_inv = cv2.bitwise_not(img_gs)
result2 = cv2.bitwise_and(img_inv, img_inv, mask = cv2.dilate(output, np.ones((3, 3))))
cv2.imwrite("output2.png", cv2.bitwise_not(result2))

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

Чтобы глазами приятней читать, можно чуть размазать этот результат и использовать в качестве маски на исходном изображении (см. # 2):

На обложке и медианный фильтр, мне кажется, должен неплохо работать.

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

Что-то более дурацкое, чем "Си-Си", существует, вообще?

Насчет стандартных хоткеев, это смотря, что Вы имеете под в виду этими горячими клавишами.

Стандартные, типа Ctrl-C, Ctrl-V. Вряд ли будет хорошо, если так просто взять и сделать, потому что чем тогда процесс убивать, и т. п., но, может, можно упороться в мульти-модальность, а-ля Vim - в одном режиме прям терминал-терминал, в другом - закос под текстовый редактор.

Information

Rating
2,010-th
Registered
Activity