Сортировка предметов, но не позволяющих одноименным предметам находиться рядом друг с другом; также добавление рандомизации в наборах - PullRequest
0 голосов
/ 28 апреля 2020

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

Например, если у нас есть следующие необработанные данные:

Apple   10/02/2020
Apple   15/02/2020
Apple   10/03/2020
Apple   15/03/2020
Apple   10/04/2020
Apple   15/04/2020
Banana  16/03/2020
Banana  21/03/2020
Banana  16/04/2020
Orange  13/03/2020
Orange  15/03/2020

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

Banana  16/04/2020
Apple   15/04/2020
Orange  15/03/2020
Banana  21/03/2020
Apple   10/04/2020
Orange  13/03/2020
Banana  16/03/2020
Apple   15/03/2020
Apple   10/03/2020
Apple   15/02/2020
Apple   10/02/2020

Проблема: повторяющиеся "группы"

Единственное проблема с этим порядком сортировки состоит в том, что у нас есть повторяющиеся группы Banana, Apple, Orange снова и снова. Поэтому мы хотим при желании отсортировать группы в несколько рандомизированном порядке. Размер «групп» будет определяться числом, которое мы выберем; не по количеству элементов.

Так что, если мы установим «группу» на 3, она будет выглядеть в указанном выше порядке и рандомизировать каждый набор из 3, таким образом, выглядя следующим образом:

Orange  15/03/2020
Apple   15/04/2020
Banana  16/04/2020
---
Banana  21/03/2020
Orange  13/03/2020
Apple   10/04/2020
---
Banana  16/03/2020
Apple   15/03/2020
Apple   10/03/2020
---
Apple   10/02/2020
Apple   15/02/2020

Проблема: потеря «самого последнего».

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

Может быть, есть способ сохранить порядок первой группы и либо рандомизировать следующие группы, либо просто изменить порядок, чтобы группы порядок НЕ совпадает с порядком предыдущих групп; например:

Banana  16/04/2020
Apple   15/04/2020
Orange  15/03/2020
---
Apple   10/04/2020
Orange  13/03/2020
Banana  21/03/2020
---
Banana  16/03/2020
Apple   15/03/2020
Apple   10/03/2020
---
Apple   15/02/2020
Apple   10/02/2020

ИЛИ

Banana  16/04/2020
Apple   15/04/2020
Orange  15/03/2020
---
Banana  21/03/2020
Orange  13/03/2020
Apple   10/04/2020
---
Apple   15/03/2020
Banana  16/03/2020
Apple   10/03/2020
---
Apple   10/02/2020
Apple   15/02/2020

Ответы [ 2 ]

1 голос
/ 29 апреля 2020

Я решил думать об этом как о сортировке отдельных фруктов по дате, а затем об их чередовании. Если я настрою каждую свободную дату так, чтобы она зависела от следующей фруктовой даты во времени, но имела только временную зависимость между теми же фруктами; затем топологическая сортировка может быть использована для разделения элементов на подгруппы разных фруктов, как указано выше. Затем я мог бы упорядочить первую такую ​​группу по времени, а затем случайным образом остальные подгруппы.

Код:

Несколько сеансов показывают, что первые три элемента и последние три всегда то же самое, но с четвертого по шестое рандомизированы, как и с седьмого по восьмое.

Образцы прогонов:

AN ORDERING OF ITEMS:
  Banana  16/04/2020
  Apple   15/04/2020
  Orange  15/03/2020
  Apple   10/04/2020
  Banana  21/03/2020
  Orange  13/03/2020
  Apple   15/03/2020
  Banana  16/03/2020
  Apple   10/03/2020
  Apple   15/02/2020
  Apple   10/02/2020

runcell(2, 'C:/Users/Paddy3118/Google Drive/Code/fruit_orderings.py')

AN ORDERING OF ITEMS:
  Banana  16/04/2020
  Apple   15/04/2020
  Orange  15/03/2020
  Orange  13/03/2020
  Banana  21/03/2020
  Apple   10/04/2020
  Apple   15/03/2020
  Banana  16/03/2020
  Apple   10/03/2020
  Apple   15/02/2020
  Apple   10/02/2020

runcell(2, 'C:/Users/Paddy3118/Google Drive/Code/fruit_orderings.py')

AN ORDERING OF ITEMS:
  Banana  16/04/2020
  Apple   15/04/2020
  Orange  15/03/2020
  Banana  21/03/2020
  Apple   10/04/2020
  Orange  13/03/2020
  Apple   15/03/2020
  Banana  16/03/2020
  Apple   10/03/2020
  Apple   15/02/2020
  Apple   10/02/2020

runcell(2, 'C:/Users/Paddy3118/Google Drive/Code/fruit_orderings.py')

AN ORDERING OF ITEMS:
  Banana  16/04/2020
  Apple   15/04/2020
  Orange  15/03/2020
  Apple   10/04/2020
  Orange  13/03/2020
  Banana  21/03/2020
  Banana  16/03/2020
  Apple   15/03/2020
  Apple   10/03/2020
  Apple   15/02/2020
  Apple   10/02/2020

runcell(2, 'C:/Users/Paddy3118/Google Drive/Code/fruit_orderings.py')

AN ORDERING OF ITEMS:
  Banana  16/04/2020
  Apple   15/04/2020
  Orange  15/03/2020
  Orange  13/03/2020
  Apple   10/04/2020
  Banana  21/03/2020
  Apple   15/03/2020
  Banana  16/03/2020
  Apple   10/03/2020
  Apple   15/02/2020
  Apple   10/02/2020
0 голосов
/ 28 апреля 2020

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

Если вы можете разделить идентичные элементы, сохраняя порядок между ними, это довольно легко; сначала выполните сортировку по дате, а затем выполните разделение. Я не мог придумать существующую функцию, которая выполняет такое разделение, поэтому я взломал одну, которая работает «достаточно хорошо» для получения разумного результата:

from datetime import datetime
from typing import Any, Callable, List, Optional, TypeVar

_I = TypeVar('_I')


def declump(
    items: List[_I], 
    key: Optional[Callable[[_I], Any]] = None
) -> None:
    """
    Separate identical items by doing repeated swaps of the form:
    AAB -> ABA  (in-place)
    Existing ordering is preserved within sets of identical items,
    but not between non-identical items.
    fixme: we can end up with a clump at the very end of the list!
    Easy workaround is to reverse the list and declump again.
    """
    if key is None:
        key = lambda x: x
    i = 0
    key(items[i])
    while i < len(items) - 2:
        if (
            key(items[i]) == key(items[i+1])
            and key(items[i+1]) != key(items[i+2])
        ):
            items[i+1], items[i+2] = items[i+2], items[i+1]
            if i > 0:
                i -= 1
                continue
        i += 1


produce = [
    ('Apple', '10/02/2020'),
    ('Apple', '15/02/2020'),
    ('Apple', '10/03/2020'),
    ('Apple', '15/03/2020'),
    ('Apple', '10/04/2020'),
    ('Apple', '15/04/2020'),
    ('Banana', '16/03/2020'),
    ('Banana', '21/03/2020'),
    ('Banana', '16/04/2020'),
    ('Orange', '13/03/2020'),
    ('Orange', '15/03/2020'),
]


produce.sort(key=lambda t: datetime.strptime(t[1], r'%d/%m/%Y'), reverse=True)
produce.reverse()
declump(produce, key=lambda t: t[0])
produce.reverse()
declump(produce, key=lambda t: t[0])
print('\n'.join(t[0].ljust(8) + t[1] for t in produce))

производит:

Apple   15/04/2020
Banana  16/04/2020
Apple   10/04/2020
Banana  21/03/2020
Apple   15/03/2020
Banana  16/03/2020
Apple   10/03/2020
Orange  15/03/2020
Apple   15/02/2020
Orange  13/03/2020
Apple   10/02/2020

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

...