Nano Hash - криптовалюты, майнинг, программирование

группировка объектов для достижения аналогичного среднего свойства для всех групп

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

Группы не обязательно будут иметь одинаковое количество членов, но размер групп будет находиться в пределах друг друга. Что касается количества, то будет от 50 до 100 объектов, а максимальный размер группы будет около 5.

Это известная проблема? Это немного похоже на проблему с рюкзаком или перегородкой. Известны ли эффективные алгоритмы для ее решения?

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

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

Спасибо за вашу помощь и предложения.


  • Это также немного похоже на проблему упаковки в мусорное ведро. Независимо от того, нет ли, вы должны ожидать, что это будет сложно с точки зрения вычислений, чтобы получить оптимальное решение. 16.12.2010

Ответы:


1

Вы можете попробовать использовать кластеризацию k-средних:

import scipy.cluster.vq as vq
import collections
import numpy as np

def auto_cluster(data,threshold=0.1,k=1):
    # There are more sophisticated ways of determining k
    # See http://en.wikipedia.org/wiki/Determining_the_number_of_clusters_in_a_data_set
    data=np.asarray(data)
    distortion=1e20
    while distortion>threshold:
        codebook,distortion=vq.kmeans(data,k)
        k+=1   
    code,dist=vq.vq(data,codebook)    
    groups=collections.defaultdict(list)
    for index,datum in zip(code,data):
        groups[index].append(datum)
    return groups

np.random.seed(784789)
N=20
weights=100*np.random.random(N)
groups=auto_cluster(weights,threshold=1.5,k=N//5)
for index,data in enumerate(sorted(groups.values(),key=lambda d: np.mean(d))):
    print('{i}: {d}'.format(i=index,d=data))

Приведенный выше код генерирует случайную последовательность из N весов. Он использует scipy.cluster.vq.kmeans чтобы разделить последовательность на k группы чисел, расположенных близко друг к другу. Если искажение выше порога, kmeans пересчитывается с увеличением k. Это повторяется до тех пор, пока искажение не станет ниже заданного порога.

Это дает кластеры, такие как это:

0: [4.9062151907551366]
1: [13.545565038022112, 12.283828883935065]
2: [17.395300245930066]
3: [28.982058040201832, 30.032607500871023, 31.484125759701588]
4: [35.449637591061979]
5: [43.239840915978043, 48.079844689518424, 40.216494950261506]
6: [52.123246083619755, 53.895726546070463]
7: [80.556052179748079, 80.925071671718413, 75.211470587171803]
8: [86.443868931310249, 82.474064251040375, 84.088655128258964]
9: [93.525705849369416]

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

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

16.12.2010
  • Спасибо за предложение об использовании алгоритма кластеризации. Однако я не понимаю, как этот подход будет работать: как упоминалось в моем исходном сообщении, мне нужно иметь определенное количество групп, каждая из которых будет иметь определенное количество участников. Эти числа вычисляются на основе заданного максимального количества объектов в группе, nmax, и требования, чтобы ни в одной группе не было меньше (nmax-1) объектов. Например, предположим, что у меня всего 28 объектов и требуется не более 5 объектов/групп. Это приводит к шести группам со следующим количеством объектов в группе: [5,5,5,5,4,4]. 16.12.2010
  • @cytochrome: О, я не знал, что к размеру группы предъявляются такие строгие требования. В этом случае вариантов решения действительно очень мало. Например, в приведенном выше примере решение должно заканчиваться тем, что размеры групп представляют собой некоторую перестановку [5,5,5,5,4,4]. (Есть только 6 choose 2 = 6*5/2 = 15 возможностей). Вы можете отсортировать веса и поместить их в группы в соответствии с каждой возможной перестановкой, а затем выбрать перестановку, обеспечивающую наименьшее искажение. 16.12.2010
  • Спасибо, унутбу. Хотя моя проблема больше, чем в примере, с ней все же можно справиться, используя рекомендованную вами стратегию. Мой текущий код уже делает часть этого, поэтому изменения должны быть простыми. 16.12.2010
  • ›› ни в одной группе не будет меньше (nmax-1) объектов. Что, если это 27 объектов и nmax==5? 30.12.2010

  • 2

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

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

    from random import randint, seed
    from itertools import cycle,chain
    
    def chunks(q, n):
        q = list(q)
        for i in range(0, len(q), n):
           yield q[i:i+n]
    
    def shuffle(q, n):
        q = list(q)
        m = len(q)//2
        left =  list(chunks(q[:m],n))
        right = list(chunks(reversed(q[m:]),n)) + [[]]
        return chain(*(a+b for a,b in zip(left, right)))
    
    def listarray(n):
        return [list() for _ in range(n)]
    
    def mean(q):
        return sum(q)/len(q)
    
    def report(q):
        for x in q:
            print mean(x), len(x), x
    
    SIZE = 5
    COUNT= 37
    
    #seed(SIZE)
    data = [randint(1,1000) for _ in range(COUNT)]
    data = sorted(data)
    NBUCKETS = (COUNT+SIZE-1) // SIZE
    
    order = shuffle(range(COUNT), NBUCKETS)
    posts = cycle(range(NBUCKETS))
    buckets = listarray(NBUCKETS)
    for o in order:
        i = posts.next()
        buckets[i].append(data[o])
    report(buckets)
    print mean(data)
    

    Сложность логарифмическая из-за шага сортировки. Это примеры результатов:

    439 5 [15, 988, 238, 624, 332]
    447 5 [58, 961, 269, 616, 335]
    467 5 [60, 894, 276, 613, 495]
    442 5 [83, 857, 278, 570, 425]
    422 5 [95, 821, 287, 560, 347]
    442 4 [133, 802, 294, 542]
    440 4 [170, 766, 301, 524]
    418 4 [184, 652, 326, 512]
    440
    

    Обратите внимание, что преобладает требование к размеру сегментов, а это означает, что средние значения не будут близкими, если дисперсия исходных данных велика. Вы можете попробовать с этим набором данных:

    data = sorted(data) + [100000]
    

    Ведро, содержащее 100000, получит как минимум еще 3 датума.

    Я пришел к такому эвристическому выводу, что это то, что сделала бы группа детей, если бы им дали пачку купюр разного номинала и попросили разделить их в соответствии с правилами этой игры. Это статистически разумно и O (log (N)).

    30.12.2010

    3

    Вы также можете попробовать алгоритм связи на основе центроида, который достигает того же.

    См. это для кода и это для понимания.

    UPGMA (на основе центроида) — это то, что вы, вероятно, захотите сделать.

    16.12.2010
    Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

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

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

    Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
    В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..