От: | Erop | ||
Дата: | 26.02.20 17:42 | ||
Оценка: | 8 (2) |
"для начала решим более простую задачу: напишем генератор таких пар подмножеств." | |||||||||||||||||||
Начнём с того, что построим список пар размеров подмножеств. Можем заметить, что может можно написать и поумнее, но мне лень, тем более, что время работы этого генератора, очевидно, очень маленькое.
Напишем ещё несколько запчастей. Для того, что бы облегчить себе отсечения множеств, отличающихся только порядком элементов, потребуем, что бы наши элементы были 1) все разными 2) все упорядочены по < Генерировать будем так. Будем строить по одному наборы подмножеств одного размера. При этом запоминать будем не пары подмножеств, а тройки, так как нас интересуют ещё и те элементы, которые ещё можно добавлять. Кроме того, давайте договоримся, не ограничивая общности, что 1) Пустым может быть только правое множество 2) Самый маленький элемент всегда находится в левом множестве 4) Сами множества будем строить упорядоченными, что бы не получать дубликаты, отличающиеся только порядком элементов. То есть в терминах python'а это будут списки 5) Левое и правое подмножества не могут пересекаться. 6) В целях экономии памяти, третье подмножество (подмножество кандидатов) будем вычислять не каждый раз, а пореже, осуществляя доп. фильтрацию при расширении подмножества Итак, начнём с функции, которая из набора упорядочиваемых уникальных элементов строит набор троек упорядоченных подмножеств, таких, что в левом один элемент, правое пустое, и в остатке есть только элементы, превышающие элемент левого подмножества Потом пишем функцию, которая берёт на вход список троек и всеми допустимыми способами расширяет левое (первое) подмножество, элементами третьего И аналогичную функцию для расширения правого (второго) подмножества. Функция для правого чуть сложнее, так как из (1) следует, что левое пустым быть не может, а правое может (2) нам обеспечит изначальное построение соответствующих остатков (третьих множеств) (5), а так же сокращение перебора в самых массовых подмножествах с относительно коротким остатком, нам обеспечит фильтрация остатка для каждой исходной тройки
Ну и теперь можем писать сам генератор подмножеств. Предлагается генерировать подмножества группами, в каждой из которых их размеры одинаковые. При этом мы так написали _get_subsets_sizes, что обычно можем генерировать новую группу на основе уже сгенерированных, пополняя левое или правое подмножество. (мы всегда можем это сделать, так как все наборы с меньшей суммой размеров подмножеств мы генерируем до того, как наборы с большей) К сожалению, из этого правила есть исключение: это случай, когда max_diff == 0, так как в этом случае мы не можем увеличить сумму на 1. И нам придётся в этом случае "преодолевать пропасть в два прыжка"
Итого, нам осталось написать только сам перебор взвешиваний. Самую муторнуя часть -- генератор варинтов расположения монет на чашках весов, мы уже написали! | |||||||||||||||||||
COINS_COUNT = 12 # число монет
SCALE_RESULTS = "<=>" # возможные результаты взвешивания
ALL_COINS = set(range(1,COINS_COUNT+1)) # просто множество всех имеющихся у нас монет
ALL_STATES = ALL_COINS | {-s for s in ALL_COINS} # а это множество возможных исходов.
MAX_SPLIT_SIZE = COINS_COUNT // 3 + 1 # Это эвристика, на самом деле её, наверное можно доказать, но можно попробовать запустить с COINS_COUNT.
def _scale_all_states( left, right, states ) :
"""
разбить множество состояний на случаи < = >
"""
res = { "count":None, '<':[], '=':[], '>':[] }
for s in states :
if -s in left or s in right :
res['<'].append( s )
elif s in left or -s in right :
res['>'].append( s )
else :
res['='].append( s )
return res
"проверим, что получилось" | |
Печатает:
| |
def get_scale_strategy( states, max_count, coins_count=None, max_split_size=MAX_SPLIT_SIZE ) :
"""
Возвращает стратегию взвешинвания на двухчашесных весах, или None, если стратегии нет
позволяющую найти отличающуюся по весу единственную фальшивую монету в наборе
states -- множество возвожных исходов
max_count - максимальное число взвешиваний
coins_count -- общее число монет. Еслидостоверно нефальшивых сначала нет, то определяется по states
"""
if len(states) <= 1 : # нечего решать, возвращаем некий формальный результат
return {"count":0, "vs":(set(),set()), "<":[], "=":list(states), ">":[]}
if len(states) > 3**max_count :
return None #отсекаем, так как это множество состояний за оставшееся число взвешиваний не разделяется
unknown = {abs(c) for c in states} # множество монет, про которые пока не ясно, фальшивые ли они
coins_count = coins_count or len( unknown )
for l, r in gen_sorted_pairs(unknown, coins_count - len(unknown), max_split_size) :
scale_res = _scale_all_states( l, r, states )
#if max_count == 3 and len(l) == 4 and len(r) == 4:
# print( l, r, scale_res )
max_state_len = max( len(scale_res[k]) for k in SCALE_RESULTS )
if max_state_len > 3**(max_count-1) :
continue #отсекаем, так как это множество состояний за оставшееся число взвешиваний не разделяется
if max_state_len <= 1 : # Мы нашли решение за одно взвешивание!!!
scale_res["count"] = 1
return scale_res
#if max( len(scale_res[k]) for k in SCALE_RESULTS ) == 0:
# continue #Это только эвристика, но правдоподобная!
if max_count <= 1 :
continue # больше ничего сделать нельзя
# Итак, с отсечениями покончено, давайте запускать поиск стратегии рекурсивно!
scale_count = 0
for k in SCALE_RESULTS :
substrategy = get_scale_strategy( scale_res[k], max_count-1, coins_count, max_split_size )
if substrategy is None : # разделить не удалось!
scale_res = None
break
scale_res[k] = substrategy
scale_count = max(substrategy["count"], scale_count)
if scale_res : # Нашли решение для всех трёх результатов взвешивания!
scale_res["count"] = scale_count + 1
return scale_res
return None
"проверим, что получилось" | |
Печатает (174ms):
а
за 2 секунды находит, что решения нет | |
"полная версия кода" | |
| |
От: | Erop | ||
Дата: | 27.02.20 09:29 | ||
Оценка: |
"полная версия кода" | |
| |
COINS_COUNT = 13
ALL_COINS = set(range(1,COINS_COUNT+1))
ALL_STATES = ALL_COINS | {-s for s in ALL_COINS}
get_scale_strategy( ALL_STATES, 3, COINS_COUNT+16, 5 )
От: | Vain | google.ru | |
Дата: | 09.03.20 05:48 | ||
Оценка: |
От: | Vain | google.ru | |
Дата: | 09.03.20 06:02 | ||
Оценка: |
От: | Erop | ||
Дата: | 10.03.20 07:32 | ||
Оценка: |
От: | Erop | ||
Дата: | 10.03.20 07:34 | ||
Оценка: |
От: | Vain | google.ru | |
Дата: | 10.03.20 16:23 | ||
Оценка: |
От: | Vain | google.ru | |
Дата: | 10.03.20 16:59 | ||
Оценка: |
o o o o
z1 z2 z3 z4
(A)
o o x o o
z1 z2 z3 x1
(B)
o o x o o
z1 z3 z4 x1
монета | (A) | (B)
-------+-----+-----
z1 > < | > < | > <
z2 > < | > < | =
z3 > < | > < | < >
z4 > < | = | < >
От: | Erop | ||
Дата: | 11.03.20 06:49 | ||
Оценка: |
(A) | (B) | монета
-----+-----+--------
< | < | z1, <
< | = | z2, <
< | > | z3, >
= | < | z4, >
= | = | НЕВОЗМОЖНО
= | > | z4, <
> | < | z3, <
> | = | z2, >
> | > | z1, >
От: | Vain | google.ru | |
Дата: | 11.03.20 09:46 | ||
Оценка: |
От: | Erop | ||
Дата: | 11.03.20 10:50 | ||
Оценка: |
От: | Vain | google.ru | |
Дата: | 11.03.20 12:03 | ||
Оценка: |
От: | Khimik | ||
Дата: | 11.03.20 14:40 | ||
Оценка: | +1 |
От: | NovaMind | ||
Дата: | 22.03.20 08:14 | ||
Оценка: |