การหาพาร์ติชันที่ไม่ต้องเสียเงิน


17

บทสรุปผู้บริหาร

ป้อนข้อมูลkให้ค้นหาพาร์ทิชันของจำนวนเต็ม1เพื่อnเป็นkส่วนย่อยฟรีสำหรับที่ใหญ่ที่สุดที่nคุณสามารถภายใน 10 นาที

พื้นหลัง: หมายเลข Schur

ชุดAคือผลรวมฟรีถ้าผลรวมของตนเองA + A = { x + y | x, y in A}มีองค์ประกอบที่ไม่มีในการร่วมกันกับมัน

สำหรับเลขจำนวนเต็มบวกทุกตัวkจะมีจำนวนเต็มที่มากที่สุดS(k)ซึ่งชุด{1, 2, ..., S(k)}นั้นสามารถแบ่งพาร์ติชันเป็นkเซ็ตย่อยที่ไม่มีผลรวมได้ หมายเลขนี้เรียกว่าหมายเลข k th Schur (OEIS A045652 )

ตัวอย่างเช่นS(2) = 4. เราสามารถแบ่งพาร์ติชัน{1, 2, 3, 4}เป็น{1, 4}, {2, 3}และนั่นคือพาร์ติชันที่ไม่ซ้ำกันในชุดย่อยที่ไม่มีผลรวมสองชุด แต่ตอนนี้เราไม่สามารถเพิ่ม5ส่วนใดส่วนหนึ่งได้

ท้าทาย

เขียนโปรแกรมกำหนดขึ้นซึ่งทำสิ่งต่อไปนี้:

  • ใช้จำนวนเต็มบวกkเป็นอินพุต
  • เขียนการประทับเวลา Unix ปัจจุบันเพื่อ stdout
  • ขาออกลำดับของพาร์ทิชันของ1การnเข้าไปในkส่วนย่อยรวมฟรีสำหรับเพิ่มขึ้นnตามลำดับด้วยเวลา Unix ปัจจุบันแต่ละ

ผู้ชนะจะได้เป็นโปรแกรมที่พิมพ์พาร์ทิชันสำหรับที่ใหญ่ที่สุดnภายใน 10 5นาทีในคอมพิวเตอร์ของฉันเมื่อได้รับการป้อนข้อมูล ความสัมพันธ์จะถูกแบ่งตามเวลาที่เร็วที่สุดเพื่อค้นหาพาร์ติชันสำหรับการnรันที่ใหญ่ที่สุดโดยเฉลี่ยมากกว่าสามการรันนั่นคือสาเหตุที่เอาต์พุตควรมีการประทับเวลา

รายละเอียดที่สำคัญ:

  • ฉันมี Ubuntu ที่แม่นยำดังนั้นหากภาษาของคุณไม่รองรับฉันจะไม่สามารถให้คะแนนได้
  • ฉันมีซีพียู Intel Core2 Quad ดังนั้นหากคุณต้องการใช้มัลติเธรดไม่ต้องใช้หัวข้อมากกว่า 4 เธรด
  • หากคุณต้องการให้ฉันใช้ธงคอมไพเลอร์ใด ๆ หรือการใช้งานเอกสารที่ชัดเจนในคำตอบของคุณ
  • 5คุณจะไม่กรณีพิเศษรหัสของคุณจะจัดการกับการป้อนข้อมูล
  • คุณไม่จำเป็นต้องแสดงผลการปรับปรุงทุกอย่างที่คุณพบ เช่นสำหรับการป้อนข้อมูลที่คุณสามารถส่งออกเพียงพาร์ทิชันสำหรับ2 n = 4แต่ถ้าคุณจะทำอะไรไม่ได้การส่งออกในช่วง 10 n = 0นาทีแรกแล้วฉันจะทำประตูได้ว่าเป็น

คำตอบ:


8

Python 3 เรียงลำดับตามจำนวนที่มากที่สุด n = 92 121

ขอบคุณ Martin Büttnerสำหรับคำแนะนำที่ปรับปรุงnถึงจำนวนสูงสุดโดยไม่คาดคิด

ผลลัพธ์สุดท้าย:

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

อัลกอริทึมเหมือนกับคำตอบก่อนหน้าของฉันที่ยกมาด้านล่าง:

มีถังขยะ k ที่มีทั้งตัวเลขอยู่ในนั้นและตัวเลขที่ไม่สามารถเข้าไปได้อีก ที่ความลึกแต่ละระดับในการทำซ้ำ (โดยทั่วไปแล้วเป็นการค้นหาความลึกครั้งแรก) การสั่งซื้อถังขยะจะถูกสับเปลี่ยนและหมายเลขถัดไป (nextN) คือ (เรียงตามลำดับ) ใส่ลงในถังขยะที่สามารถรับได้ หากไม่มีจะส่งคืนและสำรองข้อมูลหนึ่งขั้นตอน

... ด้วยข้อยกเว้นประการหนึ่ง: การสั่งซื้อถังขยะจะไม่ถูกสับ แต่จะถูกจัดเรียงในลักษณะที่ถังขยะที่มีจำนวนมากที่สุดมาก่อน ถึงn = 121ใน 8 วินาที!

รหัส:

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

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

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

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

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

            search(k, nextN2, sets2)

search(5)

หมายเหตุ:การเรียงลำดับตามจำนวนที่ยิ่งใหญ่ที่สุดของตัวเลขที่ได้รับอนุญาตให้อยู่ในช่วงของตัวเลขที่ไม่ได้รับอนุญาตให้n=59และการเรียงลำดับตามจำนวนที่ยิ่งใหญ่ที่สุดของตัวเลขที่ได้รับอนุญาตให้น้อยกว่าให้nextN n=64การเรียงลำดับตามความยาวของรายการหมายเลขที่ไม่อนุญาต (ซึ่งอาจมีซ้ำ) จะนำไปสู่n=30รูปแบบที่สวยงาม
El'endia Starman

รูปแบบเวลาที่ส่งออกไม่ถูกต้อง (ควรเป็นวินาทีตั้งแต่ยุค แต่ฉันเห็นTue Nov 10 00:44:25 2015) แต่ฉันเห็นn=92ในเวลาน้อยกว่า 2 วินาที
ปีเตอร์เทย์เลอร์

ฉันคิดว่ารูปแบบเวลาไม่สำคัญเท่ากับการแสดงให้เห็นว่าใช้เวลานานแค่ไหน ฉันจะคิดออกและเปลี่ยนมัน แก้ไข: D'oh ฉันเลือกctimeมากกว่าtimeเพราะผลลัพธ์ดีกว่าเมื่อtimeเป็นสิ่งที่ฉันควรเลือก
El'endia Starman

คุณก็รู้คุณสามารถเรียงลำดับตามจำนวนที่มากที่สุดในถังขยะได้เพราะจำนวนที่ไม่ได้รับอนุญาตมากที่สุดจะเป็นสองเท่าเสมอ
Martin Ender

@ MartinBüttner: ...... ผม ... เอ่อ ... ผมมีความคิดว่าทำไมหรือไม่ n=121แต่ที่ได้รับ oO
El'endia Starman

7

Python 3, 121, <0.001s

ขอบคุณฮิวริสติกที่ปรับปรุงใหม่ให้กับ Martin Buttner หมายความว่าเราไม่จำเป็นต้องมีการสุ่ม

เอาท์พุท:

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

รหัส:

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

n = 5

seed(0)

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

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

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

search()

Python 3, 112

เรียงตามผลรวมของ 2 องค์ประกอบแรก + ความเบ้

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

ฉันคัดลอกโครงสร้างข้อมูลของ El'endia Starman ซึ่งประกอบด้วยรายการของคู่ที่องค์ประกอบแรกของคู่คือองค์ประกอบในที่ฝากข้อมูลนั้นและที่สองคือผลรวมของที่เก็บข้อมูลนั้น

ฉันเริ่มต้นด้วย "ติดตามสิ่งที่เป็นประโยชน์จำนวนมาก" วิธีการเดียวกัน การจัดเรียงฮิวริสติกของฉันเป็นเพียงผลรวมขององค์ประกอบที่เล็กที่สุดในรายการที่กำหนด ฉันยังเพิ่มการสุ่มเอียงเล็กน้อยเพื่อลองความเป็นไปได้ที่แตกต่างกัน

การวนซ้ำแต่ละครั้งจะใส่หมายเลขใหม่ลงในถังขยะแรกที่คล้ายกับโลภแบบสุ่ม เมื่อสิ่งนี้ล้มเหลวมันก็เริ่มใหม่

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

n = 5

seed(0)

def skew():
    return randrange(9)

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

ว้าวนี่คล้ายกับรหัสของฉันมาก : P;) (ฉันไม่รังเกียจเลย)
El'endia Starman

@ El'endiaStarman เพิ่มเครดิตแล้ว มันเป็นพื้นฐานที่ดี
isaacg

7

Java 8, n = 142 144

ผลลัพธ์สุดท้าย:

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

ทำการค้นหาแบบสุ่ม seeded กระจายมากกว่า 4 เธรด เมื่อไม่สามารถหาพาร์ติชันให้พอดีnได้จะพยายามเพิ่มพื้นที่ว่างสำหรับnพาร์ติชันหนึ่งต่อครั้งโดยการทิ้งให้มากที่สุดเท่าที่จะทำได้จากพาร์ติชันอื่น

แก้ไข: tweaked อัลกอริทึมสำหรับการเพิ่มพื้นที่ว่างสำหรับnยังเพิ่มความสามารถในการกลับไปที่ตัวเลือกก่อนหน้าและเลือกอีกครั้ง

หมายเหตุ: เอาต์พุตไม่ได้ถูกกำหนดอย่างเคร่งครัดเนื่องจากมีหลายเธรดที่เกี่ยวข้องและสามารถจบการอัพเดตที่ดีที่สุดที่nพบในลำดับที่สับสน คะแนนในตอนท้ายของ 144 นั้นถูกกำหนดไว้และถึงอย่างรวดเร็ว: 30 วินาทีบนคอมพิวเตอร์ของฉัน

รหัสคือ:

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

public class SumFree {

    private static int best;

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

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

        System.out.println(start);

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

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

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

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

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

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

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

                            Partition other = partitions.get(i);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        Partition() {
        }

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

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

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

5

C, โลภแบบสุ่ม, n = 91

เพียงเพื่อมอบโซลูชันพื้นฐานซึ่งจะวนซ้ำnติดตามการถังขยะและผลรวมของพวกเขาและเพิ่มnลงในถังขยะแบบสุ่มซึ่งยังไม่ปรากฏเป็นผลรวม จะสิ้นสุดลงเมื่อnปรากฏในkผลรวมทั้งหมดและหากผลลัพธ์nนั้นดีกว่าความพยายามก่อนหน้านี้ให้พิมพ์ไปที่ STDOUT

อินพุตkถูกจัดเตรียมผ่านอาร์กิวเมนต์บรรทัดรับคำสั่ง ความเป็นไปได้สูงสุดkในปัจจุบันคือฮาร์ดโค้ดถึง 10 เพราะฉันขี้เกียจเพิ่มการจัดสรรหน่วยความจำแบบไดนามิก แต่ก็สามารถแก้ไขได้อย่างง่ายดาย

ฉันเดาว่าฉันจะไปหาเมล็ดพันธุ์ที่ดีกว่าตอนนี้ แต่คำตอบนี้อาจไม่แข่งขันโดยเฉพาะอย่างยิ่งดังนั้นฉัน

นี่คือพาร์ติชันสำหรับn = 91:

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

และสุดท้ายนี่คือรหัส:

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

#define MAX_K 10
#define MAX_N 1024

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

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

    int k = atoi(argv[1]);

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

    int best = 0;

    srand(1447101176);

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

            if (!available_bins) break;

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

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

            ++n;
        }

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

    return 0;
}

ยืนยันแล้วn=91พบใน 138 วินาที หากจำเป็นสำหรับการผูกไทม์มิ่งฉันจะดำเนินการเพื่อหลีกเลี่ยงข้อผิดพลาดที่มีขนาดใหญ่เนื่องจากโหลดซีพียูที่แตกต่างกัน
ปีเตอร์เทย์เลอร์

3

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

ผนวก n ถัดไปกับชุดย่อยที่เลือกแบบสุ่ม หากไม่สามารถทำได้ระบบจะลบตัวเลขสุ่มออกจากชุดย่อยและต่อท้ายไปยังผู้อื่นด้วยความหวังว่าจะทำให้สามารถผนวกท้ายได้

ฉันทำต้นแบบในเรื่อง awk และเพราะมันดูดีฉันจึงแปลมันเป็น C ++ เพื่อเร่งความเร็ว การใช้ a std::setควรมีความรวดเร็วยิ่งขึ้น

เอาต์พุตสำหรับ n = 135 (หลังจากประมาณ 230 วินาทีบนเครื่อง [เครื่องเก่า] ของฉัน)

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

ฉันไม่ได้ตรวจสอบความถูกต้องอีกครั้ง แต่ก็ไม่เป็นไร


2

Python 3 โลภแบบสุ่ม n = 61

ผลลัพธ์สุดท้าย:

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

สิ่งนี้ใช้อัลกอริทึมแบบเดียวกับMartin Büttnerอย่างมีประสิทธิภาพแต่ฉันพัฒนาขึ้นมาเอง

มีkถังขยะที่มีทั้งตัวเลขอยู่ในนั้นและตัวเลขที่ไม่สามารถเข้าไปได้อีก ที่ความลึกแต่ละระดับในการทำซ้ำ (โดยทั่วไปแล้วเป็นการค้นหาความลึกครั้งแรก) การสั่งซื้อถังขยะจะถูกสับเปลี่ยนและหมายเลขถัดไป ( nextN) คือ (เรียงตามลำดับ) ใส่ลงในถังขยะที่สามารถรับได้ หากไม่มีจะส่งคืนและสำรองข้อมูลหนึ่งขั้นตอน

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

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

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

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

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

            search(k, nextN2, sets2)

search(5)

2

Python, n = 31

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

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

ตกลงดังนั้นมันจึงไม่ใช่ผู้ชนะ แต่ฉันรู้สึกว่ามันเป็นของที่นี่ ฉันใช้เสรีภาพที่จะไม่รวมการประทับเวลาเนื่องจากจะยุติลงในทันทีและเนื่องจากไม่ใช่การแข่งขันที่แท้จริง

ก่อนอื่นให้สังเกตว่าผลรวมของเลขสองคี่ใด ๆ เป็นเลขคู่ดังนั้นเราสามารถถ่ายเลขคี่ทั้งหมดในบล็อกแรก จากนั้นเนื่องจากจำนวนที่เหลือทั้งหมดเป็นเลขคู่เราสามารถหารพวกมันด้วย 2 อีกครั้งเราสามารถโยนเลขคี่ที่ได้ทั้งหมดในบล็อกที่สอง (หลังจากคูณพวกเขาด้วย 2) หารตัวเลขที่เหลือด้วย 2 (เช่น โดยรวม 4), โยนคนแปลก ๆ ในบล็อกที่สาม (หลังจากคูณพวกเขาอีกครั้งด้วย 4), และอื่น ๆ ... หรือเพื่อที่จะกล่าวคำที่คุณเข้าใจเราใส่ตัวเลขทั้งหมดที่มีค่านัยสำคัญน้อยที่สุด บิตเป็นบิตแรกในบล็อกแรกตัวเลขทั้งหมดที่บิตชุดที่มีนัยสำคัญน้อยที่สุดคือบิตที่สองในบล็อกที่สองเป็นต้น ...

สำหรับkบล็อกเราจะประสบปัญหาเมื่อเราเข้าถึงn = 2 kตั้งแต่บิตชุดน้อยอย่างมีนัยสำคัญของnเป็น
ข้อมูล ( k + 1) บิต -th ซึ่งไม่สอดคล้องกับบล็อกใด ๆ ในคำอื่น ๆ โครงการนี้ทำงานขึ้น
เพื่อn = 2 k - 1 ดังนั้นในขณะที่สำหรับk = 5 เราเพียง แต่ได้รับน้อยn = 31จำนวนนี้เติบโตชี้แจงกับk นอกจากนี้ยังกำหนดว่าS ( k ) ≥ 2 k - 1 (แต่เราสามารถหาขีด จำกัด ล่างที่ดีกว่าได้ง่ายกว่า)

สำหรับการอ้างอิงนี่คือผลลัพธ์สำหรับk = 5:

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

มีวิธีง่ายๆในการบีบเพิ่มพิเศษ: เลื่อนครึ่งบนของเลขคี่ไปยังหมวดหมู่อื่น ๆ (เนื่องจากผลรวมของพวกเขาถูกผูกไว้ให้ใหญ่กว่าหมายเลขใด ๆ ที่อยู่ในหมวดนั้น) และเพิ่ม 2 ^ k ไปครึ่งล่างของ ตัวเลขคี่ ความคิดเดียวกันนี้อาจขยายออกไปเพื่อรับหมายเลข lg k อีกอันหรืออาจเป็นจำนวน k อีก
Peter Taylor

@PeterTaylor ใช่ฉันรู้สั้น ๆ หลังจากโพสต์ว่านี่เป็นเรื่องเล็กน้อย มันเทียบเท่ากับการทำ[1], [2,3], [4,5,6,7], ...ซึ่งอาจจะง่ายกว่าเพียงแค่สั่งบิตย้อนกลับและบล็อก มันง่ายที่จะเห็นว่าสิ่งนี้สามารถขยายได้
Ell
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.