Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Алгоритмы > Интересная задача


Автор: sQu1rr 26.1.2015, 16:04
Здравствуйте уважаемы программисты

Сегодня столкнулся с задачей, для решения которой не могу придумать алгоритм.
Вот упрощенная версия

Есть некий массив
Код

[[1,2,3],[4,5],[5,6],[1,4,6]]


Нужно убрать дубликаты из внутренних массивов, но не оставлять их пустыми
Код

[[1,2,3],[4],[5],[6]]


Это был очень простой пример. Сложность начинается когда один элемент появляется в трех или более массивах. Сложно определить, какой элемент убрать из какого массива. Оптимальный результат - минимальное кол-во дубликатов (который нужно оставлять только в случае если без него получится пустой массив).

Почему то мне кажется что попахивает графами, но какими именно понять не могу.
Спасибо

Пример глупого алгоритма который я хочу избежать (шаг 1 - оставляем уникальные, шаг 2 - добавляем в пустые без какой либо логики, последовательно)
Код

[[1,2,3],[5,6],[1,4,6],[4,5]] # Оригинал
[[1,2,3],[],[],[]] # Шаг 1
[[1,2,3],[5,6],[4],[4 или 5]] # Шаг 2

Автор: Akina 26.1.2015, 16:13
Задача недоопределена. Допускаю, что из всех возможных конечных вариантов можно выбрать любой, но что делать, если конечного варианта без дубликата не существует (напр., 3 массива с элементами 1 и 2)?

Автор: sQu1rr 26.1.2015, 16:19
Цитата(Akina @  26.1.2015,  13:13 Найти цитируемый пост)
Допускаю, что из всех возможных конечных вариантов можно выбрать любой

да


Цитата(Akina @  26.1.2015,  13:13 Найти цитируемый пост)
если конечного варианта без дубликата не существует (напр., 3 массива с элементами 1 и 2)? 

значит будет либо что-то вроде [1],[2],[1] или 1,2,2
Главное - минимальное кол-во дубликатов, и что бы все элементы присутсвовали хотя бы один раз

Автор: baldina 26.1.2015, 17:42
если объединить массивы в граф, то достаточно построить остовное дерево. элементы, образующие циклы, удалятся.

Автор: sQu1rr 26.1.2015, 17:57
Цитата(baldina @  26.1.2015,  14:42 Найти цитируемый пост)
если объединить массивы в граф

Можно поподробнее? Именно этот момент меня и путает. что здесь будет вершинами и что ребрами?

Автор: baldina 26.1.2015, 18:16
user posted image
красным - удаляемые ребра. нужно следить, что бы в результате удаления средние узлы не превращались листья  (иначе получатся пустые массивы)

Автор: sQu1rr 26.1.2015, 18:21
Понял, спасибо, попробую отпишусь  smile

Добавлено @ 18:28
Так проблема в том что тут же зависит от того в какой последовательности я парсю граф, вот у вас, явно было слева направо, так все получается правильно. А если пойти справа налево, то получится как у меня в примере.

Цитата(baldina @  26.1.2015,  15:16 Найти цитируемый пост)
нужно следить, что бы в результате удаления средние узлы не превращались листья  (иначе получатся пустые массивы) 

Как раз вот тут вот у меня и проблема. скажем строю я дерево, наткнулся на такой результат, мне что, откатывать изменения? и до куда? это в итоге в брутфорс превратится

Я чего то не понимаю да?

ЗЫ можно вас попросить в след. раз картинку на форум выкладывать (кроме как ссылкой) как прикрепленный файл, для меня если. На работе блокировка на неизвесные/непроверенные сайты

Автор: Akina 27.1.2015, 12:27
Давайте определим несколько моментов.
1) Задача - реальная или учебная?
2) Каково максимально возможное количество групп?
3) Каково максимально возможное количество элементов?
Дело в том, что есть подозрение, что задача-то - полненькая... при небольшом количестве элементов и групп вполне можно обойтись и полным перебором.

Автор: sQu1rr 27.1.2015, 13:46
Цитата(Akina @  27.1.2015,  09:27 Найти цитируемый пост)
Задача - реальная или учебная?

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

Цитата(Akina @  27.1.2015,  09:27 Найти цитируемый пост)
2) Каково максимально возможное количество групп?
3) Каково максимально возможное количество элементов?
Дело в том, что есть подозрение, что задача-то - полненькая... при небольшом количестве элементов и групп вполне можно обойтись и полным перебором.

Разумеется можно, и, скорее всего, задача это подрузамевает. Но вопрос для меня - можно ли решить не перебором. Пусть не за полин. время, но и не за экспоненту

Автор: baldina 27.1.2015, 16:00
Цитата(Akina @  27.1.2015,  12:27 Найти цитируемый пост)
есть подозрение, что задача-то - полненькая

ага

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

Автор: Akina 27.1.2015, 17:43
В принципе задачу можно перевести в матричную.
Есть прямоугольная матрица. Столбцы - элементы, строки - группы. Если элемент в группе - на пересечении 1, иначе 0. Задача - удалить (заменить нулями) максимально возможное количество единиц так, чтобы в каждом столбце и каждой строке осталось не менее одной единицы.

Добавлено через 3 минуты и 24 секунды
Эмпирически можно пойти приблизительно по такому пути (типа жадный алгоритм) - ищем единицу такую, чтобы суммарное количество единиц в её строке и её столбце было максимально по матрице, но по отдельности не было единичным. Заменяем на ноль. Повторяем до тех пор, пока удаётся удалять. Способ не гарантирует оптимума, но даст близкое к нему решение, и достаточно быстр.

Автор: sQu1rr 27.1.2015, 18:12
Интересно, приду домой пораскину мозгами, спасибо

Автор: sQu1rr 29.1.2015, 15:34
Решил так кому интересно. Можно оптимизировать, но решение думаю понятно (работает со всеми моими тестами по крайней мере). Приду домой опишу логику
Код

def test_algo( tests, index = None):
    if not index:
        for index, test in enumerate( tests ):
            print '#', index, ' ======================'
            print 'Solving: ', test
            print 'Result:  ', algo( copy.deepcopy( test ) )
            print '==========================='
    else:
        print '#', index, ' ======================'
        print 'Solving: ', tests[ index ]
        print 'Result:  ', algo( copy.deepcopy( tests[ index ] ) )
        print '==========================='
    
def algo_group( flat_test, test ):
    group = dict( ( key, [] ) for key in flat_test )
    for index, test_group in enumerate( test ):
        for key in test_group:
            group[ key ].append( index )
    return group

def remove_duplicates( test ):
    seen     = set()
    seen_add = seen.add
    return [ x for x in test if not ( x in seen or seen_add( x ) ) ]
            
def remove_group_duplicates( test ):
    for test_group in test:
        remove_duplicates( test_group )
        
def has_empty( output ):
    for group in output:
        if not len( group ):
            return True
    return False

def has_unique( group ):
    for length in group.values():
        if len( length ) == 1:
            return True
    return False
    
def add_unique( output, group ):
    keys = group.keys()
    for key in keys:
        if len( group[ key ] ) == 1:
            group_index = group[ key ][0]
            output[ group_index ].append( key )
            del group[ key ]
            for groups in group.values():
                if len( groups ) > 1 and group_index in groups:
                    groups.remove( group_index )
            
def add_loop( output, test, group ):
    temp_test  = copy.deepcopy( test )
    temp_group = copy.deepcopy( group )

    path = find_loop( temp_test, temp_group, [], 0 )
    if path == 'False':
        raise 'Cannot find solution'
    else:
        for out_group, key in path:
            output[ out_group ].append( key )
            for groups in group.values():
                if len( groups ) > 1 and out_group in groups:
                    groups.remove( out_group )
    
def find_loop( test, group, path, index ):
    test_indices = test[ index ]
    for key in test_indices:
        test[ index ].remove( key )
        group[ key ].remove( index )
        indices = group[ key ]
        for group_index in indices:
            if not key in test[ group_index ]:
                continue
            test[ group_index ].remove( key )
            path.append( ( group_index, key ) )
            if group_index == 0:
                return path
            else:
                result = find_loop( test, group, path, group_index )
                if result != False:
                    return result
            path.remove( ( group_index, key ) )
            test[ group_index ].append( key )
        group[ key ].append( index )
        test[ index ].append( key )
    return False
        
def algo( test ):
    remove_group_duplicates( test )
    
    flat_test = sum( test, [] )
    group      = algo_group( flat_test, test )
    
    output    = [ [] for _ in xrange( len( test ) ) ]
    while has_empty( output ):
        while has_unique( group ):
            add_unique( output, group )
        if has_empty( output ):
            add_loop( output, test, group )
            
    final_output = [ [] for _ in xrange( len( test ) ) ]
    group = algo_group( flat_test, test )
    for index, test_group in enumerate( test ):
        for key in test_group:
            if key in output[ index ] and ( len( group[ key ] ) == 1 or len( final_output[ index ] ) == 0 ):
                final_output[ index ].append( key )
        
    return final_output


Код

test_algo([[[1, 2, 3], [4, 5], [5, 6], [1, 4, 6]], [[1, 2, 3], [5, 6], [1, 4, 6], [4, 5]], [[1, 2], [1, 2], [1, 2]], [[1, 1, 5, 8], [5], [3], [6, 3, 1]], [[17133159, 12777590, 9402381], [9402381, 17133159], [9402381], [8953819]]])

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)