Yüksek boyutlu kafes benzeri bir grafikte en büyük bağımsız seti bulun


16

Belirli bir pozitif tamsayı için n, tüm ikili uzunluk dizelerini dikkate alın 2n-1. Belirli bir dizge için S, Let Luzunlukta bir dizidir nsayısı sayısı içeren 1uzunluğunun her substring s narasında S. Örneğin, eğer n=3ve S = 01010sonra L=[1,2,1]. LSayma dizisini çağırıyoruz S.

Biz iki dizeleri söylemek S1ve S2aynı uzunluktaki bir maç kendi sayma diziler halinde L1ve L2bu özelliği vardır L1[i] <= 2*L2[i]ve L2[i] <= 2*L1[i]herkes için i.

Görev

nBaşlangıç ​​noktasını artırmak için n=1, görev, her birinin uzunluğu 2n-1olan iki dizenin eşleşmemesi için en büyük dize kümesinin boyutunu bulmaktır .

Kodunuzun değeri başına bir sayı çıkmalıdır n.

Puan

Puanınız, nhiç kimsenin yanıtlarınız için daha doğru bir yanıt göndermediği en yüksek puandır . Açıkçası tüm optimum cevaplarınız varsa, o zaman en yüksek gönderdiğiniz puan için alacaksınız n. Ancak, cevabınız optimum olmasa bile, başka hiç kimse onu yenemezse skoru yine de alabilirsiniz.

Örnek cevaplar

İçin n=1,2,3,4alıyorum 2,4,10,16.

Diller ve kütüphaneler

İstediğiniz dil ve kitaplıkları kullanabilirsiniz. Mümkünse, kodunuzu çalıştırabilmeniz iyi olur, bu nedenle mümkünse kodunuzu linux'da nasıl çalıştıracağınız / derleyeceğinize ilişkin tam bir açıklama ekleyin.

Öncü girişler

  • 5 Mathematica'da Martin Büttner tarafından
  • 6 Reto Koradi tarafından C ++ . Değerler 2, 4, 10, 16, 31, 47, 75, 111, 164, 232, 328, 445, 606, 814, 1086. İlk 5'in optimal olduğu bilinmektedir.
  • 7 Peter Taylor tarafından Java . Değerler 2, 4, 10, 16, 31, 47, 76, 111, 166, 235.
  • 9 Java tarafından joriki tarafından . Değerler 2, 4, 10, 16, 31, 47, 76, 112, 168.

3
Bence not edildiğinde eşitsizliği anlamak daha doğal L1[i]/2 <= L2[i] <= 2*L1[i].
orlp

1
Ayrıca, eşleştirme bir eşdeğerlik ilişkisi değildir . match(A, B)ve match(B, C)ima etmez match(A, C)(tersi için). Örnek: [1] ve [2] eşleşir, [2] ve [3] eşleşir, ancak [1] ve [3] eşleşmez. Benzer şekilde, [1,3] ve [3,1] eşleşmez, [3, 1] ve [2, 3] eşleşmez, ancak [1, 3] ve [2,3] eşleşir.
orlp

Yanıtlar:


7

2, 4, 10, 16, 31, 47, 76, 112, 168

Her n için, bu Java kodu olası sayma dizilerini belirler ve daha sonra, rastgele bir kümeyle başlayan ve rastgele en dik inişle iyileştirilen her boyut için, artan boyutta eşleşmeyen kümeler bulur. Her adımda, setin elemanlarından biri rasgele bir şekilde seçilir ve kullanılmayanlar arasında rastgele bir şekilde seçilen başka bir sayma dizisi ile değiştirilir. Adım, eşleşme sayısını artırmazsa kabul edilir. Bu ikinci reçetenin önemli olduğu görülmektedir; adımları yalnızca maç sayısını azaltmaları durumunda kabul etmek neredeyse etkili değildir. Eşleşmelerin sayısını değişmez bırakan adımlar, arama alanının keşfedilmesine izin verir ve sonunda eşleşmelerden birini önlemek için bir miktar alan açılabilir. İyileştirmeden 2 ^ 24 adımdan sonra, n'nin mevcut değeri için bir önceki boyut çıkar ve n arttırılır.

N = 9'a kadar olan sonuçlar 2, 4, 10, 16, 31, 47, 76, 112, 168, önceki sonuçlarda n = 8 için bir ve n = 9 için ikiye çıkmaktadır . Daha yüksek n değerleri için, 2 ^ 24 adım sınırının artırılması gerekebilir.

Ben de benzetilmiş tavlamayı denedim (enerji olarak eşleşme sayısı ile), ama en dik inişte iyileşme olmadan.

kod

olarak kaydetmek Question54354.java
ile derleme javac Question54354.java
ile çalıştırmakjava Question54354

import java.util.Arrays;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;

public class Question54354 {
    static class Array {
        int [] arr;

        public Array (int [] arr) {
            this.arr = arr;
        }

        public int hashCode () {
            return Arrays.hashCode (arr);
        }

        public boolean equals (Object o) {
            return Arrays.equals (((Array) o).arr,arr);
        }
    }

    static int [] indices;
    static int [] [] counts;
    static boolean [] used;

    static Random random = new Random (0);

    static boolean match (int [] c1,int [] c2) {
        for (int k = 0;k < c1.length;k++)
            if (c1 [k] > 2 * c2 [k] || c2 [k] > 2 * c1 [k])
                return false;
        return true;
    }

    static int matches (int i) {
        int result = 0;
        for (int j = 0;j < indices.length;j++)
            if (j != i && match (counts [indices [i]],counts [indices [j]]))
                result++;
        return result;
    }

    static void randomize (int i) {
        do
            indices [i] = random.nextInt (counts.length);
        while (used [indices [i]]);
    }

    public static void main (String [] args) {
        for (int n = 1,length = 1;;n++,length += 2) {
            int [] lookup = new int [1 << n];
            for (int string = 0;string < 1 << n;string++)
                for (int bit = 1;bit < 1 << n;bit <<= 1)
                    if ((string & bit) != 0)
                        lookup [string]++;
            Set<Array> arrays = new HashSet<Array> ();
            for (int string = 0;string < 1 << length;string++) {
                int [] count = new int [n];
                for (int i = 0;i < n;i++)
                    count [i] = lookup [(string >> i) & ((1 << n) - 1)];
                arrays.add (new Array (count));
            }
            counts = new int [arrays.size ()] [];
            int j = 0;
            for (Array array : arrays)
                counts [j++] = array.arr;
            used = new boolean [counts.length];

            int m;
            outer:
            for (m = 1;m <= counts.length;m++) {
                indices = new int [m];
                for (;;) {
                    Arrays.fill (used,false);
                    for (int i = 0;i < m;i++) {
                        randomize (i);
                        used [indices [i]] = true;
                    }
                    int matches = 0;
                    for (int i = 0;i < m;i++)
                        matches += matches (i);
                    matches /= 2;
                    int stagnation = 0;
                    while (matches != 0) {
                        int k = random.nextInt (m);
                        int oldMatches = matches (k);
                        int oldIndex = indices [k];
                        randomize (k);
                        int newMatches = matches (k);
                        if (newMatches <= oldMatches) {
                            if (newMatches < oldMatches) {
                                matches += newMatches - oldMatches;
                                stagnation = 0;
                            }
                            used [oldIndex] = false;
                            used [indices [k]] = true;
                        }
                        else
                            indices [k] = oldIndex;

                        if (++stagnation == 0x1000000)
                            break outer;
                    }
                    break;
                }
            }
            System.out.println (n + " : " + (m - 1));
        }
    }
}

1
Çok güzel bir gelişme!

11

2, 4, 10, 16, 31, 47, 76, 111, 166, 235

notlar

Biz bir grafik düşünün Gköşeleri ile 0karşı n, daha sonra maç iki sayıyı katılmadan ve kenarları tensör güç G^n kesişim noktası var (x_0, ..., x_{n-1})Kartezyen gücünü oluşturan {0, ..., n}^nve eşleşen dizilerini arasındaki kenarları. İlgilenilen grafik , olası "sayım dizilerine" karşılık gelen köşelerin G^n neden olduğu alt grafiktir .

İlk alt görev bu köşeleri oluşturmaktır. Saf yaklaşım, 2^{2n-1}dizeler üzerinde veya sırasına göre numaralandırılır 4^n. Ancak bunun yerine sayma dizilerinin ilk farklar dizisine bakarsak, sadece 3^nolasılıklar olduğunu buluruz ve ilk farklardan, sıfırıncı farklardaki hiçbir öğenin daha az 0veya daha büyük n.

Daha sonra maksimum bağımsız seti bulmak istiyoruz. Bir teorem ve iki buluşsal yöntem kullanıyorum:

  • Teorem: ayrık bir grafik birleşiminin maksimum bağımsız kümesi, maksimum bağımsız kümelerinin birleşmesidir. Dolayısıyla, bir grafiği bağlı olmayan bileşenlere ayırırsak, sorunu basitleştirebiliriz.
  • Sezgisel: (n, n, ..., n)bunun maksimum bağımsız bir kümede olacağını varsayın . Köşe oldukça büyük bir klik var maçları en küçük tam sayıdır ; söz konusu klik dışında hiçbir eşleşmeyeceği garanti edilmektedir.{m, m+1, ..., n}^nmn(n, n, ..., n)
  • Sezgisel: En düşük derecenin tepe noktasını seçmenin açgözlü yaklaşımını kullanın.

Bu bulgular benim bilgisayarda 111için n=816 saniye içinde 166için n=98 hakkında dakika ve 235için n=10de yaklaşık 2 saat.

kod

Farklı kaydet PPCG54354.java, farklı derle javac PPCG54354.javave farklı çalıştır java PPCG54354.

import java.util.*;

public class PPCG54354 {
    public static void main(String[] args) {
        for (int n = 1; n < 20; n++) {
            long start = System.nanoTime();

            Set<Vertex> constructive = new HashSet<Vertex>();
            for (int i = 0; i < (int)Math.pow(3, n-1); i++) {
                int min = 0, max = 1, diffs[] = new int[n-1];
                for (int j = i, k = 0; k < n-1; j /= 3, k++) {
                    int delta = (j % 3) - 1;
                    if (delta == -1) min++;
                    if (delta != 1) max++;
                    diffs[k] = delta;
                }

                for (; min <= max; min++) constructive.add(new Vertex(min, diffs));
            }

            // Heuristic: favour (n, n, ..., n)
            Vertex max = new Vertex(n, new int[n-1]);
            Iterator<Vertex> it = constructive.iterator();
            while (it.hasNext()) {
                Vertex v = it.next();
                if (v.matches(max) && !v.equals(max)) it.remove();
            }

            Set<Vertex> ind = independentSet(constructive, n);
            System.out.println(ind.size() + " after " + ((System.nanoTime() - start) / 1000000000L) + " secs");
        }
    }

    private static Set<Vertex> independentSet(Set<Vertex> vertices, int dim) {
        if (vertices.size() < 2) return vertices;

        for (int idx = 0; idx < dim; idx++) {
            Set<Set<Vertex>> p = connectedComponents(vertices, idx);
            if (p.size() > 1) {
                Set<Vertex> ind = new HashSet<Vertex>();
                for (Set<Vertex> part : connectedComponents(vertices, idx)) {
                    ind.addAll(independentSet(part, dim));
                }
                return ind;
            }
        }

        // Greedy
        int minMatches = Integer.MAX_VALUE;
        Vertex minV = null;
        for (Vertex v0 : vertices) {
            int numMatches = 0;
            for (Vertex vi : vertices) if (v0.matches(vi)) numMatches++;
            if (numMatches < minMatches) {
                minMatches = numMatches;
                minV = v0;
            }
        }

        Set<Vertex> nonmatch = new HashSet<Vertex>();
        for (Vertex vi : vertices) if (!minV.matches(vi)) nonmatch.add(vi);
        Set<Vertex> ind = independentSet(nonmatch, dim);
        ind.add(minV);
        return ind;
    }

    // Separates out a set of vertices which form connected components when projected into the idx axis.
    private static Set<Set<Vertex>> connectedComponents(Set<Vertex> vertices, final int idx) {
        List<Vertex> sorted = new ArrayList<Vertex>(vertices);
        Collections.sort(sorted, new Comparator<Vertex>() {
                public int compare(Vertex a, Vertex b) {
                    return a.x[idx] - b.x[idx];
                }
            });

        Set<Set<Vertex>> connectedComponents = new HashSet<Set<Vertex>>();
        Set<Vertex> current = new HashSet<Vertex>();
        int currentVal = 0;
        for (Vertex v : sorted) {
            if (!match(currentVal, v.x[idx]) && !current.isEmpty()) {
                connectedComponents.add(current);
                current = new HashSet<Vertex>();
            }

            current.add(v);
            currentVal = v.x[idx];
        }

        if (!current.isEmpty()) connectedComponents.add(current);
        return connectedComponents;
    }

    private static boolean match(int a, int b) {
        return a <= 2 * b && b <= 2 * a;
    }

    private static class Vertex {
        final int[] x;
        private final int h;

        Vertex(int[] x) {
            this.x = x.clone();

            int _h = 0;
            for (int xi : x) _h = _h * 37 + xi;
            h = _h;
        }

        Vertex(int x0, int[] diffs) {
            x = new int[diffs.length + 1];
            x[0] = x0;
            for (int i = 0; i < diffs.length; i++) x[i+1] = x[i] + diffs[i];

            int _h = 0;
            for (int xi : x) _h = _h * 37 + xi;
            h = _h;
        }

        public boolean matches(Vertex v) {
            if (v == this) return true;
            if (x.length != v.x.length) throw new IllegalArgumentException("v");
            for (int i = 0; i < x.length; i++) {
                if (!match(x[i], v.x[i])) return false;
            }
            return true;
        }

        @Override
        public int hashCode() {
            return h;
        }

        @Override
        public boolean equals(Object obj) {
            return (obj instanceof Vertex) && equals((Vertex)obj);
        }

        public boolean equals(Vertex v) {
            if (v == this) return true;
            if (x.length != v.x.length) return false;
            for (int i = 0; i < x.length; i++) {
                if (x[i] != v.x[i]) return false;
            }
            return true;
        }

        @Override
        public String toString() {
            if (x.length == 0) return "e";

            StringBuilder sb = new StringBuilder(x.length);
            for (int xi : x) sb.append(xi < 10 ? (char)('0' + xi) : (char)('A' + xi - 10));
            return sb.toString();
        }
    }
}

10

Mathematica,, n = 531 dize

Lembik'in örnek cevaplarını doğrulamak için Mathematica'nın yerleşiklerini kullanarak bir kaba kuvvet çözümü yazdım, ancak aynı zamanda işleyebilir n = 5:

n = 5;
s = Tuples[{0, 1}, 2 n - 1];
l = Total /@ Partition[#, n, 1] & /@ s
g = Graph[l, 
  Cases[Join @@ Outer[UndirectedEdge, l, l, 1], 
   a_ <-> b_ /; 
    a != b && And @@ Thread[a <= 2 b] && And @@ Thread[b <= 2 a]]]
set = First@FindIndependentVertexSet[g]
Length@set

Bir bonus olarak, bu kod, sorunun her bir kenarın eşleşen iki dizeyi gösterdiği bir grafik olarak görselleştirmesini sağlar.

İşte için grafik n = 3:

resim açıklamasını buraya girin


2
İlk başta grafiğin güzel simetrik olduğunu düşündüm, ama sonra soldaki hafif ofset noktasını gördüm. Görülemez :(
orlp

3

C ++ (sezgisel): 2, 4, 10, 16, 31, 47, 75, 111, 164, 232, 328, 445, 606, 814, 1086

Bu 1 ila 3 alt olmanın Peter Taylor'ın sonucu biraz geriden gelen n=7, 9ve 10. Avantajı çok daha hızlı olmasıdır, bu yüzden daha yüksek değerler için çalıştırabilirim n. Ve herhangi bir süslü matematik olmadan anlaşılabilir. ;)

Geçerli kod, çalışacak şekilde boyutlandırılmıştır n=15. Çalışma süreleri, her bir artış için kabaca 4 kat artar n. Örneğin, 0.008 saniye n=7, 0.07 saniye n=9, 1.34 saniye n=11, 27 saniye n=13ve 9 dakika idi n=15.

Kullandığım iki önemli gözlem var:

  • Sezgisel değerler üzerinde çalışmak yerine dizileri saymaya çalışır. Bunu yapmak için, önce tüm benzersiz sayma dizilerinin bir listesi oluşturulur.
  • Küçük değerlere sahip sayma dizilerinin kullanılması, daha az çözelti alanı ortadan kaldırdığından daha faydalıdır. Bu, diğer değerlerden ila caralığına hariç her sayıya dayanır . Daha küçük değerleri için bu aralık daha küçüktür, yani daha az değer kullanılarak kullanılır.c / 22 * cc

Benzersiz Sayım Dizileri Oluşturma

Tüm değerleri yineleyerek, her biri için sayım dizisini oluşturarak ve ortaya çıkan listeyi benzersizleştirerek bu kaba kuvvet uyguladım. Bu kesinlikle daha verimli bir şekilde yapılabilir, ancak birlikte çalıştığımız değer türleri için yeterince iyidir.

Bu, küçük değerler için son derece hızlıdır. Daha büyük değerler için, ek yük önemli hale gelir. Örneğin n=15, tüm çalışma zamanının yaklaşık% 75'ini kullanır. Bu kesinlikle çok daha yüksek gitmeye çalışırken bakmak için bir alan olacaktır n=15. Sadece tüm değerler için sayım dizilerinin bir listesini oluşturmak için kullanılan bellek kullanımı bile sorunlu hale gelmeye başlayacaktır.

Benzersiz sayma dizisi sayısı, değer sayısının yaklaşık% 6'sıdır n=15. Bu göreceli sayım büyüdükçe küçülür n.

Sayma Dizisi Değerlerinin Seçimi

Algoritmanın ana kısmı, basit bir açgözlü yaklaşım kullanarak oluşturulan listeden dizi değerlerini saymayı seçer.

Küçük sayımlı sayım dizilerinin kullanılmasının yararlı olduğu teorisine dayanarak, sayım dizileri sayılarının toplamına göre sıralanır.

Daha sonra sırayla kontrol edilirler ve daha önce kullanılan tüm değerlerle uyumluysa bir değer seçilir. Bu nedenle, her adayın daha önce seçilen değerlerle karşılaştırıldığı benzersiz sayma dizilerinden tek bir doğrusal geçiş içerir.

Buluşsal yöntemlerin potansiyel olarak nasıl geliştirilebileceği hakkında bazı fikirlerim var. Ancak bu makul bir başlangıç ​​noktası gibi görünüyordu ve sonuçlar oldukça iyi görünüyordu.

kod

Bu yüksek düzeyde optimize edilmemiştir. Bir noktada daha ayrıntılı bir veri yapım vardı, ancak onu ötesinde genelleştirmek için daha fazla çalışmaya ihtiyaç duyacaktı n=8ve performans farkı çok önemli görünmüyordu.

#include <cstdint>
#include <cstdlib>
#include <vector>
#include <algorithm>
#include <sstream>
#include <iostream>

typedef uint32_t Value;

class Counter {
public:
    static void setN(int n);

    Counter();
    Counter(Value val);

    bool operator==(const Counter& rhs) const;
    bool operator<(const Counter& rhs) const;

    bool collides(const Counter& other) const;

private:
    static const int FIELD_BITS = 4;
    static const uint64_t FIELD_MASK = 0x0f;

    static int m_n;
    static Value m_valMask;

    uint64_t fieldSum() const;

    uint64_t m_fields;
};

void Counter::setN(int n) {
    m_n = n;
    m_valMask = (static_cast<Value>(1) << n) - 1;
}

Counter::Counter()
  : m_fields(0) {
}

Counter::Counter(Value val) {
    m_fields = 0;
    for (int k = 0; k < m_n; ++k) {
        m_fields <<= FIELD_BITS;
        m_fields |= __builtin_popcount(val & m_valMask);
        val >>= 1;
    }
}

bool Counter::operator==(const Counter& rhs) const {
    return m_fields == rhs.m_fields;
}

bool Counter::operator<(const Counter& rhs) const {
    uint64_t lhsSum = fieldSum();
    uint64_t rhsSum = rhs.fieldSum();
    if (lhsSum < rhsSum) {
        return true;
    }
    if (lhsSum > rhsSum) {
        return false;
    }

    return m_fields < rhs.m_fields;
}

bool Counter::collides(const Counter& other) const {
    uint64_t fields1 = m_fields;
    uint64_t fields2 = other.m_fields;

    for (int k = 0; k < m_n; ++k) {
        uint64_t c1 = fields1 & FIELD_MASK;
        uint64_t c2 = fields2 & FIELD_MASK;

        if (c1 > 2 * c2 || c2 > 2 * c1) {
            return false;
        }

        fields1 >>= FIELD_BITS;
        fields2 >>= FIELD_BITS;
    }

    return true;
}

int Counter::m_n = 0;
Value Counter::m_valMask = 0;

uint64_t Counter::fieldSum() const {
    uint64_t fields = m_fields;
    uint64_t sum = 0;
    for (int k = 0; k < m_n; ++k) {
        sum += fields & FIELD_MASK;
        fields >>= FIELD_BITS;
    }

    return sum;
}

typedef std::vector<Counter> Counters;

int main(int argc, char* argv[]) {
    int n = 0;
    std::istringstream strm(argv[1]);
    strm >> n;

    Counter::setN(n);

    int nBit = 2 * n - 1;
    Value maxVal = static_cast<Value>(1) << nBit;

    Counters allCounters;

    for (Value val = 0; val < maxVal; ++val) {
        Counter counter(val);
        allCounters.push_back(counter);
    }

    std::sort(allCounters.begin(), allCounters.end());

    Counters::iterator uniqEnd =
        std::unique(allCounters.begin(), allCounters.end());
    allCounters.resize(std::distance(allCounters.begin(), uniqEnd));

    Counters solCounters;
    int nSol = 0;

    for (Value idx = 0; idx < allCounters.size(); ++idx) {
        const Counter& counter = allCounters[idx];

        bool valid = true;
        for (int iSol = 0; iSol < nSol; ++iSol) {
            if (solCounters[iSol].collides(counter)) {
                valid = false;
                break;
            }
        }

        if (valid) {
            solCounters.push_back(counter);
            ++nSol;
        }
    }

    std::cout << "result: " << nSol << std::endl;

    return 0;
}

Ben maksimum bulmak garantili benzer kodu dayalı özyinelemeli çözümler vardı. Ama n=4zaten biraz zaman aldı . n=5Biraz sabırla bitirmiş olabilir . Bunu yapmak için daha iyi bir geri izleme stratejisi kullanmış olmalısınız n=7. Seninki sezgisel miydi, yoksa tüm çözüm alanını mı keşfetti? Bunu nasıl daha iyi hale getireceğimi, ya sıralama düzenini ince ayarlayarak ya da belki sadece açgözlü olmamayı düşündüğümü düşünüyorum.
Reto Koradi

Anladığım kadarıyla, Peter Taylor'ın cevabında geri izleme yok. Tamamen açgözlü. Ana hileler, sayma dizilerinin sayısını 3^nve tarif ettiği iki sezgiselliği azaltmasıdır .

@Lembik Yorumum, silinen bir yoruma yanıt olarak verildi. Sayma dizilerinin sayısı aynı olmalıdır, çünkü bunu gerçek değerlere dayanarak oluşturuyorum ve yalnızca benzersiz değerlere indiriyorum. Algoritmanın geri izleme sürümünü güncelledim. Makul bir süre içinde sona ermese de, 76'yı n=7hızlı bir şekilde bulur . Ama denemek için n=9, 20 dakika sonra durdurduğumda hala 164'te sıkışmıştı. Bu yüzden bunu sınırlı bir basit geri izleme formuyla genişletmek genellikle umut verici görünmüyor.
Reto Koradi
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.