Znajdowanie partycji bez sumy

17

Streszczenie wykonawcze

Biorąc pod uwagę wejście kznajdziesz partycję liczb całkowitych 1, aby ndo kSUM-wolny podzbiorów dla największych nmożna w ciągu 10 minut.

Tło: liczby Schur

Zestaw Ajest sum, jeśli jego suma A + A = { x + y | x, y in A}nie ma z nim żadnych wspólnych elementów.

Dla każdej dodatniej liczby całkowitej kistnieje największa liczba całkowita, S(k)dzięki czemu zestaw {1, 2, ..., S(k)}można podzielić na kpodzbiory bez sum. Ten numer nazywa się k- tym numerem Schura (OEIS A045652 ).

Na przykład S(2) = 4. Możemy podzielić {1, 2, 3, 4}jako{1, 4}, {2, 3} , i jest to unikalny podział na dwa podzbiory bez sumy, ale nie możemy teraz dodać żadnego 5z nich.

Wyzwanie

Napisz program deterministyczny, który wykonuje następujące czynności:

  • Weź dodatnia kjako wejście
  • Zapisz aktualny znacznik czasu Unix na standardowe wyjście
  • Wysyła sekwencję partycji 1do nw kpodzestawy bez sumy w celu zwiększenia n, po każdej sekwencji z bieżącym znacznikiem czasu Unix.

Zwycięzcą zostanie program, który wydrukuje partycję największej nw ciągu 10 minut na moim komputerze po otrzymaniu danych wejściowych 5. Więzy zostaną zerwane przez najszybszy czas na znalezienie partycji dla największej n, uśrednionej dla trzech przebiegów: dlatego dane wyjściowe powinny zawierać znaczniki czasu.

Ważne szczegóły:

  • Mam Ubuntu Precise, więc jeśli twój język nie jest obsługiwany, nie będę mógł go ocenić.
  • Mam czterordzeniowy procesor Intel Core2, więc jeśli chcesz korzystać z wielowątkowości, nie ma sensu używać więcej niż 4 wątków.
  • Jeśli chcesz, żebym użył konkretnych flag kompilatora lub implementacji, udokumentuj to wyraźnie w swojej odpowiedzi.
  • Nie będziesz specjalnie kodować swojego kodu do obsługi danych wejściowych 5 .
  • Nie musisz przedstawiać każdej znalezionej poprawy. Np. Dla danych wejściowych 2można wyprowadzić tylko partycję n = 4. Jeśli jednak nie wyprowadzisz niczego w ciągu pierwszych 10 minut, zdobędę to jako n = 0.
Peter Taylor
źródło

Odpowiedzi:

8

Python 3, sortuj według największej liczby, n = 92 121

Podziękowania dla Martina Büttnera za sugestię, która nieoczekiwanie poprawiła nosiągnięte maksimum .

Ostatnie wyjście:

[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]

Algorytm jest taki sam jak moja poprzednia odpowiedź, cytowana poniżej:

Istnieją k-bin, które zawierają zarówno liczby jak i liczby, które nie mogą już w nim wchodzić. Na każdej głębokości iteracji (jest to w zasadzie wyszukiwanie od pierwszej głębokości) kolejność bin jest tasowana, a kolejna liczba (nextN) jest (sekwencyjnie) umieszczana w pojemnikach, które mogą ją wziąć, a następnie idzie o krok głębiej. Jeśli nie ma żadnych, wraca, tworząc kopię zapasową o jeden krok.

... z jednym wyjątkiem: kolejność bin nie jest tasowana. Zamiast tego jest sortowany w taki sposób, że pierwsze są pojemniki z największą liczbą . Osiągnięto to n = 121w 8 sekund!

Kod:

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)
El'endia Starman
źródło
Uwaga: sortowanie według największej liczby dozwolonych liczb w zakresie niedozwolonych liczb daje n=59i sortowanie według największej liczby dozwolonych liczb mniejszej niż nextNdaje n=64. Sortowanie według długości niedozwolonej listy numerów (która może zawierać powtórzenia) prowadzi bardzo szybko do eleganckiego n=30wzoru.
El'endia Starman
Format czasu wyjściowego jest niepoprawny (powinny być sekundy od epoki, ale widzę Tue Nov 10 00:44:25 2015), ale zobaczyłem n=92w mniej niż 2 sekundy.
Peter Taylor,
Ach, doszedłem do wniosku, że format czasu nie był tak ważny, jak pokazywanie dokładnie, ile czasu to zajęło. Rozpracuję to i zmienię. EDYCJA: D'oh. Podniosłem ctimesię time, ponieważ produkcja była ładniejsza, kiedy timebyło dokładnie to, co powinien wybrałem.
El'endia Starman
Wiesz, możesz również posortować według największej liczby w koszu, ponieważ największa niedozwolona liczba będzie zawsze dwa razy większa.
Martin Ender,
@ MartinBüttner: ...... Ja ... uh ... Nie mam pojęcia jak i dlaczego, ale to się dzieje n=121. oO
El'endia Starman
7

Python 3, 121, <0,001s

Ulepszona heurystyka dzięki Martinowi Buttnerowi oznacza, że ​​nie potrzebujemy nawet przypadkowości.

Wynik:

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

Kod:

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()

Python 3, 112

Sortuj według sumy pierwszych 2 elementów + pochylenia

[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

Skopiowałem strukturę danych El'endii Starman, która składa się z listy par, gdzie pierwszy element pary to elementy w tym segmencie, a drugi to sumy tego segmentu.

Zaczynam od tego samego podejścia „śledź, jakie kwoty są dostępne”. Moja heurystyka sortowania jest po prostu sumą dwóch najmniejszych elementów na danej liście. Dodam również małe, losowe pochylenie, aby wypróbować różne możliwości.

Każda iteracja po prostu umieszcza każdy nowy numer w pierwszym dostępnym pojemniku, podobnie jak losowy chciwy. Gdy to się nie powiedzie, po prostu uruchamia się ponownie.

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
isaacg
źródło
Wow, wygląda to bardzo podobnie do mojego kodu. : P;) (Nie mam nic przeciwko.)
El'endia Starman
@ Dodano kredyt El'endiaStarman. To dobra podstawa.
Isaacg,
7

Java 8, n = 142 144

Ostatnie wyjście:

@ 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]

Przeprowadza rozsiane losowe wyszukiwanie rozłożone na 4 wątki. Gdy nie może znaleźć odpowiedniej partycji n, próbuje zwolnić miejsce na njednej partycji naraz, zrzucając jak najwięcej z innej partycji.

edycja: poprawiono algorytm zwalniania miejsca dla n , dodano także możliwość powrotu do poprzedniego wyboru i ponownego wyboru.

Uwaga: wynik nie jest ściśle deterministyczny, ponieważ w grę wchodzi wiele wątków i mogą one ostatecznie zaktualizować najlepsze nznalezione do tej pory w pomieszanym porządku; ostateczny wynik 144 jest jednak deterministyczny i osiągany jest dość szybko: 30 sekund na moim komputerze.

Kod to:

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<>();
    }
}
SamYonnou
źródło
5

C, Random Greedy, n = 91

Aby zapewnić podstawowe rozwiązanie, iteruje się n, śledząc pojemniki oraz ich sumy i sumyn do losowego pojemnika, w którym nie pojawia się jeszcze jako suma. Kończy się raz npojawia się we wszystkich ksumach, a jeśli wynikn był lepszy niż jakakolwiek poprzednia próba, drukuje go do STDOUT.

Dane wejściowe k są dostarczane za pomocą argumentu wiersza polecenia. Maksymalne możliwe kjest obecnie zakodowane na 10, ponieważ byłem zbyt leniwy, aby dodać dynamiczny przydział pamięci, ale można to łatwo naprawić.

Chyba mógłbym teraz polować na lepsze ziarno, ale ta odpowiedź i tak prawdopodobnie nie jest szczególnie konkurencyjna, więc meh.

Oto partycja dla 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

I na koniec, oto kod:

#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;
}
Martin Ender
źródło
Potwierdzony n=91, znaleziony w 138 sekund. Jeśli to konieczne do zerwania remisu, zrestartuję, aby uniknąć dużych błędów z powodu różnych obciążeń procesora.
Peter Taylor,
3

C ++, 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;
}

Dołącza następny n do losowo wybranego podzbioru. Jeśli nie jest to możliwe, usuwa losowe liczby z podzbiorów i dołącza je do innych w nadziei, że pozwoli to gdzieś dołączyć n.

Prototypowałem to w awk, a ponieważ wyglądało to obiecująco, przetłumaczyłem to na C ++, aby przyspieszyć. Używaćstd::set powinno nawet przyspieszyć.

Wyjście dla n = 135 (po około 230 sekundach na mojej [starej] maszynie)

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 

Nie sprawdziłem ponownie ważności, ale powinno być w porządku.

Cabbie407
źródło
2

Python 3, losowy chciwy, n = 61

Ostatnie wyjście:

[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]

Wykorzystuje to skutecznie ten sam algorytm, co Martin Büttner , ale opracowałem go niezależnie.

Istnieją kpojemniki, które zawierają zarówno liczby, jak i liczby, które nie mogą już w nim wchodzić. Na każdej głębokości w iteracji (jest to w zasadzie wyszukiwanie od pierwszej głębokości) kolejność bin jest tasowana, a następna liczba ( nextN) jest (sekwencyjnie) umieszczana w pojemnikach, które mogą ją wziąć, a następnie idzie o krok głębiej. Jeśli nie ma żadnych, wraca, tworząc kopię zapasową o jeden krok.

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)
El'endia Starman
źródło
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))])

Ok, więc oczywiście nie jest to zwycięzca, ale czułem, że i tak tu należy. Pozwoliłem sobie nie uwzględniać znaczników czasu, ponieważ wygasają one natychmiast, a ponieważ nie jest to prawdziwy konkurent.

Najpierw zauważ, że suma dwóch dowolnych liczb nieparzystych jest parzysta, więc możemy zrzucić wszystkie nieparzyste liczby w pierwszym bloku. Następnie, ponieważ wszystkie pozostałe liczby są parzyste, możemy je podzielić przez 2. Ponownie możemy rzucić wszystkie uzyskane liczby nieparzyste w drugim bloku (po ponownym pomnożeniu ich przez 2), podzielić pozostałe liczby przez 2 (tj. , o 4 razem), rzuć nieparzyste w trzecim bloku (po ponownym pomnożeniu ich przez 4) i tak dalej ... Lub, mówiąc słowami, rozumiecie, umieszczamy wszystkie liczby, których najmniej znaczący zbiór bit to pierwszy bit w pierwszym bloku, wszystkie liczby, których najmniej znaczący ustawiony bit to drugi bit w drugim bloku i tak dalej ...

W przypadku k bloków wpadamy w kłopoty, gdy osiągniemy n = 2 k , ponieważ najmniej znaczący ustawiony bit n to
( k + 1) -ty bit, który nie odpowiada żadnemu blokowi. Innymi słowy, ten schemat działa
do n = 2 k - 1. Tak więc, podczas gdy dla k = 5 otrzymujemy tylko skromne n = 31 , liczba ta rośnie wykładniczo z k . Ustalono również, że S ( k ) ≥ 2 k - 1 (ale tak naprawdę możemy łatwo znaleźć niższy limit niż ten.)

Dla porównania, oto wynik dla 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]
Łokieć
źródło
Istnieje prosty sposób na wyciśnięcie dodatkowej: przenieś górną połowę liczb nieparzystych do dowolnej innej kategorii (ponieważ ich suma musi być większa niż dowolna liczba już w tej kategorii) i dodaj 2 ^ k do dolnej połowy liczby nieparzyste. Ten sam pomysł można prawdopodobnie rozszerzyć, aby uzyskać kolejne liczby kg, a może nawet inny k.
Peter Taylor,
@PeterTaylor Tak, wkrótce po opublikowaniu zdałem sobie sprawę, że jest to dość trywialne. Jest to równoważne z działaniem [1], [2,3], [4,5,6,7], ..., które prawdopodobnie jest prostsze, z odwrotną kolejnością bitów i bloków. Łatwo jest zobaczyć, jak można go rozszerzyć.
Ell,