Encontrar particiones sin suma

17

Resumen Ejecutivo

Teniendo en cuenta la entrada k, encontrar una partición de números enteros 1a nen ksubconjuntos libre de suma para el más grande nque pueda dentro de los 10 minutos.

Antecedentes: números de Schur

Un conjunto no Atiene suma si su auto suma A + A = { x + y | x, y in A}no tiene elementos en común.

Por cada número entero positivo khay un número entero más grande de S(k)tal manera que el conjunto {1, 2, ..., S(k)}se puede dividir en ksubconjuntos sin suma. Este número se denomina número k th Schur (OEIS A045652 ).

Por ejemplo, S(2) = 4. Podemos particionar {1, 2, 3, 4}como {1, 4}, {2, 3}, y esa es la partición única en dos subconjuntos sin suma, pero ahora no podemos agregar 5a ninguna de las partes.

Desafío

Escriba un programa determinista que haga lo siguiente:

  • Tome un entero positivo kcomo entrada
  • Escribir la marca de tiempo actual de Unix en stdout
  • Emite una secuencia de particiones de 1a nen ksubconjuntos sin suma para aumentar n, siguiendo cada secuencia con la marca de tiempo Unix actual.

El ganador será el programa que imprime una partición para el más grande nen 10 minutos en mi computadora cuando se le da entrada 5. Los empates se romperán por el tiempo más rápido para encontrar una partición para el mayor npromedio de tres ejecuciones: es por eso que la salida debe incluir marcas de tiempo.

Detalles importantes:

  • Tengo Ubuntu Precise, por lo que si su idioma no es compatible, no podré calificarlo.
  • Tengo una CPU Intel Core2 Quad, por lo que si desea usar subprocesos múltiples, no tiene sentido usar más de 4 subprocesos.
  • Si desea que use algún indicador o implementación particular del compilador, documente eso claramente en su respuesta.
  • No deberá poner en mayúsculas y minúsculas su código para manejar la entrada 5.
  • No es necesario que produzca todas las mejoras que encuentre. Por ejemplo, para la entrada 2, solo puede generar la partición n = 4. Sin embargo, si no muestra nada en los primeros 10 minutos, lo calificaré como n = 0.
Peter Taylor
fuente

Respuestas:

8

Python 3, ordenar por mayor número, n = 92 121

Gracias a Martin Büttner por una sugerencia que inesperadamente mejoró el máximo nalcanzado.

Última salida:

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

El algoritmo es el mismo que mi respuesta anterior, citado a continuación:

Hay k contenedores que tienen tanto los números hasta ahora como los números que ya no pueden entrar. En cada profundidad en la iteración (es básicamente una búsqueda de profundidad primero), se ordena el orden de los contenedores y el siguiente número (nextN) se coloca (secuencialmente) en los contenedores que pueden llevarlo y luego va un paso más profundo. Si no hay ninguno, regresa, retrocediendo un paso.

... con una excepción: el orden de los contenedores no se baraja. En cambio, se clasifica de tal manera que los contenedores con el mayor número son lo primero. Esto alcanzó n = 121en 8 segundos!

Código:

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
fuente
Nota: ordenar por el mayor número de números permitidos dentro del rango de números no permitidos da n=59, y ordenar por el mayor número de números permitidos menos que nextNda n=64. Ordenar por la longitud de la lista de números no permitidos (que puede tener repeticiones) conduce muy rápidamente a un n=30patrón elegante .
El'endia Starman
El formato de tiempo de salida no es correcto (debería ser segundos desde la época, pero estoy viendo Tue Nov 10 00:44:25 2015), pero lo vi n=92en menos de 2 segundos.
Peter Taylor el
Ah, pensé que el formato de hora no era tan importante como mostrar exactamente cuánto tiempo tardó. Sin embargo, lo resolveré y lo cambiaré. EDITAR: D'oh. Cogí ctimemás timeporque la salida era más bonita cuando timeera exactamente lo que debería he elegido.
El'endia Starman
Ya sabes, también podrías ordenar por el mayor número en el contenedor, porque el mayor número no permitido siempre será el doble.
Martin Ender
@ MartinBüttner: ...... Yo ... eh ... No tengo idea de cómo o por qué, pero eso se pone n=121. oO
El'endia Starman
7

Python 3, 121, <0.001s

La mejora heurística gracias a Martin Buttner significa que ni siquiera necesitamos aleatoriedad.

Salida:

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

Código:

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

Pitón 3, 112

Ordenar por suma de los 2 primeros elementos + sesgo

[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

Copié la estructura de datos de El'endia Starman, que consiste en una lista de pares, donde el primer elemento del par son los elementos en ese cubo, y el segundo son las sumas de ese cubo.

Comienzo con el mismo enfoque de "realizar un seguimiento de las sumas disponibles". Mi heurística de clasificación es simplemente la suma de los dos elementos más pequeños en una lista dada. También agrego un pequeño sesgo aleatorio para probar diferentes posibilidades.

Cada iteración simplemente coloca a cada uno el nuevo número en el primer contenedor disponible, similar a la codicia aleatoria. Una vez que esto falla, simplemente se reinicia.

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
fuente
Wow, esto se ve extremadamente similar a mi código. : P;) (No me importa en absoluto.)
El'endia Starman
@ El'endiaStarman Credit agregado. Es una buena base.
isaacg
7

Java 8, n = 142 144

Última salida:

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

Realiza una búsqueda aleatoria sembrada distribuida en 4 hilos. Cuando no puede encontrar una partición para encajar n, intenta liberar espacio para nuna partición a la vez volcando todo lo que pueda en las otras particiones.

editar: modificó el algoritmo para liberar espacio n, también agregó la capacidad de volver a una opción anterior y elegir nuevamente.

nota: el resultado no es estrictamente determinista porque hay varios subprocesos involucrados y pueden terminar actualizando el mejor nencontrado hasta ahora en orden desordenado; Sin embargo, la puntuación final de 144 es determinista y se alcanza con bastante rapidez: 30 segundos en mi computadora.

El código es:

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
fuente
5

C, codicioso al azar, n = 91

Solo para proporcionar una solución de referencia, esto se repite n, realiza un seguimiento de los contenedores y sus sumas y se agrega na un contenedor aleatorio donde todavía no aparece como una suma. Termina una vez que naparece en todas las ksumas, y si el resultado nfue mejor que cualquier intento anterior, lo imprime en STDOUT.

La entrada kse proporciona a través de un argumento de línea de comandos. El máximo posible kestá actualmente codificado en 10 porque era demasiado perezoso agregar asignación de memoria dinámica, pero eso podría solucionarse fácilmente.

Supongo que podría ir a buscar una semilla mejor ahora, pero esta respuesta probablemente no sea particularmente competitiva de todos modos, así que meh.

Aquí está la partición para 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

Y finalmente, aquí está el código:

#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
fuente
Confirmado n=91, encontrado en 138 segundos. Si es necesario para el desempate, volveré a tomar el tiempo para evitar grandes errores debido a una carga de CPU diferente.
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;
}

Agrega la siguiente n a un subconjunto elegido al azar. Si eso no es posible, elimina números aleatorios de subconjuntos y los agrega a otros con la esperanza de que eso permita agregar n en alguna parte.

Creé un prototipo de esto en awk y, como parecía prometedor, lo traduje a C ++ para acelerarlo. Usar un std::setincluso debería acelerarlo más.

Salida para n = 135 (después de unos 230 segundos en mi máquina [antigua])

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 

No volví a verificar la validez, pero debería estar bien.

Cabbie407
fuente
2

Python 3, codicioso al azar, n = 61

Última salida:

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

Esto usa efectivamente el mismo algoritmo que Martin Büttner , pero lo desarrollé independientemente.

Hay kcontenedores que tienen tanto los números hasta ahora como los números que ya no pueden entrar. En cada profundidad en la iteración (es básicamente una búsqueda de profundidad primero), el orden de los contenedores se baraja y el siguiente número ( nextN) se coloca (secuencialmente) en los contenedores que pueden llevarlo y luego va un paso más profundo. Si no hay ninguno, regresa, retrocediendo un paso.

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
fuente
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, obviamente no es un ganador, pero sentí que pertenecía aquí de todos modos. Me he tomado la libertad de no incluir marcas de tiempo, ya que termina instantáneamente y no es un verdadero contendiente.

Primero, tenga en cuenta que la suma de dos números impares es par, por lo que podemos volcar todos los números impares en el primer bloque. Luego, dado que todos los números restantes son pares, podemos dividirlos por 2. Una vez más, podemos arrojar todos los números impares resultantes en el segundo bloque (después de volver a multiplicarlos por 2), dividir los números restantes por 2 (es decir, , por 4 en general), arroje los impares en el tercer bloque (después de volver a multiplicarlos por 4), y así sucesivamente ... O, para poner en palabras que ustedes entienden, ponemos todos los números cuyo conjunto menos significativo bit es el primer bit en el primer bloque, todos los números cuyo bit de conjunto menos significativo es el segundo bit en el segundo bloque, y así sucesivamente ...

Para k bloques, nos encontramos con problemas una vez que alcanzamos n = 2 k , ya que el bit de conjunto menos significativo de n es
el bit ( k + 1), que no corresponde a ningún bloque. En otras palabras, este esquema funciona hasta
que n = 2 k - 1. Por lo tanto, mientras que para k = 5 sólo tenemos un magro n = 31 , este número crece exponencialmente con k . También establece que S ( k ) ≥ 2 k - 1 (pero en realidad podemos encontrar un límite inferior mejor que eso con bastante facilidad).

Como referencia, aquí está el resultado para 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]
Ana
fuente
Hay una manera fácil de obtener uno extra: mueva la mitad superior de los números impares a cualquier otra categoría (ya que su suma será mayor que cualquier número que ya esté en esa categoría) y agregue 2 ^ k a la mitad inferior de Los números impares. La misma idea probablemente puede extenderse para obtener otros números lg k, o tal vez incluso otro k.
Peter Taylor
@ PeterTaylor Sí, me di cuenta poco después de publicar que esto es bastante trivial. Es equivalente a hacer [1], [2,3], [4,5,6,7], ..., lo que probablemente sea más simple, solo con el orden inverso de bits y bloques. Es fácil ver cómo se puede extender este.
Ell