X özelliği olmadan en yüksek puanlama matrisini bulun


14

Bu zorluk kısmen bir algoritma zorluğu, kısmen bir optimizasyon zorluğu ve kısmen de en hızlı kod zorluğudur.

Döngüsel bir matris, ilk satırı ile tamamen belirtilir r. Kalan satırların her biri r, ofseti satır dizinine eşit olan satırın döngüsel permütasyonlarıdır . Kare olmayan döngüsel matrislere izin veririz, böylece son satırlarından bazılarını kaçırırlar. Bununla birlikte, her zaman satır sayısının sütun sayısından fazla olmadığını varsayıyoruz. Örneğin, aşağıdaki 3 x 5 döngüsel matrisi düşünün.

10111
11011
11101

Aynı (vektör) toplamı aynı olmayan indeksleri olan boş olmayan iki sütun kümesi içeriyorsa, bir matrisin X özelliğine sahip olduğunu söylüyoruz. İki sütunun vektör toplamı, iki sütunun element bazında bir toplamıdır. Bu, xher biri element içeren iki sütunun toplamıdır x.

İlk ve son sütunlar aynı olduğundan yukarıdaki matris önemsiz bir şekilde X özelliğine sahiptir. Kimlik matrisinin asla X özelliği yoktur.

Yukarıdaki matrisin son sütununu kaldırırsak, X özelliğine sahip olmayan ve 4/3 puan verecek bir örnek alırız.

1011
1101
1110

Görev

Görev, girdileri 0 veya 1 olan ve X özelliği bulunmayan en yüksek puanlı döngüsel matrisi bulmak için kod yazmaktır .

Puan

Puanınız, sayı sütunlarının en iyi puanlama matrisinizdeki satır sayısına bölünmesiyle elde edilir.

Kravat Kırıcı

İki cevap aynı puana sahipse, gönderilen cevap ilk kazanır.

Birinin sınırsız puan almak için bir yöntem bulması (çok) olası bir durumda, böyle bir çözümün ilk geçerli kanıtı kabul edilecektir. Sonlu bir matrisin en iyiliğinin bir kanıtı bulabileceğiniz daha da olası olmayan bir olayda, elbette kazanmayı da ödüllendireceğim.

İpucu

12/8 puan almak çok zor değil.

Diller ve kütüphaneler

Serbestçe kullanılabilen bir derleyici / tercüman / vb. Olan herhangi bir dili kullanabilirsiniz. Linux ve Linux için serbestçe bulunan tüm kütüphaneler için.

Öncü girişler

  • 36/19 Peter Taylor (Java)
  • Suboptimus Prime tarafından 32/17 (C #)
  • 21/12 justhalf tarafından (Python 2)

Ah, X özelliği satırlarda değil sütunlardadır.
Doktor

Yazıldığı gibi, 1 x 2 matrisi 01 X özelliğine sahiptir, çünkü ilk sütun kümesi boş kümeyle aynı vektör toplamına sahiptir. Belki de boş sütunlar demek istediniz? Bence değiştirmemek daha temiz.
xnor

2
Kuralların en kolay okunması 01X özelliğine sahip olmasıdır (1) = (0) + (1). Bunu hariç tutmak istiyorsanız, iki sütun kümesinin ayrık olması gerektiğini söylemelisiniz.
Peter Taylor

1
Bu soru bu sorun hakkında çok fikir verecektir (maalesef NP-hard olan X özelliğini kontrol etmenin
adalet

3
Şu anda sadece 2^mX özelliğini kontrol etmek için tüm sütun kombinasyonlarını kaba-zorlamak vardır . Eğer bir şekilde "ortada buluşmak" düzeni ("alt küme toplamı" sorununa bakın) tasarlayabiliriz eğer bu muhtemelen bunu azaltabilir m * 2^(m/2)...
kennytm

Yanıtlar:


11

16/9 20/11 22/12 28/15 30/16 32/17 34/18 36/19 (Java)

Bu, arama alanını ve maliyeti azaltmak için bir dizi fikir kullanır. Kodun önceki sürümleriyle ilgili daha fazla ayrıntı için düzeltme geçmişini görüntüleyin.

  • Wlog'un sadece ilk satırın bir Lyndon kelimesi olduğu sirkülasyon matrislerini düşünebileceğimiz açıktır : eğer kelime asal değilse, X özelliğine sahip olmalı ve aksi takdirde puanı veya X özelliğini etkilemeden dönebiliriz.
  • Gözlenen kısa kazananların sezgisel taramalarına dayanarak, şimdi% 50 yoğunluğa (yani aynı sayıda 0ve 1) sahip olanlardan başlayıp çalışarak Lyndon sözcüklerini yineliyorum ; Sabit yoğunluklu kolyeler ve Lyndon kelimeleri için A Gray kodunda açıklanan algoritmayı sabit amortismanlı zamanda kullanıyorum , Sawada ve Williams, Theoretical Computer Science 502 (2013): 46-54.
  • Ampirik bir gözlem, değerlerin çiftler halinde meydana gelmesidir: bulduğum her optimum Lyndon kelimesi, tersine çevirmeyle aynı puanlara sahiptir. Bu yüzden her bir çiftin sadece yarısını göz önünde bulundurarak yaklaşık iki kat hız elde ediyorum.
  • Orijinal kodum BigIntegertam bir test vermek için çalıştı . Modulo'yu büyük bir başbakan çalıştırarak ve her şeyi ilkel olarak tutarak, yanlış negatifler riski altında önemli bir hız artışı elde ediyorum. Seçtiğim asal, 2 57'den küçük olan en büyüğüdür , çünkü taşma olmadan kavramsal vektör temsilimin tabanı ile çarpılmasına izin verir.
  • Ben çaldım Suboptimus Başbakan o boyutta artan sırada alt kümeleri dikkate alarak hızlı ret elde etmenin mümkün olduğunu 'ın sezgisel bir yaklaşım. Şimdi bu fikri, çarpışan alt kümeleri test etmek için üçlü alt küme-ortadaki buluşma yaklaşımıyla birleştirdim. ( Yaklaşımı tamsayı altkümesi sorunundan uyarlamaya çalışmayı önerdiği için KennyTM'ye teşekkür ederiz; sanırım xnor ve ben bunu aynı anda yapmanın yolunu gördük). Her sütunu 0 veya 1 kez içerebilen ve aynı toplamı içeren iki alt kümeyi aramak yerine, her sütunu -1, 0 veya 1 kez içerebilen ve sıfıra toplayan bir alt kümeyi ararız. Bu, bellek gereksinimlerini önemli ölçüde azaltır.
  • Her bir öğenin {-1,0,1}^molumsuzlaması da olduğu {-1,0,1}^miçin bellek gereksinimlerinden tasarruf etmenin ekstra bir faktörü var, sadece ikisinden birini saklamak gerekiyor.
  • Ayrıca özel bir hashmap uygulaması kullanarak bellek gereksinimlerini ve performansını geliştiriyorum. 36/19 test etmek için 3 ^ 18 toplam depolamak gerekir ve 3 ^ 18 uzunluğunda herhangi bir ek yük olmadan neredeyse 3GB - 4GB yeterli olmadığı için 6GB yığın verdim; 8GB RAM içinde daha ileri gitmek (yani 38/20 testi) uzun süre yerine ints depolamak için daha fazla optimizasyon gerektirir. Hangi alt kümenin 12 bit artı kovadan örtülü bitler bırakacak toplamı ürettiğini söylemek için 20 bit gereklidir; Herhangi bir isabet elde etmek için çok fazla yanlış çarpışma olacağından korkuyorum.
  • Kanıtların ağırlığı, bakmamız gerektiğini gösterdiğinden 2n/(n+1), sadece bunu test ederek işleri hızlandırıyorum.
  • Gereksiz ama güven verici bir istatistiksel çıktı var.
import java.util.*;

// Aiming to find a solution for (2n, n+1).
public class PPCG41021_QRTernary_FixedDensity {
    private static final int N = 36;
    private static int density;
    private static long start;
    private static long nextProgressReport;

    public static void main(String[] args) {
        start = System.nanoTime();
        nextProgressReport = start + 5 * 60 * 1000000000L;

        // 0, -1, 1, -2, 2, ...
        for (int i = 0; i < N - 1; i++) {
            int off = i >> 1;
            if ((i & 1) == 1) off = ~off;
            density = (N >> 1) + off;

            // Iterate over Lyndon words of length N and given density.
            for (int j = 0; j < N; j++) a[j] = j < N - density ? '0' : '1';
            c = 1;
            Bs[1] = N - density;
            Bt[1] = density;
            gen(N - density, density, 1);
            System.out.println("----");
        }

        System.out.println("Finished in " + (System.nanoTime() - start)/1000000 + " ms");
    }

    private static int c;
    private static int[] Bs = new int[N + 1], Bt = new int[N + 1];
    private static char[] a = new char[N];
    private static void gen(int s, int t, int r) {
        if (s > 0 && t > 0) {
            int j = oracle(s, t, r);
            for (int i = t - 1; i >= j; i--) {
                updateBlock(s, t, i);
                char tmp = a[s - 1]; a[s - 1] = a[s+t-i - 1]; a[s+t-i - 1] = tmp;
                gen(s-1, t-i, testSuffix(r) ? c-1 : r);
                tmp = a[s - 1]; a[s - 1] = a[s+t-i - 1]; a[s+t-i - 1] = tmp;
                restoreBlock(s, t, i);
            }
        }
        visit();
    }

    private static int oracle(int s, int t, int r) {
        int j = pseudoOracle(s, t, r);
        updateBlock(s, t, j);
        int p = testNecklace(testSuffix(r) ? c - 1 : r);
        restoreBlock(s, t, j);
        return p == N ? j : j + 1;
    }

    private static int pseudoOracle(int s, int t, int r) {
        if (s == 1) return t;
        if (c == 1) return s == 2 ? N / 2 : 1;
        if (s - 1 > Bs[r] + 1) return 0;
        if (s - 1 == Bs[r] + 1) return cmpPair(s-1, t, Bs[c-1]+1, Bt[c-1]) <= 0 ? 0 : 1;
        if (s - 1 == Bs[r]) {
            if (s == 2) return Math.max(t - Bt[r], (t+1) >> 1);
            return Math.max(t - Bt[r], (cmpPair(s-1, t, Bs[c-1] + 1, Bt[c-1]) <= 0) ? 0 : 1); 
        }
        if (s == Bs[r]) return t;
        throw new UnsupportedOperationException("Hit the case not covered by the paper or its accompanying code");
    }

    private static int testNecklace(int r) {
        if (density == 0 || density == N) return 1;
        int p = 0;
        for (int i = 0; i < c; i++) {
            if (r - i <= 0) r += c;
            if (cmpBlocks(c-i, r-i) < 0) return 0;
            if (cmpBlocks(c-i, r-1) > 0) return N;
            if (r < c) p += Bs[r-i] + Bt[r-i];
        }
        return p;
    }

    private static int cmpPair(int a1, int a2, int b1, int b2) {
        if (a1 < b1) return -1;
        if (a1 > b1) return 1;
        if (a2 < b2) return -1;
        if (a2 > b2) return 1;
        return 0;
    }

    private static int cmpBlocks(int i, int j) {
        return cmpPair(Bs[i], Bt[i], Bs[j], Bt[j]);
    }

    private static boolean testSuffix(int r) {
        for (int i = 0; i < r; i++) {
            if (c - 1 - i == r) return true;
            if (cmpBlocks(c-1-i, r-i) < 0) return false;
            if (cmpBlocks(c-1-i, r-1) > 0) return true;
        }
        return false;
    }

    private static void updateBlock(int s, int t, int i) {
        if (i == 0 && c > 1) {
            Bs[c-1]++;
            Bs[c] = s - 1;
        }
        else {
            Bs[c] = 1;
            Bt[c] = i;
            Bs[c+1] = s-1;
            Bt[c+1] = t-i;
            c++;
        }
    }

    private static void restoreBlock(int s, int t, int i) {
        if (i == 0 && (c > 0 || (Bs[1] != 1 || Bt[1] != 0))) {
            Bs[c-1]--;
            Bs[c] = s;
        }
        else {
            Bs[c-1] = s;
            Bt[c-1] = t;
            c--;
        }
    }

    private static long[] stats = new long[N/2+1];
    private static long visited = 0;
    private static void visit() {
        String word = new String(a);

        visited++;
        if (precedesReversal(word) && testTernary(word)) System.out.println(word + " after " + (System.nanoTime() - start)/1000000 + " ms");
        if (System.nanoTime() > nextProgressReport) {
            System.out.println("Progress: visited " + visited + "; stats " + Arrays.toString(stats) + " after " + (System.nanoTime() - start)/1000000 + " ms");
             nextProgressReport += 5 * 60 * 1000000000L;
        }
    }

    private static boolean precedesReversal(String w) {
        int n = w.length();
        StringBuilder rev = new StringBuilder(w);
        rev.reverse();
        rev.append(rev, 0, n);
        for (int i = 0; i < n; i++) {
            if (rev.substring(i, i + n).compareTo(w) < 0) return false;
        }
        return true;
    }

    private static boolean testTernary(String word) {
        int n = word.length();
        String rep = word + word;

        int base = 1;
        for (char ch : word.toCharArray()) base += ch & 1;

        // Operating base b for b up to 32 implies that we can multiply by b modulo p<2^57 without overflowing a long.
        // We're storing 3^(n/2) ~= 2^(0.8*n) sums, so while n < 35.6 we don't get *too* bad a probability of false reject.
        // (In fact the birthday paradox assumes independence, and our values aren't independent, so we're better off than that).
        long p = (1L << 57) - 13;
        long[] basis = new long[n];
        basis[0] = 1;
        for (int i = 1; i < basis.length; i++) basis[i] = (basis[i-1] * base) % p;

        int rows = n / 2 + 1;
        long[] colVals = new long[n];
        for (int col = 0; col < n; col++) {
            for (int row = 0; row < rows; row++) {
                colVals[col] = (colVals[col] + basis[row] * (rep.charAt(row + col) & 1)) % p;
            }
        }

        MapInt57Int27 map = new MapInt57Int27();
        // Special-case the initial insertion.
        int[] oldLens = new int[map.entries.length];
        int[] oldSupercounts = new int[1 << 10];
        {
            // count = 1
            for (int k = 0; k < n/2; k++) {
                int val = 1 << (25 - k);
                if (!map.put(colVals[k], val)) { stats[1]++; return false; }
                if (!map.put(colVals[k + n/2], val + (1 << 26))) { stats[1]++; return false; }
            }
        }
        final long keyMask = (1L << 37) - 1;
        for (int count = 2; count <= n/2; count++) {
            int[] lens = map.counts.clone();
            int[] supercounts = map.supercounts.clone();
            for (int sup = 0; sup < 1 << 10; sup++) {
                int unaccountedFor = supercounts[sup] - oldSupercounts[sup];
                for (int supi = 0; supi < 1 << 10 && unaccountedFor > 0; supi++) {
                    int i = (sup << 10) + supi;
                    int stop = lens[i];
                    unaccountedFor -= stop - oldLens[i];
                    for (int j = oldLens[i]; j < stop; j++) {
                        long existingKV = map.entries[i][j];
                        long existingKey = ((existingKV & keyMask) << 20) + i;
                        int existingVal = (int)(existingKV >>> 37);

                        // For each possible prepend...
                        int half = (existingVal >> 26) * n/2;
                        // We have 27 bits of key, of which the top marks the half, so 26 bits. That means there are 6 bits at the top which we need to not count.
                        int k = Integer.numberOfLeadingZeros(existingVal << 6) - 1;
                        while (k >= 0) {
                            int newVal = existingVal | (1 << (25 - k));
                            long pos = (existingKey + colVals[k + half]) % p;
                            if (pos << 1 > p) pos = p - pos;
                            if (pos == 0 || !map.put(pos, newVal)) { stats[count]++; return false; }
                            long neg = (p - existingKey + colVals[k + half]) % p;
                            if (neg << 1 > p) neg = p - neg;
                            if (neg == 0 || !map.put(neg, newVal)) { stats[count]++; return false; }
                            k--;
                        }
                    }
                }
            }
            oldLens = lens;
            oldSupercounts = supercounts;
        }

        stats[n/2]++;
        return true;
    }

    static class MapInt57Int27 {
        private long[][] entries;
        private int[] counts;
        private int[] supercounts;

        public MapInt57Int27() {
            entries = new long[1 << 20][];
            counts = new int[1 << 20];
            supercounts = new int[1 << 10];
        }

        public boolean put(long key, int val) {
            int bucket = (int)(key & (entries.length - 1));
            long insert = (key >>> 20) | (((long)val) << 37);
            final long mask = (1L << 37) - 1;

            long[] chain = entries[bucket];
            if (chain == null) {
                chain = new long[16];
                entries[bucket] = chain;
                chain[0] = insert;
                counts[bucket]++;
                supercounts[bucket >> 10]++;
                return true;
            }

            int stop = counts[bucket];
            for (int i = 0; i < stop; i++) {
                if ((chain[i] & mask) == (insert & mask)) {
                    return false;
                }
            }

            if (stop == chain.length) {
                long[] newChain = new long[chain.length < 512 ? chain.length << 1 : chain.length + 512];
                System.arraycopy(chain, 0, newChain, 0, chain.length);
                entries[bucket] = newChain;
                chain = newChain;
            }
            chain[stop] = insert;
            counts[bucket]++;
            supercounts[bucket >> 10]++;
            return true;
        }
    }
}

İlk bulunan

000001001010110001000101001111111111

ve 15 saat içindeki tek isabet bu.

Daha küçük kazananlar:

4/3:    0111                       (plus 8 different 8/6)
9/6:    001001011                  (and 5 others)
11/7:   00010100111                (and 3 others)
13/8:   0001001101011              (and 5 others)
15/9:   000010110110111            (and 21 others)
16/9:   0000101110111011           (and 1 other)
20/11:  00000101111011110111       (and others)
22/12:  0000001100110011101011     (and others)
24/13:  000000101011101011101011   (and others)
26/14:  00000001101110010011010111 (and others)
28/15:  0000000010000111100111010111 (and others)
30/16:  000000001011001110011010101111 (and probably others)
32/17:  00001100010010100100101011111111 (and others)
34/18:  0000101000100101000110010111111111 (and others)

Bu iyi bir gelişme. Lyndon kelimelerini kullanmak, ilk satır için 2 ^ n yerine kabaca 2 ^ n / n ikili dizeleri kontrol etmeniz gerektiği anlamına gelir.

BigInteger'in her basamağını matris hücresi olarak kullandığınız için, n> 10 olduğunda yanlış cevap olmaz mı?
kennytm

@KennyTM, ikinci parametrenin sayı tabanı olduğunu unutmayın. Küçük bir hata var: Geçersiz çözümleri kabul etmek yerine geçerli çözümleri atması anlamında başarısız olsa da, kullanmak nyerine kullanmalıyım rows. Ayrıca sonuçları etkilemez.
Peter Taylor

1
Daha hızlı olarak değerlendirilebilecek başka bir eşdeğer koşul bulamadığımız sürece X kontrolü özelliği çok zaman alıcı olduğundan pratik olarak bu puanla sınırlı olduğumuzu düşünüyorum. Bu yüzden "asal olmayan" X = D özelliği anlamına geldiğini görmek için çok istekliydim
sadece

1
@SuboptimusPrime, bunu people.math.sfu.ca/~kya17/teaching/math343/16-343.pdf adresinde buldum ve bir hatayı düzelttim. İlginçtir ki şimdi Lyndon kelimeleri yinelemek için kullandığım algoritma, aynı zamanda k-of-n altkümeleri yapan ilgili algoritma sınıflarından biridir, bu yüzden bazı kodları yeniden düzenleyebilir ve paylaşabiliriz.
Peter Taylor

9

Python 2 - 21/12

Herhangi biri 2-(3/n)için her zaman var olduğunu kanıtlama sürecinden

Bu sorudan esinlenerek olası matrisleri kaba kuvvetlemek için De Bruijn Sekansını kullandım . Ve kaba kuvvet n=6,7,8,9,10uyguladıktan sonra, en yüksek çözümün daima şeklinde olduğu bir desen buldum (n, 2n-3).

Bu yüzden bu matrisin şeklini bruteforce etmek ve işleri hızlandırmak için çok işlemciliği kullanmak için başka bir yöntem oluşturdum, çünkü bu görev oldukça dağıtılabilir. 16 çekirdekli ubuntu'da n=12yaklaşık 4 dakika içinde çözüm bulabilir :

Deneme (0, 254)
Deneme (254, 509)
Deneme (509, 764)
Deneme (764, 1018)
Deneme (1018, 1273)
Deneme (1273, 1528)
Deneme (1528, 1782)
Deneme (1782, 2037)
Deneme (2037, 2292)
Deneme (2292, 2546)
Deneme (2546, 2801)
Deneme (2801, 3056)
Deneme (3056, 3310)
Deneme (3820, 4075)
Deneme (3565, 3820)
Deneme (3310, 3565)
(1625, 1646)
[[0 0 0 1 0 0 1 0 1 1 1 1 0 0 0 1 0 0 1 1 0]
 [0 0 1 0 0 1 0 1 1 1 1 0 0 0 1 0 0 1 1 0 0]
 [0 1 0 0 1 0 1 1 1 1 0 0 0 1 0 0 1 1 0 0 0]
 [1 0 0 1 0 1 1 1 1 0 0 0 1 0 0 1 1 0 0 0 0]
 [0 0 1 0 1 1 1 1 0 0 0 1 0 0 1 1 0 0 0 0 1]
 [0 1 0 1 1 1 1 0 0 0 1 0 0 1 1 0 0 0 0 1 0]
 [1 0 1 1 1 1 0 0 0 1 0 0 1 1 0 0 0 0 1 0 0]
 [0 1 1 1 1 0 0 0 1 0 0 1 1 0 0 0 0 1 0 0 1]
 [1 1 1 1 0 0 0 1 0 0 1 1 0 0 0 0 1 0 0 1 0]
 [1 1 1 0 0 0 1 0 0 1 1 0 0 0 0 1 0 0 1 0 1]
 [1 1 0 0 0 1 0 0 1 1 0 0 0 0 1 0 0 1 0 1 1]
 [1 0 0 0 1 0 0 1 1 0 0 0 0 1 0 0 1 0 1 1 1]]
(12, 21)
Puan: 1.7500

gerçek 4m9.121s
kullanıcı 42m47.472s
sys 0m5.780s

Hesaplamanın büyük kısmı, tüm alt kümelerin kontrol edilmesini gerektiren X özelliğini kontrol etmeye gider ( 2^(2n-3)alt kümeler vardır)

İlk satırı soruda olduğu gibi sağa değil sola döndürdüğümü unutmayın. Fakat bunlar eşdeğerdir, çünkü tüm matrisi tersine çevirebilirsiniz. =)

Kod:

import math
import numpy as np
from itertools import combinations
from multiprocessing import Process, Queue, cpu_count

def de_bruijn(k, n):
    """
    De Bruijn sequence for alphabet k
    and subsequences of length n.
    """
    alphabet = list(range(k))
    a = [0] * k * n
    sequence = []
    def db(t, p):
        if t > n:
            if n % p == 0:
                for j in range(1, p + 1):
                    sequence.append(a[j])
        else:
            a[t] = a[t - p]
            db(t + 1, p)
            for j in range(a[t - p] + 1, k):
                a[t] = j
                db(t + 1, t)
    db(1, 1)
    return sequence

def generate_cyclic_matrix(seq, n):
    result = []
    for i in range(n):
        result.append(seq[i:]+seq[:i])
    return np.array(result)

def generate_cyclic_matrix_without_property_x(n=3, n_jobs=-1):
    seq = de_bruijn(2,n)
    seq = seq + seq[:n/2]
    max_idx = len(seq)
    max_score = 1
    max_matrix = np.array([[]])
    max_ij = (0,0)
    workers = []
    queue = Queue()
    if n_jobs < 0:
        n_jobs += cpu_count()+1
    for i in range(n_jobs):
        worker = Process(target=worker_function, args=(seq,i*(2**n-2*n+3)/n_jobs, (i+1)*(2**n-2*n+3)/n_jobs, n, queue))
        workers.append(worker)
        worker.start()
    (result, max_ij) = queue.get()
    for worker in workers:
        worker.terminate()
    return (result, max_ij)

def worker_function(seq,min_idx,max_idx,n,queue):
    print 'Trying (%d, %d)' % (min_idx, max_idx)
    for i in range(min_idx, max_idx):
        j = i+2*n-3
        result = generate_cyclic_matrix(seq[i:j], n)
        if has_property_x(result):
            continue
        else:
            queue.put( (result, (i,j)) )
            return

def has_property_x(mat):
    vecs = zip(*mat)
    vector_sums = set()
    for i in range(1, len(vecs)+1):
        for combination in combinations(vecs, i):
            vector_sum = tuple(sum(combination, np.array([0]*len(mat))))
            if vector_sum in vector_sums:
                return True
            else:
                vector_sums.add(vector_sum)
    return False

def main():
    import sys
    n = int(sys.argv[1])
    if len(sys.argv) > 2:
        n_jobs = int(sys.argv[2])
    else:
        n_jobs = -1
    (matrix, ij) = generate_cyclic_matrix_without_property_x(n, n_jobs)
    print ij
    print matrix
    print matrix.shape
    print 'Score: %.4f' % (float(matrix.shape[1])/matrix.shape[0])

if __name__ == '__main__':
    main()

Eski cevap, referans için

Şimdiye kadar en uygun çözüm ( n=10):

(855, 872)
[[1 1 0 1 0 1 0 0 1 1 1 1 0 1 1 1 0]
 [1 0 1 0 1 0 0 1 1 1 1 0 1 1 1 0 1]
 [0 1 0 1 0 0 1 1 1 1 0 1 1 1 0 1 1]
 [1 0 1 0 0 1 1 1 1 0 1 1 1 0 1 1 0]
 [0 1 0 0 1 1 1 1 0 1 1 1 0 1 1 0 1]
 [1 0 0 1 1 1 1 0 1 1 1 0 1 1 0 1 0]
 [0 0 1 1 1 1 0 1 1 1 0 1 1 0 1 0 1]
 [0 1 1 1 1 0 1 1 1 0 1 1 0 1 0 1 0]
 [1 1 1 1 0 1 1 1 0 1 1 0 1 0 1 0 0]
 [1 1 1 0 1 1 1 0 1 1 0 1 0 1 0 0 1]]
(10, 17)
Puan: 1.7000

Şunun için n=7:

(86, 97)
[[0 1 1 1 0 1 0 0 1 1 1]
 [1 1 1 0 1 0 0 1 1 1 0]
 [1 1 0 1 0 0 1 1 1 0 1]
 [1 0 1 0 0 1 1 1 0 1 1]
 [0 1 0 0 1 1 1 0 1 1 1]
 [1 0 0 1 1 1 0 1 1 1 0]
 [0 0 1 1 1 0 1 1 1 0 1]]
(7, 11)
Puan: 1.5714

OP ( n=8) tarafından tarif edilen şekle sahip bir çözüm :

(227, 239)
[[0 1 0 1 1 1 1 1 0 1 1 0]
 [1 0 1 1 1 1 1 0 1 1 0 0]
 [0 1 1 1 1 1 0 1 1 0 0 1]
 [1 1 1 1 1 0 1 1 0 0 1 0]
 [1 1 1 1 0 1 1 0 0 1 0 1]
 [1 1 1 0 1 1 0 0 1 0 1 1]
 [1 1 0 1 1 0 0 1 0 1 1 1]
 [1 0 1 1 0 0 1 0 1 1 1 1]]
(8, 12)
Puan: 1.5000

Ama daha iyisi ( n=8):

(95, 108)
[[0 1 1 0 0 1 0 0 0 1 1 0 1]
 [1 1 0 0 1 0 0 0 1 1 0 1 0]
 [1 0 0 1 0 0 0 1 1 0 1 0 1]
 [0 0 1 0 0 0 1 1 0 1 0 1 1]
 [0 1 0 0 0 1 1 0 1 0 1 1 0]
 [1 0 0 0 1 1 0 1 0 1 1 0 0]
 [0 0 0 1 1 0 1 0 1 1 0 0 1]
 [0 0 1 1 0 1 0 1 1 0 0 1 0]]
(8, 13)
Puan: 1.6250

Ayrıca başka bir optimal çözüm buldu n=9:

(103, 118)
[[0 1 0 1 1 1 0 0 0 0 1 1 0 0 1]
 [1 0 1 1 1 0 0 0 0 1 1 0 0 1 0]
 [0 1 1 1 0 0 0 0 1 1 0 0 1 0 1]
 [1 1 1 0 0 0 0 1 1 0 0 1 0 1 0]
 [1 1 0 0 0 0 1 1 0 0 1 0 1 0 1]
 [1 0 0 0 0 1 1 0 0 1 0 1 0 1 1]
 [0 0 0 0 1 1 0 0 1 0 1 0 1 1 1]
 [0 0 0 1 1 0 0 1 0 1 0 1 1 1 0]
 [0 0 1 1 0 0 1 0 1 0 1 1 1 0 0]]
(9, 15)
Puan: 1.6667

Kod aşağıdaki gibidir. Sadece kaba kuvvet, ama en azından OP'nin iddiasından daha iyi bir şey bulabilir =)

import numpy as np
from itertools import combinations

def de_bruijn(k, n):
    """
    De Bruijn sequence for alphabet k
    and subsequences of length n.
    """
    alphabet = list(range(k))
    a = [0] * k * n
    sequence = []
    def db(t, p):
        if t > n:
            if n % p == 0:
                for j in range(1, p + 1):
                    sequence.append(a[j])
        else:
            a[t] = a[t - p]
            db(t + 1, p)
            for j in range(a[t - p] + 1, k):
                a[t] = j
                db(t + 1, t)
    db(1, 1)
    return sequence

def generate_cyclic_matrix(seq, n):
    result = []
    for i in range(n):
        result.append(seq[i:]+seq[:i])
    return np.array(result)

def generate_cyclic_matrix_without_property_x(n=3):
    seq = de_bruijn(2,n)
    max_score = 0
    max_matrix = []
    max_ij = (0,0)
    for i in range(2**n+1):
        for j in range(i+n, 2**n+1):
            score = float(j-i)/n
            if score <= max_score:
                continue
            result = generate_cyclic_matrix(seq[i:j], n)
            if has_property_x(result):
                continue
            else:
                if score > max_score:
                    max_score = score
                    max_matrix = result
                    max_ij = (i,j)
    return (max_matrix, max_ij)

def has_property_x(mat):
    vecs = zip(*mat)
    vector_sums = set()
    for i in range(1, len(vecs)):
        for combination in combinations(vecs, i):
            vector_sum = tuple(sum(combination, np.array([0]*len(mat))))
            if vector_sum in vector_sums:
                return True
            else:
                vector_sums.add(vector_sum)
    return False

def main():
    import sys
    n = int(sys.argv[1])
    (matrix, ij) = generate_cyclic_matrix_without_property_x(n)
    print ij
    print matrix
    print matrix.shape
    print 'Score: %.4f' % (float(matrix.shape[1])/matrix.shape[0])

if __name__ == '__main__':
    main()

Harika bir başlangıç ​​:)

2
@Lembik Şimdi 2'nin altında herhangi bir puan olduğunu iddia eden neredeyse neredeyse (hesaplama süresi ile sınırlı)
yenebilirim

Bu durumda, 19/10 yenebilir misin?

@Lembik Yapabileceğimi sanmıyorum. O gerektirir n >= 31ı kadar kontrol etmek gerekiyordu anlamına gelen 2^(2n-3) = 2^5931 boyutlu vektör kombinasyonları.
Yaşamımızda bitmeyecek

2
Her zamann*(2n-3)
xnor

7

24/13 26/14 28/15 30/16 32/17 (C #)

Düzenle: Cevabımdaki eski bilgileri sildi. Ben kendi optimizasyon bazı ekledim rağmen, Peter Taylor ( Düzenleme: şimdi daha iyi bir algoritma kullanıyor gibi görünüyor) çoğunlukla aynı algoritmayı kullanıyorum:

  • Aynı vektör toplamına sahip sütun kümelerini arama stratejisi "ortada buluş" stratejisini uyguladım ( bu KennyTM'nin yorumu tarafından önerildi ). Bu strateji, bellek kullanımını çok geliştirdi, ancak oldukça yavaş, bu yüzden HasPropertyXFast"ortada buluşma" yaklaşımını kullanmadan önce eşit miktarda küçük set olup olmadığını hızlıca kontrol eden işlevi ekledim .
  • İşlevdeki sütun kümeleri arasında yineleme yaparken HasPropertyXFast , sütun kümelerini 1 sütun, sonra 2, 3 vb. Sütun toplamlarının ilk çarpışması bulunur bulunmaz işlev geri döner. Pratikte bu, milyonlarca değil, yalnızca birkaç yüz veya binlerce sütun kümesini kontrol etmem gerektiği anlamına gelir.
  • longTüm sütunları ve vektör toplamlarını depolamak ve karşılaştırmak için değişkenler kullanıyorum . Bu yaklaşım, sütunları diziler olarak karşılaştırmaya göre en azından daha büyük bir mertebedir.
  • long Veri türü ve kullanım kalıplarım için optimize edilmiş kendi hashset uygulamasını ekledim .
  • Bellek ayırma sayısını azaltmak ve performansı artırmak için uygulamanın tüm ömrü boyunca aynı 3 karma setleri yeniden kullanıyorum.
  • Çoklu kullanım desteği.

Program çıktısı:

00000000000111011101010010011111
10000000000011101110101001001111
11000000000001110111010100100111
11100000000000111011101010010011
11110000000000011101110101001001
11111000000000001110111010100100
01111100000000000111011101010010
00111110000000000011101110101001
10011111000000000001110111010100
01001111100000000000111011101010
00100111110000000000011101110101
10010011111000000000001110111010
01001001111100000000000111011101
10100100111110000000000011101110
01010010011111000000000001110111
10101001001111100000000000111011
11010100100111110000000000011101
Score: 32/17 = 1,88235294117647
Time elapsed: 02:11:05.9791250

Kod:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

class Program
{
    const int MaxWidth = 32;
    const int MaxHeight = 17;

    static object _lyndonWordLock = new object();

    static void Main(string[] args)
    {
        Stopwatch sw = Stopwatch.StartNew();
        double maxScore = 0;
        const int minHeight = 17; // 1
        for (int height = minHeight; height <= MaxHeight; height++)
        {
            Console.WriteLine("Row count = " + height);
            Console.WriteLine("Time elapsed: " + sw.Elapsed + "\r\n");

            int minWidth = Math.Max(height, (int)(height * maxScore) + 1);
            for (int width = minWidth; width <= MaxWidth; width++)
            {
#if MULTITHREADING
                int[,] matrix = FindMatrixParallel(width, height);
#else
                int[,] matrix = FindMatrix(width, height);
#endif
                if (matrix != null)
                {
                    PrintMatrix(matrix);
                    Console.WriteLine("Time elapsed: " + sw.Elapsed + "\r\n");
                    maxScore = (double)width / height;
                }
                else
                    break;
            }
        }
    }

#if MULTITHREADING
    static int[,] FindMatrixParallel(int width, int height)
    {
        _lyndonWord = 0;
        _stopSearch = false;

        int threadCount = Environment.ProcessorCount;
        Task<int[,]>[] tasks = new Task<int[,]>[threadCount];
        for (int i = 0; i < threadCount; i++)
            tasks[i] = Task<int[,]>.Run(() => FindMatrix(width, height));

        int index = Task.WaitAny(tasks);
        if (tasks[index].Result != null)
            _stopSearch = true;

        Task.WaitAll(tasks);
        foreach (Task<int[,]> task in tasks)
            if (task.Result != null)
                return task.Result;

        return null;
    }

    static volatile bool _stopSearch;
#endif

    static int[,] FindMatrix(int width, int height)
    {
#if MULTITHREADING
        _columnSums = new LongSet();
        _left = new LongSet();
        _right = new LongSet();
#endif

        foreach (long rowTemplate in GetLyndonWords(width))
        {
            int[,] matrix = new int[width, height];
            for (int x = 0; x < width; x++)
            {
                int cellValue = (int)(rowTemplate >> (width - 1 - x)) % 2;
                for (int y = 0; y < height; y++)
                    matrix[(x + y) % width, y] = cellValue;
            }

            if (!HasPropertyX(matrix))
                return matrix;

#if MULTITHREADING
            if (_stopSearch)
                return null;
#endif
        }

        return null;
    }

#if MULTITHREADING
    static long _lyndonWord;
#endif

    static IEnumerable<long> GetLyndonWords(int length)
    {
        long lyndonWord = 0;
        long max = (1L << (length - 1)) - 1;
        while (lyndonWord <= max)
        {
            if ((lyndonWord % 2 != 0) && PrecedesReversal(lyndonWord, length))
                yield return lyndonWord;

#if MULTITHREADING
            lock (_lyndonWordLock)
            {
                if (_lyndonWord <= max)
                    _lyndonWord = NextLyndonWord(_lyndonWord, length);
                else
                    yield break;

                lyndonWord = _lyndonWord;
            }
#else
            lyndonWord = NextLyndonWord(lyndonWord, length);
#endif
        }
    }

    static readonly int[] _lookup =
    {
        32, 0, 1, 26, 2, 23, 27, 0, 3, 16, 24, 30, 28, 11, 0, 13, 4, 7, 17,
        0, 25, 22, 31, 15, 29, 10, 12, 6, 0, 21, 14, 9, 5, 20, 8, 19, 18
    };

    static int NumberOfTrailingZeros(uint i)
    {
        return _lookup[(i & -i) % 37];
    }

    static long NextLyndonWord(long w, int length)
    {
        if (w == 0)
            return 1;

        int currentLength = length - NumberOfTrailingZeros((uint)w);
        while (currentLength < length)
        {
            w += w >> currentLength;
            currentLength *= 2;
        }

        w++;

        return w;
    }

    private static bool PrecedesReversal(long lyndonWord, int length)
    {
        int shift = length - 1;

        long reverse = 0;
        for (int i = 0; i < length; i++)
        {
            long bit = (lyndonWord >> i) % 2;
            reverse |= bit << (shift - i);
        }

        for (int i = 0; i < length; i++)
        {
            if (reverse < lyndonWord)
                return false;

            long bit = reverse % 2;
            reverse /= 2;
            reverse += bit << shift;
        }

        return true;
    }

#if MULTITHREADING
    [ThreadStatic]
#endif
    static LongSet _left = new LongSet();
#if MULTITHREADING
    [ThreadStatic]
#endif
    static LongSet _right = new LongSet();

    static bool HasPropertyX(int[,] matrix)
    {
        long[] matrixColumns = GetMatrixColumns(matrix);
        if (matrixColumns.Length == 1)
            return false;

        return HasPropertyXFast(matrixColumns) || MeetInTheMiddle(matrixColumns);
    }

    static bool MeetInTheMiddle(long[] matrixColumns)
    {
        long[] leftColumns = matrixColumns.Take(matrixColumns.Length / 2).ToArray();
        long[] rightColumns = matrixColumns.Skip(matrixColumns.Length / 2).ToArray();

        if (PrepareHashSet(leftColumns, _left) || PrepareHashSet(rightColumns, _right))
            return true;

        foreach (long columnSum in _left.GetValues())
            if (_right.Contains(columnSum))
                return true;

        return false;
    }

    static bool PrepareHashSet(long[] columns, LongSet sums)
    {
        int setSize = (int)System.Numerics.BigInteger.Pow(3, columns.Length);
        sums.Reset(setSize, setSize);
        foreach (long column in columns)
        {
            foreach (long sum in sums.GetValues())
                if (!sums.Add(sum + column) || !sums.Add(sum - column))
                    return true;

            if (!sums.Add(column) || !sums.Add(-column))
                return true;
        }

        return false;
    }

#if MULTITHREADING
    [ThreadStatic]
#endif
    static LongSet _columnSums = new LongSet();

    static bool HasPropertyXFast(long[] matrixColumns)
    {
        int width = matrixColumns.Length;

        int maxColumnCount = width / 3;
        _columnSums.Reset(width, SumOfBinomialCoefficients(width, maxColumnCount));

        int resetBit, setBit;
        for (int k = 1; k <= maxColumnCount; k++)
        {
            uint columnMask = (1u << k) - 1;
            long sum = 0;
            for (int i = 0; i < k; i++)
                sum += matrixColumns[i];

            while (true)
            {
                if (!_columnSums.Add(sum))
                    return true;
                if (!NextColumnMask(columnMask, k, width, out resetBit, out setBit))
                    break;
                columnMask ^= (1u << resetBit) ^ (1u << setBit);
                sum = sum - matrixColumns[resetBit] + matrixColumns[setBit];
            }
        }

        return false;
    }

    // stolen from Peter Taylor
    static bool NextColumnMask(uint mask, int k, int n, out int resetBit, out int setBit)
    {
        int gap = NumberOfTrailingZeros(~mask);
        int next = 1 + NumberOfTrailingZeros(mask & (mask + 1));

        if (((k - gap) & 1) == 0)
        {
            if (gap == 0)
            {
                resetBit = next - 1;
                setBit = next - 2;
            }
            else if (gap == 1)
            {
                resetBit = 0;
                setBit = 1;
            }
            else
            {
                resetBit = gap - 2;
                setBit = gap;
            }
        }
        else
        {
            if (next == n)
            {
                resetBit = 0;
                setBit = 0;
                return false;
            }

            if ((mask & (1 << next)) == 0)
            {
                if (gap == 0)
                {
                    resetBit = next - 1;
                    setBit = next;
                }
                else
                {
                    resetBit = gap - 1;
                    setBit = next;
                }
            }
            else
            {
                resetBit = next;
                setBit = gap;
            }
        }

        return true;
    }

    static long[] GetMatrixColumns(int[,] matrix)
    {
        int width = matrix.GetLength(0);
        int height = matrix.GetLength(1);

        long[] result = new long[width];
        for (int x = 0; x < width; x++)
        {
            long column = 0;
            for (int y = 0; y < height; y++)
            {
                column *= 13;
                if (matrix[x, y] == 1)
                    column++;
            }

            result[x] = column;
        }

        return result;
    }

    static int SumOfBinomialCoefficients(int n, int k)
    {
        int result = 0;
        for (int i = 0; i <= k; i++)
            result += BinomialCoefficient(n, i);
        return result;
    }

    static int BinomialCoefficient(int n, int k)
    {
        long result = 1;
        for (int i = n - k + 1; i <= n; i++)
            result *= i;
        for (int i = 2; i <= k; i++)
            result /= i;
        return (int)result;
    }

    static void PrintMatrix(int[,] matrix)
    {
        int width = matrix.GetLength(0);
        int height = matrix.GetLength(1);

        for (int y = 0; y < height; y++)
        {
            for (int x = 0; x < width; x++)
                Console.Write(matrix[x, y]);
            Console.WriteLine();
        }

        Console.WriteLine("Score: {0}/{1} = {2}", width, height, (double)width / height);
    }
}


class LongSet
{
    private static readonly int[] primes =
    {
        17, 37, 67, 89, 113, 149, 191, 239, 307, 389, 487, 613, 769, 967, 1213, 1523, 1907,
        2389, 2999, 3761, 4703, 5879, 7349, 9187, 11489, 14369, 17971, 22469, 28087, 35111,
        43889, 54869, 68597, 85751, 107197, 133999, 167521, 209431, 261791, 327247, 409063,
        511333, 639167, 798961, 998717, 1248407, 1560511, 1950643, 2438309, 3047909,
        809891, 4762367, 5952959, 7441219, 9301529, 11626913, 14533661, 18167089, 22708867,
        28386089, 35482627, 44353297, 55441637, 69302071, 86627603, 108284507, 135355669,
        169194593, 211493263, 264366593, 330458263, 413072843, 516341057, 645426329,
        806782913, 1008478649, 1260598321
    };

    private int[] _buckets;
    private int[] _nextItemIndexes;
    private long[] _items;
    private int _count;
    private int _minCapacity;
    private int _maxCapacity;
    private int _currentCapacity;

    public LongSet()
    {
        Initialize(0, 0);
    }

    private int GetPrime(int capacity)
    {
        foreach (int prime in primes)
            if (prime >= capacity)
                return prime;

        return int.MaxValue;
    }

    public void Reset(int minCapacity, int maxCapacity)
    {
        if (maxCapacity > _maxCapacity)
            Initialize(minCapacity, maxCapacity);
        else
            ClearBuckets();
    }

    private void Initialize(int minCapacity, int maxCapacity)
    {
        _minCapacity = GetPrime(minCapacity);
        _maxCapacity = GetPrime(maxCapacity);
        _currentCapacity = _minCapacity;

        _buckets = new int[_maxCapacity];
        _nextItemIndexes = new int[_maxCapacity];
        _items = new long[_maxCapacity];
        _count = 0;
    }

    private void ClearBuckets()
    {
        Array.Clear(_buckets, 0, _currentCapacity);
        _count = 0;
        _currentCapacity = _minCapacity;
    }

    public bool Add(long value)
    {
        int bucket = (int)((ulong)value % (ulong)_currentCapacity);
        for (int i = _buckets[bucket] - 1; i >= 0; i = _nextItemIndexes[i])
            if (_items[i] == value)
                return false;

        if (_count == _currentCapacity)
        {
            Grow();
            bucket = (int)((ulong)value % (ulong)_currentCapacity);
        }

        int index = _count;
        _items[index] = value;
        _nextItemIndexes[index] = _buckets[bucket] - 1;
        _buckets[bucket] = index + 1;
        _count++;

        return true;
    }

    private void Grow()
    {
        Array.Clear(_buckets, 0, _currentCapacity);

        const int growthFactor = 8;
        int newCapacity = GetPrime(_currentCapacity * growthFactor);
        if (newCapacity > _maxCapacity)
            newCapacity = _maxCapacity;
        _currentCapacity = newCapacity;

        for (int i = 0; i < _count; i++)
        {
            int bucket = (int)((ulong)_items[i] % (ulong)newCapacity);
            _nextItemIndexes[i] = _buckets[bucket] - 1;
            _buckets[bucket] = i + 1;
        }
    }

    public bool Contains(long value)
    {
        int bucket = (int)((ulong)value % (ulong)_buckets.Length);
        for (int i = _buckets[bucket] - 1; i >= 0; i = _nextItemIndexes[i])
            if (_items[i] == value)
                return true;

        return false;
    }

    public IReadOnlyList<long> GetValues()
    {
        return new ArraySegment<long>(_items, 0, _count);
    }
}

Yapılandırma dosyası:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <runtime>
    <gcAllowVeryLargeObjects enabled="true" />
  </runtime>
</configuration>

Bazı açılardan optimize edilmiş olmak yerine kötümser görünüyorsunuz. Gerçekten bir optimizasyona benzeyen tek şey, bitlerin kullanarak ulongkaydırma çakmasına izin vermek ve kullanmak yerine kaydırma sarmasına izin vermektir BigInteger.
Peter Taylor

@PeterTaylor En önemli optimizasyon HasPropertyX fonksiyonundadır. Sütun toplamlarının ilk çarpışması bulunur bulunmaz işlev döner (skorLyndonWord işlevinizin aksine). Ayrıca, ilk önce çarpışma olasılığı daha yüksek olan sütun kümelerini kontrol edecek şekilde sütun maskelerini sıraladım. Bu iki optimizasyon performansı büyüklük sırasına göre iyileştirdi.
Suboptimus Prime

Performans değişiklikleri genellikle şaşırtıcı olsa da, prensip olarak erken durdurma 2 kattan fazla vermemeli ve 2 GetSumOfColumnskatından daha fazla maliyet beklediğim ekstra bir döngü ekliyor. Maske sıralama ilginç geliyor: belki bu konu hakkında biraz konuşmak için cevabı düzenle? (Ve bir noktada erken iptal etmenin alternatif bir yolunu deneyeceğim: bunu yapamamın nedeni HashSet'in eşzamanlı yineleme ve modifikasyonu desteklememesi, ancak bir yineleyici ihtiyacından kaçınmak için fikirlerim var) .
Peter Taylor

2
@justhalf, sabit bir boyutta alt kümelerini üzerinde yinelemek için Gri-vari bir yaklaşım kullanarak bir gerçekte değerli. 9 dakikanın altında bir 26/14 ve iki saat içinde 34 tanesini bulmama izin verdi, bu noktada iptal ettim. Şu anda makul bir zamanda 28/15 alıp alamayacağımı görmek için test ediyorum.
Peter Taylor

1
@Lembik, 75.5 saat içinde 29/15'i ayrıntılı bir şekilde araştırdım. 31/16 yaklaşık 3 kat daha uzun sürecek - bir haftadan fazla. 29/15 testini yapmaya başladığımdan beri her ikimiz de bazı optimizasyonlar yaptık, bu yüzden belki bir haftaya inebilir. Kodumu veya SuboptimusPrime kodunu derlemenizi ve o kadar uzun süre açık bırakabileceğiniz bir bilgisayarınız varsa, onu kendiniz çalıştırmanızı engelleyen hiçbir şey yoktur.
Peter Taylor
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.