Нахождение разделов без сумм


17

Управляющее резюме

С учетом вводом k, найти разбиение чисел 1на nв kсвободных от сумм , подмножеств крупнейшего nвы можете в течение 10 минут.

Фон: числа Шура

Множество Aявляется свободным от суммы, если его самосумма A + A = { x + y | x, y in A}не имеет общих с ним элементов.

Для каждого положительного целого числа kсуществует наибольшее целое число S(k), так что набор {1, 2, ..., S(k)}может быть разбит на kподмножества без суммы. Это число называется к - й номер Щур (OEIS A045652 ).

Например, S(2) = 4. Мы можем разделить {1, 2, 3, 4}как {1, 4}, {2, 3}, и это уникальное разделение на два подмножества без суммы, но мы не можем теперь добавить ни 5к одной из частей.

Вызов

Напишите детерминированную программу, которая выполняет следующее:

  • Возьмите положительное целое число в kкачестве входных данных
  • Записать текущую метку времени Unix в стандартный вывод
  • Выводит последовательность разбиений 1to nв kподмножества без суммы для увеличения n, следуя каждой последовательности с текущей временной меткой Unix.

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

Важные детали:

  • У меня Ubuntu Precise, поэтому, если ваш язык не поддерживается, я не смогу его оценить.
  • У меня есть процессор Intel Core2 Quad, поэтому, если вы хотите использовать многопоточность, нет смысла использовать более 4 потоков.
  • Если вы хотите, чтобы я использовал какие-либо конкретные флаги или реализацию компилятора, запишите это в своем ответе.
  • Вы не должны использовать специальный код для обработки ввода 5.
  • Вы не обязаны выводить каждое улучшение, которое найдете. Например, для ввода 2вы можете вывести только раздел n = 4. Тем не менее, если вы ничего не выводите в первые 10 минут, я оцениваю это как n = 0.

Ответы:


8

Python 3, сортировка по наибольшему числу, n = 92 121

Спасибо Мартину Бюттнеру за предложение, которое неожиданно улучшило nдостигнутый максимум .

Последний вывод:

[2, 3, 11, 12, 29, 30, 38, 39, 83, 84, 92, 93, 110, 111, 119, 120]
[1, 4, 10, 13, 28, 31, 37, 40, 82, 85, 91, 94, 109, 112, 118, 121]
[5, 6, 7, 8, 9, 32, 33, 34, 35, 36, 86, 87, 88, 89, 90, 113, 114, 115, 116, 117]
[14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108]
[41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81]

Алгоритм такой же, как мой предыдущий ответ, приведенный ниже:

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

... с одним исключением: порядок бункеров не перемешивается. Вместо этого он сортируется таким образом, что бункеры с наибольшим числом идут первыми. Это достигнуто n = 121за 8 секунд!

Код:

from copy import deepcopy
from random import shuffle, seed
from time import clock, time
global maxN
maxN = 0
clock()

def search(k,nextN=1,sets=None):
    global maxN
    if clock() > 600: return

    if nextN == 1: #first iteration
        sets = []
        for i in range(k):
            sets.append([[],[]])

    sets.sort(key=lambda x:max(x[0]or[0]), reverse=True)
    for i in range(k):
        if clock() > 600: return
        if nextN not in sets[i][1]:
            sets2 = deepcopy(sets)
            sets2[i][0].append(nextN)
            sets2[i][1].extend([nextN+j for j in sets2[i][0]])
            nextN2 = nextN + 1

            if nextN > maxN:
                maxN = nextN
                print("New maximum!",maxN)
                for s in sets2: print(s[0])
                print(time())
                print()

            search(k, nextN2, sets2)

search(5)

Примечание: сортировка по наибольшему количеству разрешенных чисел в диапазоне запрещенных чисел дает n=59, а сортировка по наибольшему количеству разрешенных чисел меньше, чем nextNдает n=64. Сортировка по длине списка запрещенных номеров (который может иметь повторы) очень быстро приводит к элегантному n=30шаблону.
El'endia Starman

Неправильный формат выходного времени (должно быть секунд с начала эпохи, но я вижу Tue Nov 10 00:44:25 2015), но я видел n=92менее чем за 2 секунды.
Питер Тейлор

Ах, я подумал, что формат времени не так важен, как показ того, сколько именно времени прошло. Я пойму это и изменю это все же. РЕДАКТИРОВАТЬ: D'Oh. Я выбрал ctimeболее , timeпотому что выход был симпатичнее , когда timeбыло именно то , что я должен выбрал.
El'endia Starman

Знаете, вы также можете просто отсортировать по наибольшему числу в корзине, потому что наибольшее запрещенное число всегда будет вдвое больше.
Мартин Эндер

@ MartinBüttner: ...... Я ... э-э ... Понятия не имею, как и почему, но это происходит n=121. oO
Эльендия Старман

7

Python 3, 121, <0,001 с

Улучшенная эвристика благодаря Мартину Буттнеру означает, что нам даже не нужна случайность.

Выход:

1447152500.9339304
[1, 4, 10, 13, 28, 31, 37, 40, 82, 85, 91, 94, 109, 112, 118, 121]
[2, 3, 11, 12, 29, 30, 38, 39, 83, 84, 92, 93, 110, 111, 119, 120]
[5, 6, 7, 8, 9, 32, 33, 34, 35, 36, 86, 87, 88, 89, 90, 113, 114, 115, 116, 117]
[14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108]
[41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81]
1447152500.934646 121

Код:

from copy import deepcopy
from random import seed, randrange
from time import clock, time
from cProfile import run

n = 5

seed(0)

def heuristic(bucket):
    return len(bucket[0]) and bucket[0][-1]

def search():
    best = 0
    next_add = 1
    old_add = 0
    lists = [[[],set()] for _ in range(n)]
    print(time())
    while clock() < 600 and next_add != old_add:
        old_add = next_add
        lists.sort(key=heuristic, reverse=True)
        for i in range(n):
            if next_add not in lists[i][1]:
                lists[i][0].append(next_add)
                lists[i][1].update([next_add + old for old in lists[i][0]])
                if next_add > best:
                    best = next_add
                next_add += 1
                break

    for l in lists:
        print(l[0])
    print(time(), next_add-1, end='\n\n')

search()

Питон 3, 112

Сортировать по сумме первых 2 элементов + перекос

[40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79]
[7, 8, 9, 10, 11, 12, 13, 27, 28, 29, 30, 31, 32, 33, 80, 81, 82, 83, 84, 85, 86, 100, 101, 102, 103, 104, 105, 106]
[3, 4, 14, 19, 21, 26, 36, 37, 87, 92, 94, 99, 109, 110]
[2, 5, 16, 17, 20, 23, 24, 35, 38, 89, 90, 96, 97, 108, 111]
[1, 6, 15, 18, 22, 25, 34, 39, 88, 91, 93, 95, 98, 107, 112]
1447137688.032085 138.917074 112

Я скопировал структуру данных El'endia Starman, которая состоит из списка пар, где первый элемент пары - это элементы этого сегмента, а второй - суммы этого сегмента.

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

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

from copy import deepcopy
from random import seed, randrange
from time import clock, time

n = 5

seed(0)

def skew():
    return randrange(9)

best = 0
next_add = old_add = 1
while clock() < 600:
    if next_add == old_add:
        lists = [[[],[]] for _ in range(n)]
        next_add = old_add = 1
    old_add = next_add
    lists.sort(key=lambda x:sum(x[0][:2]) + skew(), reverse=True)
    for i in range(n):
        if next_add not in lists[i][1]:
            lists[i][0].append(next_add)
            lists[i][1].extend([next_add + old for old in lists[i][0]])
            if next_add > best:
                best = next_add
                for l in lists:
                    print(l[0])
                print(time(), clock(), next_add, end='\n\n')
            next_add += 1
            break

Вау, это выглядит очень похоже на мой код. : P;) (Я не против).
El'endia Starman

@ El'endiaStarman Кредит добавлен. Это хорошая основа.
Исаак

7

Java 8, n = 142 144

Последний вывод:

@ 0m 31s 0ms
n: 144
[9, 12, 17, 20, 22, 23, 28, 30, 33, 38, 41, 59, 62, 65, 67, 70, 72, 73, 75, 78, 80, 83, 86, 91, 107, 115, 117, 122, 123, 125, 128, 133, 136]
[3, 8, 15, 24, 25, 26, 31, 35, 45, 47, 54, 58, 64, 68, 81, 87, 98, 100, 110, 114, 119, 120, 121, 130, 137, 142]
[5, 13, 16, 19, 27, 36, 39, 42, 48, 50, 51, 94, 95, 97, 103, 106, 109, 112, 118, 126, 129, 132, 138, 140, 141]
[2, 6, 11, 14, 34, 37, 44, 53, 56, 61, 69, 76, 79, 84, 89, 92, 101, 104, 108, 111, 124, 131, 134, 139, 143, 144]
[1, 4, 7, 10, 18, 21, 29, 32, 40, 43, 46, 49, 52, 55, 57, 60, 63, 66, 71, 74, 77, 82, 85, 88, 90, 93, 96, 99, 102, 105, 113, 116, 127, 135]

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

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

примечание: выходные данные не являются строго детерминированными, поскольку в них задействовано несколько потоков, и они могут в конечном итоге обновить лучшее из nнайденных на данный момент в беспорядочном порядке; окончательная оценка 144 является детерминированной и достигается довольно быстро: 30 секунд на моем компьютере.

Код является:

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Deque;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

public class SumFree {

    private static int best;

    public static void main(String[] args) {
        int k = 5; // Integer.valueOf(args[0]);
        int numThreadsPeterTaylorCanHandle = 4;

        long start = System.currentTimeMillis();
        long end = start + TimeUnit.MINUTES.toMillis(10);

        System.out.println(start);

        Random rand = new Random("Lucky".hashCode());
        for (int i = 0; i < numThreadsPeterTaylorCanHandle; i++) {
            new Thread(() -> search(k, new Random(rand.nextLong()), start, end)).start();
        }
    }

    private static void search(int k, Random rand, long start, long end) {
        long now = System.currentTimeMillis();
        int localBest = 0;

        do {
            // create k empty partitions
            List<Partition> partitions = new ArrayList<>();
            for (int i = 0; i < k; i++) {
                partitions.add(new Partition());
            }

            Deque<Choice> pastChoices = new ArrayDeque<>();
            int bestNThisRun = 0;

            // try to fill up the partitions as much as we can
            for (int n = 1;; n++) {
                // list of partitions that can fit n
                List<Partition> partitionsForN = new ArrayList<>(k);
                for (Partition partition : partitions) {
                    if (!partition.sums.contains(n)) {
                        partitionsForN.add(partition);
                    }
                }

                // if we can't fit n anywhere then try to free up some space
                // by rearranging partitions
                Set<Set<Set<Integer>>> rearrangeAttempts = new HashSet<>();
                rearrange: while (partitionsForN.size() == 0 && rearrangeAttempts
                        .add(partitions.stream().map(Partition::getElements).collect(Collectors.toSet()))) {

                    Collections.shuffle(partitions, rand);
                    for (int candidateIndex = 0; candidateIndex < k; candidateIndex++) {
                        // partition we will try to free up
                        Partition candidate = partitions.get(candidateIndex);
                        // try to dump stuff that adds up to n into the other
                        // partitions
                        List<Integer> badElements = new ArrayList<>(candidate.elements.size());
                        for (int candidateElement : candidate.elements) {
                            if (candidate.elements.contains(n - candidateElement)) {
                                badElements.add(candidateElement);
                            }
                        }
                        for (int i = 0; i < k && !badElements.isEmpty(); i++) {
                            if (i == candidateIndex) {
                                continue;
                            }

                            Partition other = partitions.get(i);

                            for (int j = 0; j < badElements.size(); j++) {
                                int candidateElement = badElements.get(j);
                                if (!other.sums.contains(candidateElement)
                                        && !other.elements.contains(candidateElement + candidateElement)) {
                                    boolean canFit = true;
                                    for (int otherElement : other.elements) {
                                        if (other.elements.contains(candidateElement + otherElement)) {
                                            canFit = false;
                                            break;
                                        }
                                    }

                                    if (canFit) {
                                        other.elements.add(candidateElement);
                                        for (int otherElement : other.elements) {
                                            other.sums.add(candidateElement + otherElement);
                                        }
                                        candidate.elements.remove((Integer) candidateElement);
                                        badElements.remove(j--);
                                    }
                                }
                            }
                        }

                        // recompute the sums
                        candidate.sums.clear();
                        List<Integer> elementList = new ArrayList<>(candidate.elements);
                        int elementListSize = elementList.size();
                        for (int i = 0; i < elementListSize; i++) {
                            int ithElement = elementList.get(i);
                            for (int j = i; j < elementListSize; j++) {
                                int jthElement = elementList.get(j);
                                candidate.sums.add(ithElement + jthElement);
                            }
                        }

                        // if candidate can now fit n then we can go on
                        if (!candidate.sums.contains(n)) {
                            partitionsForN.add(candidate);
                            break rearrange;
                        }
                    }
                }

                // if we still can't fit in n, then go back in time to our last
                // choice (if it's saved) and this time choose differently
                if (partitionsForN.size() == 0 && !pastChoices.isEmpty() && bestNThisRun > localBest - localBest / 3) {
                    Choice lastChoice = pastChoices.peek();
                    partitions = new ArrayList<>(lastChoice.partitions.size());
                    for (Partition partition : lastChoice.partitions) {
                        partitions.add(new Partition(partition));
                    }
                    n = lastChoice.n;
                    Partition partition = lastChoice.unchosenPartitions
                            .get(rand.nextInt(lastChoice.unchosenPartitions.size()));
                    lastChoice.unchosenPartitions.remove(partition);
                    partition = partitions.get(lastChoice.partitions.indexOf(partition));
                    partition.elements.add(n);
                    for (int element : partition.elements) {
                        partition.sums.add(element + n);
                    }
                    if (lastChoice.unchosenPartitions.size() == 0) {
                        pastChoices.pop();
                    }
                    continue;
                }

                if (partitionsForN.size() > 0) {
                    // if we can fit in n somewhere,
                    // pick that somewhere randomly
                    Partition chosenPartition = partitionsForN.get(rand.nextInt(partitionsForN.size()));
                    // if we're making a choice then record it so that we may
                    // return to it later if we get stuck
                    if (partitionsForN.size() > 1) {
                        Choice choice = new Choice();
                        choice.n = n;
                        for (Partition partition : partitions) {
                            choice.partitions.add(new Partition(partition));
                        }
                        for (Partition partition : partitionsForN) {
                            if (partition != chosenPartition) {
                                choice.unchosenPartitions.add(choice.partitions.get(partitions.indexOf(partition)));
                            }
                        }
                        pastChoices.push(choice);

                        // only keep 3 choices around
                        if (pastChoices.size() > 3) {
                            pastChoices.removeLast();
                        }
                    }

                    chosenPartition.elements.add(n);
                    for (int element : chosenPartition.elements) {
                        chosenPartition.sums.add(element + n);
                    }
                    bestNThisRun = Math.max(bestNThisRun, n);
                }

                if (bestNThisRun > localBest) {
                    localBest = Math.max(localBest, bestNThisRun);

                    synchronized (SumFree.class) {
                        now = System.currentTimeMillis();

                        if (bestNThisRun > best) {
                            // sanity check
                            Set<Integer> allElements = new HashSet<>();
                            for (Partition partition : partitions) {
                                for (int e1 : partition.elements) {
                                    if (!allElements.add(e1)) {
                                        throw new RuntimeException("Oops!");
                                    }
                                    for (int e2 : partition.elements) {
                                        if (partition.elements.contains(e1 + e2)) {
                                            throw new RuntimeException("Oops!");
                                        }
                                    }
                                }
                            }
                            if (allElements.size() != bestNThisRun) {
                                throw new RuntimeException("Oops!" + allElements.size() + "!=" + bestNThisRun);
                            }

                            best = bestNThisRun;
                            System.out.printf("@ %dm %ds %dms\n", TimeUnit.MILLISECONDS.toMinutes(now - start),
                                    TimeUnit.MILLISECONDS.toSeconds(now - start) % 60, (now - start) % 1000);
                            System.out.printf("n: %d\n", bestNThisRun);
                            for (Partition partition : partitions) {
                                // print in sorted order since everyone else
                                // seems to to that
                                List<Integer> partitionElementsList = new ArrayList<>(partition.elements);
                                Collections.sort(partitionElementsList);
                                System.out.println(partitionElementsList);
                            }
                            System.out.printf("timestamp: %d\n", now);
                            System.out.println("------------------------------");
                        }
                    }
                }

                if (partitionsForN.size() == 0) {
                    break;
                }
            }
        } while (now < end);
    }

    // class representing a partition
    private static final class Partition {

        // the elements of this partition
        Set<Integer> elements = new HashSet<>();

        // the sums of the elements of this partition
        Set<Integer> sums = new HashSet<>();

        Partition() {
        }

        Partition(Partition toCopy) {
            elements.addAll(toCopy.elements);
            sums.addAll(toCopy.sums);
        }

        Set<Integer> getElements() {
            return elements;
        }
    }

    private static final class Choice {
        int n;
        List<Partition> partitions = new ArrayList<>();
        List<Partition> unchosenPartitions = new ArrayList<>();
    }
}

5

C, Случайный Жадный, n = 91

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

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

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

Вот раздел для n = 91:

1 5 12 18 22 29 32 35 46 48 56 59 62 69 72 76 79 82 86 89
2 3 10 11 16 17 25 30 43 44 51 52 57 64 71 83 84 90 91
6 8 13 15 24 31 33 38 40 42 49 54 61 63 65 77 81 88
9 14 19 21 27 34 37 45 60 67 70 73 75 78 80 85
4 7 20 23 26 28 36 39 41 47 50 53 55 58 66 68 74 87

И, наконец, вот код:

#include <stdlib.h>
#include <stdio.h>
#include <time.h>

#define MAX_K 10
#define MAX_N 1024

int main(int argc, char **argv) {
    if (argc < 2)
    {
        printf("Pass in k as a command-line argument");
        return 1;
    }

    printf("%u\n", (unsigned)time(NULL)); 

    int k = atoi(argv[1]);

    int sizes[MAX_K];
    int bins[MAX_K][MAX_N];
    int sums[MAX_K][2*MAX_N];
    int selection[MAX_K];
    int available_bins;

    int best = 0;

    srand(1447101176);

    while (1)
    {
        int i,j;
        for (i = 0; i < k; ++i)
            sizes[i] = 0;
        for (i = 0; i < k*MAX_N; ++i)
            bins[0][i] = 0;
        for (i = 0; i < k*MAX_N*2; ++i)
            sums[0][i] = 0;
        int n = 1;
        while (1)
        {
            available_bins = 0;
            for (i = 0; i < k; ++i)
                if (!sums[i][n])
                {
                    selection[available_bins] = i;
                    ++available_bins;
                }

            if (!available_bins) break;

            int bin = selection[rand() % available_bins];

            bins[bin][sizes[bin]] = n;
            ++sizes[bin];
            for (i = 0; i < sizes[bin]; ++i)
                sums[bin][bins[bin][i] + n] = 1;

            ++n;
        }

        if (n > best)
        {
            best = n;
            for (i = 0; i < k; ++i)
            {
                for (j = 0; j < sizes[i]; ++j)
                    printf("%d ", bins[i][j]);
                printf("\n");
            }
            printf("%u\n", (unsigned)time(NULL));
        }
    }

    return 0;
}

Подтверждено n=91, найдено за 138 секунд. Если это необходимо для разрыва связи, я сделаю время заново, чтобы избежать больших ошибок из-за разной загрузки процессора.
Питер Тейлор

3

С ++, 135

#include <iostream>
#include <cstdlib>
#include <ctime>
#include <cmath>
#include <set>
#include <vector>
#include <algorithm>


using namespace std;

vector<vector<int> > subset;
vector<int> len, tmp;
set<int> sums;

bool is_sum_free_with(int elem, int subnr) {
    sums.clear();
    sums.insert(elem+elem);
    for(int i=0; i<len[subnr]; ++i) {
        sums.insert(subset[subnr][i]+elem);
        for(int j=i; j<len[subnr]; ++j) sums.insert(subset[subnr][i]+subset[subnr][j]);
    }
    if(sums.find(elem)!=sums.end()) return false;
    for(int i=0; i<len[subnr]; ++i) if(sums.find(subset[subnr][i])!=sums.end()) return false;
    return true;
}

int main()
{
    int k = 0; cin >> k;

    int start=time(0);
    cout << start << endl;

    int allmax=0, cnt=0;
    srand(0);

    do {
        len.clear();
        len.resize(k);
        subset.clear();
        subset.resize(k);
        for(int i=0; i<k; ++i) subset[i].resize((int)pow(3, k));

        int n=0, last=0, c, y, g, h, t, max=0;
        vector<int> p(k);

        do {
            ++n;
            c=-1;
            for(int i=0; i++<k; ) {
                y=(last+i)%k;
                if(is_sum_free_with(n, y)) p[++c]=y;
            }

            if(c<0) --n;

            t=n;

            while(c<0) {
                g=rand()%k;
                h=rand()%len[g];
                t=subset[g][h];
                for(int l=h; l<len[g]-1; ++l) subset[g][l]=subset[g][l+1];
                --len[g];
                for(int i=0; i++<k; ) {
                    y=(g+i)%k;
                    if(is_sum_free_with(t, y) && y!=g) p[++c]=y;
                }
                if(c<0) subset[g][len[g]++]=t;
            }

            c=p[rand()%(c+1)];
            subset[c][len[c]++]=t;

            last=c;

            if(n>max) {
                max=n;
                cnt=0;
                if(n>allmax) {
                    allmax=n;
                    for(int i=0; i<k; ++i) {
                        tmp.clear();
                        for(int j=0; j<len[i]; ++j) tmp.push_back(subset[i][j]);
                        sort(tmp.begin(), tmp.end());
                        for(int j=0; j<len[i]; ++j) cout << tmp[j] << " ";
                        cout << endl;
                    }
                    cout << time(0) << " " << time(0)-start << " " << allmax << endl;
                }

            }

        } while(++cnt<50*n && time(0)-start<600);

        cnt=0;

    } while(time(0)-start<600);

    return 0;
}

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

Я прототипировал его в awk, и, поскольку он выглядел многообещающе, я перевел его на C ++, чтобы ускорить его. Использование std::setдолжно даже ускорить его больше.

Вывод для n = 135 (примерно через 230 секунд на моей [старой] машине)

2 6 9 10 13 17 24 28 31 35 39 42 43 46 50 57 61 68 75 79 90 94 97 101 105 108 119 123 126 127 130 131 134 
38 41 45 48 51 52 55 56 58 59 62 64 65 66 67 69 70 71 72 74 78 80 81 84 85 87 88 91 95 98 
5 12 15 16 19 22 23 25 26 29 33 36 73 83 93 100 103 107 110 111 113 114 117 120 121 124 
1 4 11 14 21 27 34 37 40 47 53 60 76 86 89 96 99 102 109 112 115 122 125 132 135 
3 7 8 18 20 30 32 44 49 54 63 77 82 92 104 106 116 118 128 129 133 

Я не перепроверил действительность, но все должно быть в порядке.


2

Python 3, случайный жадный, n = 61

Последний вывод:

[5, 9, 13, 20, 24, 30, 32, 34, 42, 46, 49, 57, 61]
[8, 12, 14, 23, 25, 44, 45, 47, 54]
[2, 6, 7, 19, 22, 27, 35, 36, 39, 40, 52, 53, 56]
[3, 10, 15, 16, 17, 29, 37, 51, 55, 59, 60]
[1, 4, 11, 18, 21, 26, 28, 31, 33, 38, 41, 43, 48, 50, 58]

Это эффективно использует тот же алгоритм, что и у Мартина Бюттнера , но я разработал его независимо.

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

from copy import deepcopy
from random import shuffle, seed
from time import clock, time
global maxN
maxN = 0
clock()
seed(0)

def search(k,nextN=1,sets=None):
    global maxN
    if clock() > 600: return

    if nextN == 1: #first iteration
        sets = []
        for i in range(k):
            sets.append([[],[]])

    R = list(range(k))
    shuffle(R)
    for i in R:
        if clock() > 600: return
        if nextN not in sets[i][1]:
            sets2 = deepcopy(sets)
            sets2[i][0].append(nextN)
            sets2[i][1].extend([nextN+j for j in sets2[i][0]])
            nextN2 = nextN + 1

            if nextN > maxN:
                maxN = nextN
                print("New maximum!",maxN)
                for s in sets2: print(s[0])
                print(time())
                print()

            search(k, nextN2, sets2)

search(5)

2

Python, n = 31

import sys
k = int(sys.argv[1])

for i in range(k):
    print ([2**i * (2*j + 1) for j in range(2**(k - i - 1))])

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

Во-первых, обратите внимание, что сумма любых двух нечетных чисел четна, поэтому мы можем вывести все нечетные числа в первом блоке. Затем, поскольку все оставшиеся числа четные, мы можем разделить их на 2. Еще раз, мы можем бросить все результирующие нечетные числа во втором блоке (после повторного умножения их на 2), разделить оставшиеся числа на 2 (т.е. , на 4 в целом), выбросить нечетные в третьем блоке (после повторного умножения их на 4) и так далее ... Или, чтобы выразить словами, которые вы, ребята, понимаете, мы ставим все числа, чей наименее значимый набор бит - это первый бит в первом блоке, все числа, младший значащий бит которого установлен во втором бите во втором блоке и т. д.

Для k блоков мы сталкиваемся с проблемой, как только мы достигаем n = 2 k , поскольку младший значащий установленный бит n является
( k + 1) -м битом, который не соответствует ни одному блоку. Другими словами, эта схема работает до
с п = 2 к - 1. Итак, в то время как для к = 5 мы только получаем мизерный п = 31 , это число растет экспоненциально с ростом к . Также установлено, что S ( k ) ≥ 2 k - 1 (но мы можем действительно найти лучший нижний предел, чем это довольно легко.)

Для справки вот результат для k = 5:

[1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31]
[2, 6, 10, 14, 18, 22, 26, 30]
[4, 12, 20, 28]
[8, 24]
[16]

Есть простой способ выжать лишнюю: переместить верхнюю половину нечетных чисел в любую другую категорию (поскольку их сумма должна быть больше любого числа, уже находящегося в этой категории) и добавить 2 ^ k в нижнюю половину нечетные числа. Эта же идея, вероятно, может быть расширена, чтобы получить еще одно lg k чисел или, может быть, даже другое k.
Питер Тейлор

@PeterTaylor Да, я понял вскоре после публикации, что это на самом деле довольно тривиально. Это эквивалентно выполнению [1], [2,3], [4,5,6,7], ..., что, вероятно, проще, только с обратным битом и порядком блоков. Легко увидеть, как это можно расширить.
Ell
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.