Алгоритм для Деления списка чисел в 2 равных списка суммы

Используйте этот код, чтобы найти запись между двумя датами, используя $gte и $lt:

db.CollectionName.find({"whenCreated": {
    '$gte': ISODate("2018-03-06T13:10:40.294Z"),
    '$lt': ISODate("2018-05-06T13:10:40.294Z")
}});
23
задан Lakshman Prasad 21 May 2009 в 06:21
поделиться

14 ответов

Новое решение

Это поиск в ширину с эвристическим отбраковкой. Дерево ограничено глубиной игроков / 2. Лимит суммы игрока составляет totalcores / 2. При пуле игроков из 100 на решение потребовалось примерно 10 секунд.

def team(t):
    iterations = range(2, len(t)/2+1)

    totalscore = sum(t)
    halftotalscore = totalscore/2.0

    oldmoves = {}

    for p in t:
        people_left = t[:]
        people_left.remove(p)
        oldmoves[p] = people_left

    if iterations == []:
        solution = min(map(lambda i: (abs(float(i)-halftotalscore), i), oldmoves.keys()))
        return (solution[1], sum(oldmoves[solution[1]]), oldmoves[solution[1]])

    for n in iterations:
        newmoves = {}
        for total, roster in oldmoves.iteritems():
            for p in roster:
                people_left = roster[:]
                people_left.remove(p)
                newtotal = total+p
                if newtotal > halftotalscore: continue
                newmoves[newtotal] = people_left
        oldmoves = newmoves

    solution = min(map(lambda i: (abs(float(i)-halftotalscore), i), oldmoves.keys()))
    return (solution[1], sum(oldmoves[solution[1]]), oldmoves[solution[1]])

print team([90,200,100])
print team([2,3,10,5,8,9,7,3,5,2])
print team([1,1,1,1,1,1,1,1,1,9])
print team([87,100,28,67,68,41,67,1])
print team([1, 1, 50, 50, 50, 1000])

#output
#(200, 190, [90, 100])
#(27, 27, [3, 9, 7, 3, 5])
#(5, 13, [1, 1, 1, 1, 9])
#(229, 230, [28, 67, 68, 67])
#(150, 1002, [1, 1, 1000])

Также обратите внимание, что я попытался решить эту проблему, используя описание GS, но невозможно получить достаточно информации, просто сохраняя промежуточные итоги. И если вы сохранили и количество элементов и итогов, то это будет то же самое, что и это решение, за исключением того, что вы сохранили ненужные данные. Потому что вам нужно ограничить количество итераций n-1 и n до numplayers / 2.

У меня была старая исчерпывающая версия, основанная на биномиальных коэффициентах (посмотрите в истории). Он отлично решил пример задачи длины 10, но потом я увидел, что на соревновании были люди длиной до 100.

6
ответ дан 29 November 2019 в 01:55
поделиться

Ну, вы можете найти решение с процентной точностью за полиномиальное время, но для того, чтобы найти оптимальное (абсолютное минимальное различие) решение, задача является NP-полной. Это означает, что нет решения проблемы за полиномиальное время. В результате, даже при относительно небольшом списке чисел, он слишком трудоемок для решения. Если вам действительно нужно решение, взгляните на некоторые алгоритмы аппроксимации для этого.

http://en.wikipedia.org/wiki/Subset_sum_problem

2
ответ дан Sean Turner 29 November 2019 в 01:55
поделиться

Вы можете немного затянуть петлю, используя следующее:

def make_teams(que):
    que.sort()
    t1, t2 = []
    while que:
        t1.append(que.pop())
        if sum(t1) > sum(t2):
            t2, t1 = t1, t2
    print min(sum(t1),sum(t2)), max(sum(t1),sum(t2))
0
ответ дан 29 November 2019 в 01:55
поделиться

Для повышения производительности вы экономите вычисления, заменяя append () и sum () промежуточными итогами.

0
ответ дан 29 November 2019 в 01:55
поделиться
class Team(object):
    def __init__(self):
        self.members = []
        self.total = 0

    def add(self, m):
        self.members.append(m)
        self.total += m

    def __cmp__(self, other):
        return cmp(self.total, other.total)


def make_teams(ns):
    ns.sort(reverse = True)
    t1, t2 = Team(), Team()

    for n in ns:
        t = t1 if t1 < t2 else t2
        t.add(n)

    return t1, t2

if __name__ == "__main__":
    import sys
    t1, t2 = make_teams([int(s) for s in sys.argv[1:]])
    print t1.members, sum(t1.members)
    print t2.members, sum(t2.members)

>python two_piles.py 1 50 50 100
[50, 50] 100
[100, 1] 101
0
ответ дан 29 November 2019 в 01:55
поделиться

Динамическое программирование - это решение, которое вы ищете.

Пример с [4, 3, 10, 3, 2, 5]:

X-Axis: Reachable sum of group.        max = sum(all numbers) / 2    (rounded up)
Y-Axis: Count elements in group.       max = count numbers / 2       (rounded up)

      1  2  3  4  5  6  7  8  9 10 11 12 13 14
 1  |  |  |  | 4|  |  |  |  |  |  |  |  |  |  |       //  4
 2  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |
 3  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |
      1  2  3  4  5  6  7  8  9 10 11 12 13 14
 1  |  |  | 3| 4|  |  |  |  |  |  |  |  |  |  |       //  3
 2  |  |  |  |  |  |  | 3|  |  |  |  |  |  |  |
 3  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |
      1  2  3  4  5  6  7  8  9 10 11 12 13 14
 1  |  |  | 3| 4|  |  |  |  |  |10|  |  |  |  |       // 10
 2  |  |  |  |  |  |  | 3|  |  |  |  |  |10|10|
 3  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |
      1  2  3  4  5  6  7  8  9 10 11 12 13 14
 1  |  |  | 3| 4|  |  |  |  |  |10|  |  |  |  |       //  3
 2  |  |  |  |  |  | 3| 3|  |  |  |  |  |10|10|
 3  |  |  |  |  |  |  |  |  |  | 3|  |  |  |  |
      1  2  3  4  5  6  7  8  9 10 11 12 13 14
 1  |  | 2| 3| 4|  |  |  |  |  |10|  |  |  |  |       //  2
 2  |  |  |  |  | 2| 3| 3|  |  |  |  | 2|10|10|
 3  |  |  |  |  |  |  |  | 2| 2| 3|  |  |  |  |
      1  2  3  4  5  6  7  8  9 10 11 12 13 14
 1  |  | 2| 3| 4| 5|  |  |  |  |10|  |  |  |  |       //  5
 2  |  |  |  |  | 2| 3| 3| 5| 5|  |  | 2|10|10|
 3  |  |  |  |  |  |  |  | 2| 2| 3| 5| 5|  |  |
                                       ^

12 - нам повезло число! Обратный поиск для получения группы:

12 - 5 = 7        {5}
 7 - 3 = 4        {5, 3}
 4 - 4 = 0        {5, 3, 4}

Затем можно вычислить другой набор: {4,3,10,3,2,5} - {5,3,4} = {10,3,2}

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

BTW: Это называется задача о рюкзаке .

Если все веса (w1, ..., wn и W) равны неотрицательные целые числа, рюкзак проблема может быть решена в псевдополиномиальное время с использованием динамического программирование.

29
ответ дан 29 November 2019 в 01:55
поделиться

Обратите внимание, что это тоже эвристика, и я вынес сортировку из функции.

 def g(data):
   sums = [0, 0]
   for pair in zip(data[::2], data[1::2]):
     item1, item2 = sorted(pair)
     sums = sorted([sums[0] + item2, sums[1] + item1])
   print sums

data = sorted([2,3,10,5,8,9,7,3,5,2])
g(data)
1
ответ дан 29 November 2019 в 01:55
поделиться

В. Для мультимножества S целых чисел существует ли способ разделить S на два подмножества S1 и S2 так, чтобы сумма чисел в S1 была равна сумме чисел в S2?

A. Задача о разделении .

Удачи приблизительно. :)

2
ответ дан 29 November 2019 в 01:55
поделиться

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

import random
def f(data, nb_iter=20):
  diff = None
  sums = (None, None)
  for _ in xrange(nb_iter):
    random.shuffle(data)
    mid = len(data)/2
    sum1 = sum(data[:mid])
    sum2 = sum(data[mid:])
    if diff is None or abs(sum1 - sum2) < diff:
      sums = (sum1, sum2)
  print sums

Вы можете настроить nb_iter, если проблема больше.

Это решает все проблемы. упоминается выше почти всегда.

0
ответ дан 29 November 2019 в 01:55
поделиться

Поскольку списки должны быть равны, проблема вовсе не в NP.

Я разделил отсортированный список с помощью шаблона t1 <-que (1st, last), t2 <-que (2nd, last-1) ...

def make_teams2(que):
    que.sort()
    if len(que)%2: que.insert(0,0)
    t1 = []
    t2 = []
    while que:
        if len(que) > 2:
            t1.append(que.pop(0))
            t1.append(que.pop())
            t2.append(que.pop(0))
            t2.append(que.pop())
        else:
            t1.append(que.pop(0))
            t2.append(que.pop())
    print sum(t1), sum(t2), "\n"

Edit : Я полагаю, что это тоже неправильный метод. Неправильные результаты!

0
ответ дан 29 November 2019 в 01:55
поделиться

Это фактически РАЗДЕЛ, частный случай KNAPSACK.

Это NP Complete, с псевдополиномиальными алгоритмами dp. Псевдо в псевдополиноме относится к тому факту, что время выполнения зависит от диапазона весов.

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

1
ответ дан 29 November 2019 в 01:55
поделиться

Тестовый пример, в котором ваш метод не работает:

que = [1, 1, 50, 50, 50, 1000]

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

Вот код, который это делает

def make_teams(que):
    que.sort()
    que.reverse()
    if len(que)%2: que.insert(0,0)
    t1,t2 = [],[]
    while que:
        if abs(len(t1)-len(t2))>=len(que):
            [t1, t2][len(t1)>len(t2)].append(que.pop(0))
        else:
            [t1, t2][sum(t1)>sum(t2)].append(que.pop(0))
    print min(sum(t1),sum(t2)), max(sum(t1),sum(t2)), "\n"

if __name__=="__main__":
    que = [2,3,10,5,8,9,7,3,5,2]
    make_teams(que)
    que = [1, 1, 50, 50, 50, 1000]
    make_teams(que)

Это дает 27, 27 и 150, 1002, которые являются ответами, которые имеют смысл для я

Редактировать: В обзоре я обнаружил, что это на самом деле не работает, хотя, в конце концов, я не совсем уверен, почему. Я опубликую здесь свой тестовый код, так как он может быть полезен. Тест просто генерирует случайную последовательность с равными суммами, складывает их вместе и сравнивает (с печальными результатами).

Редактировать № 2: На основе примера, указанного Неизвестным, [87,100,28,67] , 68,41,67,1] , это ' Понятно, почему мой метод не работает. В частности, чтобы решить этот пример, два самых больших числа должны быть добавлены в одну и ту же последовательность, чтобы получить правильное решение.

def make_sequence():
    """return the sums and the sequence that's devided to make this sum"""
    while 1:
        seq_len = randint(5, 200)
        seq_max = [5, 10, 100, 1000, 1000000][randint(0,4)]
        seqs = [[], []]
        for i in range(seq_len):
            for j in (0, 1):
                seqs[j].append(randint(1, seq_max))
        diff = sum(seqs[0])-sum(seqs[1])
        if abs(diff)>=seq_max: 
            continue
        if diff<0:
            seqs[0][-1] += -diff
        else:
            seqs[1][-1] += diff
        return sum(seqs[0]), sum(seqs[1]), seqs[0], seqs[1]

if __name__=="__main__":

    for i in range(10):
        s0, s1, seq0, seq1 = make_sequence()
        t0, t1 = make_teams(seq0+seq1)
        print s0, s1, t0, t1
        if s0 != t0 or s1 != t1:
            print "FAILURE", s0, s1, t0, t1
1
ответ дан 29 November 2019 в 01:55
поделиться

В предыдущем комментарии я предположил, что проблема в том виде, в каком она была установлена, решаема, поскольку тщательно отобрали тестовые данные, чтобы они были совместимы с различными алгоритмами в отведенное время. Оказалось, что это не так - это проблема ограничений - числа не выше 450 и окончательный набор не более 50 чисел является ключевым. Они совместимы с решением проблемы с использованием решения динамического программирования, которое я разместил в более позднем посте. Ни один из других алгоритмов (эвристика или исчерпывающее перечисление с помощью комбинаторного генератора шаблонов), возможно, не может работать, потому что будут тестовые примеры, достаточно большие или достаточно сложные, чтобы сломать эти алгоритмы. Честно говоря, это довольно неприятно, потому что другие решения более сложные и, безусловно, более увлекательные. Обратите внимание, что без большой дополнительной работы решение динамического программирования просто говорит, возможно ли решение с N / 2 для любой заданной суммы, но не сообщает вам содержимое любого раздела.

0
ответ дан 29 November 2019 в 01:55
поделиться

Очевидно, они ищут ранцевое решение для динамического программирования. Итак, после моей первой попытки (я подумал, что это довольно хорошая исходная эвристика) и моей второй попытки (действительно хитрое точное комбинаторное решение, которое работало для небольших наборов данных и даже для наборов до 100 элементов, длина которых равна числу уникальные значения были низкими), я, наконец, уступил давлению сверстников и написал то, что они хотели (не слишком сложно - обработка дублированных записей была самой сложной частью - базовый алгоритм, на котором я его основал, работает только в том случае, если все входные данные уникальны - Я уверен, что long long достаточно велик, чтобы вместить 50 бит!).

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

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

Спасибо,

Грэм

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

#include <stdio.h>

#define TRUE (0==0)
#define FALSE (0!=0)

static int debug = TRUE;

//int simple(const void *a, const void *b) {
//  return *(int *)a - *(int *)b;
//}

int main(int argc, char **argv) {
  int p[101];
  char *s, line[128];
  long long mask, c0[45001], c1[45001];
  int skill, players, target, i, j, tests, total = 0;

  debug = (argc == 2 && argv[1][0] == '-' && argv[1][1] == 'd' && argv[1][2] == '\0');

  s = fgets(line, 127, stdin);
  tests = atoi(s);
  while (tests --> 0) {

    for (i = 0; i < 45001; i++) {c0[i] = 0LL;}

    s = fgets(line, 127, stdin); /* blank line */
    s = fgets(line, 127, stdin); /* no of players */
    players = atoi(s);
    for (i = 0; i < players; i++) {s = fgets(line, 127, stdin); p[i] = atoi(s);}

    if (players == 1) {
      printf("0 %d\n", p[0]);
    } else {

    if (players&1) p[players++] = 0; // odd player fixed by adding a single player of 0 strength
    //qsort(p, players, sizeof(int), simple);

    total = 0; for ( i = 0; i < players; i++) total += p[i];
    target = total/2; // ok if total was odd and result rounded down - teams of n, n+1
    mask = 1LL << (((long long)players/2LL)-1LL);

    for (i = 0; i < players; i++) {
      for (j = 0; j <= target; j++) {c1[j] = 0LL;} // memset would be faster
      skill = p[i];
      //add this player to every other player and every partial subset
      for (j = 0; j <= target-skill; j++) {
        if (c0[j]) c1[j+skill] = c0[j]<<1;  // highest = highest j+skill for later optimising
      }
      c0[skill] |= 1; // so we don't add a skill number to itself unless it occurs more than once
      for (j = 0; j <= target; j++) {c0[j] |= c1[j];}
      if (c0[target]&mask) break; // early return for perfect fit!
    }

    for (i = target; i > 0; i--) {
      if (debug || (c0[i] & mask)) {
        fprintf(stdout, "%d %d\n", i, total-i);
        if (debug) {
          if (c0[i] & mask) printf("******** ["); else
          printf("         [");
          for (j = 0; j <= players; j++) if (c0[i] & (1LL<<(long long)j)) printf(" %d", j+1);
          printf(" ]\n");
        } else break;
      }
    }
    }
    if (tests) printf("\n");
  }
  return 0;
}
1
ответ дан 29 November 2019 в 01:55
поделиться
Другие вопросы по тегам:

Похожие вопросы: