Summenfreie Partitionen finden


17

Zusammenfassung

Gegeben Eingang kfinden Sie eine Partition von ganzen Zahlen 1auf nin kSumme freie Teilmengen für die größte nSie innerhalb von 10 Minuten kann.

Hintergrund: Schurnummern

Eine Menge Aist summenfrei, wenn ihre Selbstsumme A + A = { x + y | x, y in A}keine Elemente gemeinsam hat.

Für jede positive Ganzzahl kgibt es eine größte Ganzzahl S(k), sodass die Menge {1, 2, ..., S(k)}in ksummenfreie Teilmengen aufgeteilt werden kann. Diese Nummer wird die k genannt th Schur Nummer (OEIS A045652 ).

Zum Beispiel S(2) = 4. Wir können {1, 2, 3, 4}als partitionieren {1, 4}, {2, 3}, und das ist die eindeutige Partition in zwei summenfreie Teilmengen, aber wir können jetzt keinem 5der beiden Teile eine hinzufügen .

Herausforderung

Schreiben Sie ein deterministisches Programm , das Folgendes ausführt:

  • Nehmen Sie eine positive ganze Zahl kals Eingabe
  • Schreiben Sie den aktuellen Unix-Zeitstempel in stdout
  • Gibt nach jeder Sequenz mit dem aktuellen Unix-Zeitstempel eine Folge von Partitionen von 1bis nin ksummenfreie Teilmengen zum Vergrößern aus n.

Der Gewinner ist das Programm, das ninnerhalb von 10 Minuten nach Eingabe eine Partition für die größte Partition auf meinem Computer druckt 5. Die Verbindungen werden zum schnellsten Zeitpunkt unterbrochen, um eine Partition für die größte zu finden n, gemittelt über drei Läufe: Aus diesem Grund sollte die Ausgabe Zeitstempel enthalten.

Wichtige Details:

  • Ich habe Ubuntu Precise. Wenn Ihre Sprache nicht unterstützt wird, kann ich sie nicht bewerten.
  • Ich habe eine Intel Core2 Quad-CPU. Wenn Sie also Multithreading verwenden möchten, macht es keinen Sinn, mehr als 4 Threads zu verwenden.
  • Wenn Sie möchten, dass ich bestimmte Compiler-Flags oder Implementierungen verwende, dokumentieren Sie dies in Ihrer Antwort.
  • Sie dürfen Ihren Code für die Eingabe nicht als Sonderfall behandeln 5.
  • Sie müssen nicht jede Verbesserung ausgeben, die Sie finden. ZB für die Eingabe können 2Sie nur die Partition für ausgeben n = 4. Wenn Sie jedoch in den ersten 10 Minuten nichts ausgeben, werde ich dies als bewerten n = 0.

Antworten:


8

Python 3, sortiere nach der größten Zahl, n = 92 121

Vielen Dank an Martin Büttner für einen Vorschlag, der das nerreichte Maximum unerwartet verbessert hat .

Letzte Ausgabe:

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

Der Algorithmus ist derselbe wie meine vorherige Antwort, die unten zitiert wird:

Es gibt k Behälter, in denen sowohl die Zahlen als auch die Zahlen enthalten sind, die nicht mehr enthalten sind. Bei jeder Tiefe in der Iteration (im Grunde handelt es sich um eine Tiefensuche) wird die Sortierung der Behälter gemischt, und die nächste Zahl (nextN) wird (sequentiell) in die Behälter gelegt, die sie aufnehmen können, und geht dann einen Schritt tiefer. Wenn es keine gibt, kehrt es zurück und sichert einen Schritt.

... mit einer Ausnahme: Die Behälterbestellung wird nicht gemischt. Stattdessen wird es so sortiert , dass die Behälter mit der größten Anzahl zuerst kommen. Dies erreichte n = 121in 8 Sekunden!

Code:

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)

Hinweis: Das Sortieren nach der größten Anzahl zulässiger Zahlen im Bereich der nicht zulässigen Zahlen ergibt n=59und das Sortieren nach der größten Anzahl zulässiger Zahlen nextNergibt weniger als n=64. Das Sortieren nach der Länge der Liste nicht zugelassener Nummern (die Wiederholungen enthalten kann) führt sehr schnell zu einem eleganten n=30Muster.
El'endia Starman

Das Ausgabezeitformat ist nicht korrekt (sollte Sekunden seit der Epoche sein, aber ich sehe es Tue Nov 10 00:44:25 2015), aber ich habe es n=92in weniger als 2 Sekunden gesehen.
Peter Taylor

Ah, ich stellte mir vor, dass das Zeitformat nicht so wichtig war wie die genaue Dauer. Ich werde es herausfinden und es aber ändern. EDIT: D'oh. Ich ctimeübernahm, timeweil die Ausgabe schöner war, als timegenau das war, was ich hätte auswählen sollen.
El'endia Starman

Sie können auch einfach nach der größten Zahl in der Tonne sortieren, da die größte unzulässige Zahl immer doppelt so groß ist.
Martin Ender

@ MartinBüttner: ...... Ich ... äh ... ich habe keine Ahnung wie oder warum, aber das wird n=121. oO
El'endia Starman

7

Python 3, 121, <0,001 s

Verbesserte Heuristik dank Martin Buttner bedeutet, dass wir nicht einmal Zufälligkeit brauchen.

Ausgabe:

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

Code:

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

Nach Summe der ersten 2 Elemente + Versatz sortieren

[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

Ich habe die Datenstruktur von El'endia Starman kopiert, die aus einer Liste von Paaren besteht, wobei das erste Element des Paares die Elemente in diesem Bucket und das zweite die Summen dieses Bucket sind.

Ich beginne mit dem gleichen Ansatz "Verfolge, welche Summen verfügbar sind". Meine Sortierheuristik ist einfach die Summe der kleinsten zwei Elemente in einer gegebenen Liste. Ich füge auch einen kleinen zufälligen Versatz hinzu, um verschiedene Möglichkeiten auszuprobieren.

Bei jeder Iteration wird einfach die neue Nummer in die erste verfügbare Bin gelegt, ähnlich wie bei zufälliger Gier. Sobald dies fehlschlägt, wird es einfach neu gestartet.

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

Wow, das sieht meinem Code sehr ähnlich. : P;) (Ich habe überhaupt nichts dagegen.)
El'endia Starman

@ El'endiaStarman Guthaben hinzugefügt. Es ist eine schöne Basis.
isaacg

7

Java 8, n = 142 144

Letzte Ausgabe:

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

Führt eine gesetzte zufällige Suche durch, die auf 4 Threads verteilt ist. Wenn keine passende Partition gefunden wird, nwird versucht, Speicherplatz für jeweils neine Partition freizugeben, indem so viel wie möglich auf die anderen Partitionen kopiert wird.

Bearbeiten: Der Algorithmus zum Freigeben von Speicherplatz wurde überarbeitet n. Außerdem wurde die Möglichkeit hinzugefügt, zu einer vorherigen Auswahl zurückzukehren und erneut eine Auswahl zu treffen.

Hinweis: Die Ausgabe ist nicht streng deterministisch, da mehrere Threads beteiligt sind und die besten Threads, die bisher ngefunden wurden, möglicherweise in unübersichtlicher Reihenfolge aktualisiert werden. Der Endwert von 144 ist jedoch deterministisch und wird ziemlich schnell erreicht: 30 Sekunden auf meinem Computer.

Code ist:

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, Random Greedy, n = 91

Nur um eine Basislösung bereitzustellen, wird diese wiederholt n, wobei die Klassen und ihre Summen verfolgt und neiner zufälligen Klasse hinzugefügt werden, in der sie noch nicht als Summe angezeigt wird. Es wird nin allen kSummen einmal beendet. Wenn das Ergebnis nbesser war als alle vorherigen Versuche, wird es an STDOUT ausgegeben.

Die Eingabe kerfolgt über ein Befehlszeilenargument. Das maximal mögliche kist derzeit auf 10 fest codiert, da ich zu faul war, dynamische Speicherzuweisung hinzuzufügen, aber das konnte leicht behoben werden.

Ich denke, ich könnte jetzt auf die Jagd nach einem besseren Samen gehen, aber diese Antwort ist wahrscheinlich sowieso nicht besonders wettbewerbsfähig, also meh.

Hier ist die Partition für 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

Und zum Schluss hier der Code:

#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;
}

Bestätigt n=91, gefunden in 138 Sekunden. Wenn es nötig ist, um das Unentschieden zu beenden, stelle ich eine Pause ein, um große Fehler aufgrund unterschiedlicher CPU-Auslastung zu vermeiden.
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;
}

Hängt das nächste n an eine zufällig ausgewählte Untermenge an. Wenn dies nicht möglich ist, werden Zufallszahlen aus Teilmengen entfernt und an andere angehängt, in der Hoffnung, dass dadurch irgendwo n angehängt werden kann.

Ich habe das in awk als Prototyp erstellt und es, da es vielversprechend aussah, in C ++ übersetzt, um es zu beschleunigen. Die Verwendung von a std::setsollte die Geschwindigkeit sogar noch erhöhen.

Ausgabe für n = 135 (nach ca. 230 Sekunden auf meiner [alten] Maschine)

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 

Ich habe die Gültigkeit nicht überprüft, aber es sollte in Ordnung sein.


2

Python 3, zufällig gierig, n = 61

Letzte Ausgabe:

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

Dies verwendet effektiv den gleichen Algorithmus wie Martin Büttner , aber ich habe ihn unabhängig entwickelt.

Es gibt kBehälter, in denen sowohl die Zahlen als auch die Zahlen enthalten sind, die nicht mehr verwendet werden können. Bei jeder Tiefe in der Iteration (im Grunde handelt es sich um eine Tiefensuche) wird die Sortierung der Behälter gemischt, und die nächste Zahl ( nextN) wird (nacheinander) in die Behälter gelegt, die sie aufnehmen können, und geht dann einen Schritt tiefer. Wenn es keine gibt, kehrt es zurück und sichert einen Schritt.

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

Ok, es ist offensichtlich kein Gewinner, aber ich hatte das Gefühl, dass es trotzdem hierher gehört. Ich habe mir erlaubt, keine Zeitstempel einzuschließen, da diese sofort beendet werden und es sich nicht um einen echten Konkurrenten handelt.

Beachten Sie zunächst, dass die Summe von zwei ungeraden Zahlen gerade ist, sodass wir alle ungeraden Zahlen im ersten Block ausgeben können. Dann, da alle verbleibenden Zahlen gerade sind, können wir sie durch 2 teilen. Noch einmal können wir alle resultierenden ungeraden Zahlen in den zweiten Block werfen (nachdem wir sie erneut mit 2 multipliziert haben), die verbleibenden Zahlen durch 2 teilen (dh Wirf die ungeraden Zahlen in den dritten Block (nachdem du sie mit 4 multipliziert hast) und so weiter. Oder, um es in Worte zu fassen, wir setzen alle Zahlen, deren niedrigstwertige Menge bit ist das erste Bit im ersten Block, alle Zahlen, deren niedrigstwertiges gesetztes Bit das zweite Bit im zweiten Block ist, und so weiter ...

Für k Blöcke, führen wir in Schwierigkeiten , wenn wir erreichen n = 2 k , da das niedrigstwertige Bit Satz von n ist
der ( k + 1) -te Bit, das zu jedem Block nicht stimmt. Mit anderen Worten, dieses Schema funktioniert bis
zu n = 2 k - 1. Während also für k = 5 nur ein mageres n = 31 erhalten wird , wächst diese Zahl exponentiell mit k . Es stellt auch fest, dass S ( k ) ≥ 2 k - 1 ist (aber wir können tatsächlich leicht eine bessere Untergrenze als diese finden.)

Als Referenz ist hier das Ergebnis für 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]

Es gibt eine einfache Möglichkeit, eine weitere herauszufiltern: Verschieben Sie die obere Hälfte der ungeraden Zahlen in eine andere Kategorie (da ihre Summe zwangsläufig größer ist als jede Zahl, die sich bereits in dieser Kategorie befindet), und addieren Sie 2 ^ k zur unteren Hälfte von die ungeraden Zahlen. Die gleiche Idee kann wahrscheinlich erweitert werden, um weitere lg k-Zahlen oder vielleicht sogar ein weiteres k zu erhalten.
Peter Taylor

@PeterTaylor Ja, kurz nach dem Posten wurde mir klar, dass das eigentlich ziemlich trivial ist. Es ist äquivalent zu tun [1], [2,3], [4,5,6,7], ..., was wahrscheinlich einfacher ist, nur mit umgekehrter Bit- und Blockreihenfolge. Es ist leicht zu sehen, wie dieser erweitert werden kann.
Ell
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.