Hankelable matris sayısını sayın


12

Arka fon

İkili Hankel matrisi , yalnızca 0s ve 1s içeren sabit eğim-köşegenleri (pozitif eğimli köşegenleri) olan bir matristir . 5x5 ikili Hankel matrisi,

a b c d e
b c d e f
c d e f g
d e f g h
e f g h i

nerede a, b, c, d, e, f, g, h, iya 0da 1.

Diyelim bir matris tanımlayan M olarak Hankelable satır ve sütun için bir permütasyon varsa M böylece M bir Hankel matrisidir. Bu, satırların sırasına bir permütasyon ve sütunlara muhtemelen farklı bir permütasyon uygulanabileceği anlamına gelir.

Meydan okuma

Zorluk n , nmatrisler tarafından kaç tane Hankelablen olabileceğinin mümkün olduğunca büyük bir değer olduğunu saymaktır .

Çıktı

Her bir tamsayı için n, 1 yukarı çıkış sayısı ile ilgili Hankelable n göre nmatrisler olan girişleri ile 0ya 1.

İçin n = 1,2,3,4,5cevapları olmalıdır 2,12,230,12076,1446672. (Kodun bunları üretmesi için orlp'ye teşekkürler.)

Zaman sınırı

Kodunuzu makinemde çalıştıracağım ve 1 dakika sonra durduracağım. En büyük n değerine kadar doğru cevapları veren kod kazanır. Zaman sınırı, yanıt verdiğiniz n = 1en büyük değere kadar olan her şey niçindir.

Kazanan en iyi cevap 18 Nisan Cumartesi sonuna kadar olacak.

Kravat Kırıcı

Maksimum bir beraberlik durumunda n, çıkışların ne kadar n+1sürede verileceğini ve en hızlı olanın kazanacağını zamanlayacağım. Aynı anda bir saniyeye kadar koşmaları durumunda n+1, ilk başvuru kazanır.

Diller ve kütüphaneler

Serbestçe kullanılabilen bir derleyici / yorumlayıcı / vb. Dilleri kullanabilirsiniz. Linux ve Linux için serbestçe bulunan tüm kütüphaneler için.

Benim makine

Zamanlamalar makinemde çalışacak. Bu, bir Asus M5A78L-M / USB3 Anakarttaki (Soket AM3 +, 8GB DDR3) AMD FX-8350 Sekiz Çekirdekli İşlemciye standart bir ubuntu yüklemesidir. Bu ayrıca kodunuzu çalıştırabilmem gerektiği anlamına gelir. Sonuç olarak, yalnızca kolayca bulunabilen ücretsiz yazılımları kullanın ve lütfen kodunuzu nasıl derleyeceğiniz ve çalıştıracağınızla ilgili tüm talimatları ekleyin.

notlar

Ben n tüm n matrisler üzerinde yineleme ve her biri açıkladığım özelliği olup olmadığını tespit etmeye çalışıyorum öneririz. Birincisi, çok fazla ve ikinci var, bu tespiti yapmanın hızlı bir yolu yok gibi görünüyor .

Şimdiye kadar lider girişler

  • n = 8 Peter Taylor tarafından. Java
  • orlp ile n = 5. piton

4
Gerçekten "Hankelable" kelimesinden hoşlanıyorum.
Alex

3
İçin n=6toplam olduğunu 260357434. Bellek baskısının CPU zamanından daha büyük bir sorun olduğunu düşünüyorum.
Peter Taylor

Bu harika bir soru. Tamamen nerd-sniped oldum.
alexander-brett

Yanıtlar:


7

Java (n = 8)

import java.util.*;
import java.util.concurrent.*;

public class HankelCombinatorics {
    public static final int NUM_THREADS = 8;

    private static final int[] FACT = new int[13];
    static {
        FACT[0] = 1;
        for (int i = 1; i < FACT.length; i++) FACT[i] = i * FACT[i-1];
    }

    public static void main(String[] args) {
        long prevElapsed = 0, start = System.nanoTime();
        for (int i = 1; i < 12; i++) {
            long count = count(i), elapsed = System.nanoTime() - start;
            System.out.format("%d in %dms, total elapsed %dms\n", count, (elapsed - prevElapsed) / 1000000, elapsed / 1000000);
            prevElapsed = elapsed;
        }
    }

    @SuppressWarnings("unchecked")
    private static long count(int n) {
        int[][] perms = new int[FACT[n]][];
        genPermsInner(0, 0, new int[n], perms, 0);

        // We partition by canonical representation of the row sum multiset, discarding any with a density > 50%.
        Map<CanonicalMatrix, Map<CanonicalMatrix, Integer>> part = new HashMap<CanonicalMatrix, Map<CanonicalMatrix, Integer>>();
        for (int m = 0; m < 1 << (2*n-1); m++) {
            int density = 0;
            int[] key = new int[n];
            for (int i = 0; i < n; i++) {
                key[i] = Integer.bitCount((m >> i) & ((1 << n) - 1));
                density += key[i];
            }
            if (2 * density <= n * n) {
                CanonicalMatrix _key = new CanonicalMatrix(key);
                Map<CanonicalMatrix, Integer> map = part.get(_key);
                if (map == null) part.put(_key, map = new HashMap<CanonicalMatrix, Integer>());
                map.put(new CanonicalMatrix(m, perms[0]), m);
            }
        }

        List<Job> jobs = new ArrayList<Job>();
        ExecutorService pool = Executors.newFixedThreadPool(NUM_THREADS);

        for (Map.Entry<CanonicalMatrix, Map<CanonicalMatrix, Integer>> e : part.entrySet()) {
            Job job = new Job(n, perms, e.getKey().sum() << 1 == n * n ? 0 : 1, e.getValue());
            jobs.add(job);
            pool.execute(job);
        }

        pool.shutdown();
        try {
            pool.awaitTermination(1, TimeUnit.DAYS); // i.e. until it's finished - inaccurate results are useless
        }
        catch (InterruptedException ie) {
            throw new IllegalStateException(ie);
        }

        long total = 0;
        for (Job job : jobs) total += job.subtotal;
        return total;
    }

    private static int genPermsInner(int idx, int usedMask, int[] a, int[][] perms, int off) {
        if (idx == a.length) perms[off++] = a.clone();
        else for (int i = 0; i < a.length; i++) {
            int m = 1 << (a[idx] = i);
            if ((usedMask & m) == 0) off = genPermsInner(idx+1, usedMask | m, a, perms, off);
        }
        return off;
    }

    static class Job implements Runnable {
        private volatile long subtotal = 0;
        private final int n;
        private final int[][] perms;
        private final int shift;
        private final Map<CanonicalMatrix, Integer> unseen;

        public Job(int n, int[][] perms, int shift, Map<CanonicalMatrix, Integer> unseen) {
            this.n = n;
            this.perms = perms;
            this.shift = shift;
            this.unseen = unseen;
        }

        public void run() {
            long result = 0;
            int[][] perms = this.perms;
            Map<CanonicalMatrix, Integer> unseen = this.unseen;
            while (!unseen.isEmpty()) {
                int m = unseen.values().iterator().next();
                Set<CanonicalMatrix> equiv = new HashSet<CanonicalMatrix>();
                for (int[] perm : perms) {
                    CanonicalMatrix canonical = new CanonicalMatrix(m, perm);
                    if (equiv.add(canonical)) {
                        result += canonical.weight() << shift;
                        unseen.remove(canonical);
                    }
                }
            }

            subtotal = result;
        }
    }

    static class CanonicalMatrix {
        private final int[] a;
        private final int hash;

        public CanonicalMatrix(int m, int[] r) {
            this(permuteRows(m, r));
        }

        public CanonicalMatrix(int[] a) {
            this.a = a;
            Arrays.sort(a);

            int h = 0;
            for (int i : a) h = h * 37 + i;
            hash = h;
        }

        private static int[] permuteRows(int m, int[] perm) {
            int[] cols = new int[perm.length];
            for (int i = 0; i < perm.length; i++) {
                for (int j = 0; j < cols.length; j++) cols[j] |= ((m >> (perm[i] + j)) & 1L) << i;
            }
            return cols;
        }

        public int sum() {
            int sum = 0;
            for (int i : a) sum += i;
            return sum;
        }

        public int weight() {
            int prev = -1, count = 0, weight = FACT[a.length];
            for (int col : a) {
                if (col == prev) weight /= ++count;
                else {
                    prev = col;
                    count = 1;
                }
            }
            return weight;
        }

        @Override public boolean equals(Object obj) {
            // Deliberately unsuitable for general-purpose use, but helps catch bugs faster.
            CanonicalMatrix that = (CanonicalMatrix)obj;
            for (int i = 0; i < a.length; i++) {
                if (a[i] != that.a[i]) return false;
            }
            return true;
        }

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

Farklı kaydet HankelCombinatorics.java, farklı derle javac HankelCombinatorics.java, farklı çalıştır java -Xmx2G HankelCombinatorics.

İle NUM_THREADS = 4zaman çekirdekli makinede bu alır 20420819767436için n=850 ila 55 saniye işlem arasındaki değişkenlik adil bir miktar ile, geçen olarak; Sekiz çekirdekli makinenizde aynı şeyi kolayca yönetmesini bekliyorum, ancak alınması bir saat veya daha fazla sürecek n=9.

Nasıl çalışır

Verilen n, 2^(2n-1)ikili nx nHankel matrisleri vardır. Satırlara n!ve sütunlara da izin verilebilir n!. Tek yapmamız gereken çift sayımdan kaçınmak ...

Her satırın toplamını hesaplarsanız, ne satırlara ne de sütunlara izin vermek toplamların çoklu kümesini değiştirmez. Örneğin

0 1 1 0 1
1 1 0 1 0
1 0 1 0 0
0 1 0 0 1
1 0 0 1 0

satır toplamı çoklu {3, 3, 2, 2, 2}kümesine sahiptir ve ondan türetilen tüm Hankelable matrisleri de öyle. Bu, Hankel matrislerini bu satır toplamı çoklu kümelerine göre gruplayabildiğimiz ve daha sonra her bir grubu bağımsız olarak işleyerek birden fazla işlemci çekirdeğinden yararlanabileceğimiz anlamına gelir.

Ayrıca sömürülebilir bir simetri vardır: sıfırdan daha fazla sıfır olan matrisler, sıfırdan daha fazla olan matrislerle birlikte.

Hankel matrisi zaman çift sayma meydana M_1satır permütasyon ile r_1ve sütun permütasyon c_1Hankel matris eşleşen M_2satır permütasyon ile r_2ve sütun permütasyon c_2(iki adede kadar olan, ancak hepsi değil, üç M_1 = M_2, r_1 = r_2, c_1 = c_2). Satır ve sütun permütasyon biz satır permütasyonu uygulamak eğer öyleyse, bağımsız r_1etmek M_1ve satır permütasyon r_2için M_2, sütunlar MULTISETS olarak eşit olmalıdır. Her grup için, gruptaki bir matrise bir satır permütasyonu uygulayarak elde edilen tüm sütun çoklu setlerini hesaplıyorum. Çoklu kümelerin standart bir gösterimini elde etmenin kolay yolu sütunları sıralamaktır ve bu bir sonraki adımda da yararlıdır.

Farklı sütun çoklu setlerini elde ettikten sonra n!, her birinin permütasyonlarının kaçının benzersiz olduğunu bulmamız gerekir . Bu noktada, çift sayma yalnızca belirli bir sütun çoklu kümesinin yinelenen sütunları varsa oluşabilir: yapmamız gereken, çoklu kümedeki her farklı sütunun yineleme sayısını saymak ve daha sonra karşılık gelen çok uluslu katsayıyı hesaplamaktır. Sütunlar sıralandığından, sayımı yapmak kolaydır.

Sonunda hepsini topladık.

Asimptotik karmaşıklık tam hassasiyetle hesaplamak için önemsiz değildir, çünkü setler hakkında bazı varsayımlar yapmamız gerekir. Her biri için zaman 2^(2n-2) n!ayırarak n^2 ln n(sıralama dahil) sütun çoklu kümelerinin sırasını değerlendiririz ; gruplama bir ln nfaktörden fazlasını almazsa , zaman karmaşıklığımız vardır Theta(4^n n! n^2 ln n). Fakat üstel faktörler polinom faktörlerine tamamen hakim olduğundan, öyle Theta(4^n n!) = Theta((4n/e)^n).


Bu çok etkileyici. Kullandığınız algoritma hakkında bir şeyler söyleyebilir misiniz?

3

Python2 / 3

Yavaş bir dilde oldukça naif bir yaklaşım:

import itertools

def permute_rows(m):
    for perm in itertools.permutations(m):
        yield perm

def permute_columns(m):
    T = zip(*m)
    for perm in itertools.permutations(T):
        yield zip(*perm)

N = 1
while True:
    base_template = ["abcdefghijklmnopqrstuvwxyz"[i:i+N] for i in range(N)]

    templates = set()
    for c in permute_rows(base_template):
        for m in permute_columns(c):
            templates.add("".join("".join(row) for row in m))

    def possibs(free, templates):
        if free == 2*N - 1:
            return set(int(t, 2) for t in templates)

        s = set()
        for b in "01":
            new_templates = set(t.replace("abcdefghijklmnopqrstuvwxyz"[free], b) for t in templates)
            s |= possibs(free + 1, new_templates)

        return s

    print(len(possibs(0, templates)))
    N += 1

Yazarak çalıştırın python script.py.


Python 2/3 olarak listelenen diliniz var, ancak Python 2'de çalışması için ihtiyacınız yok from __future__ import print_function(veya bunun gibi bir şey)?
Alex

2
@AlexA. Normalde, evet, ama bu durumda değil. Yazarken Python2'nin davranışını düşünün return(1). Şimdi :)returnprint
orlp

Güzel! Her gün yeni bir şey öğreniyorum. :)
Alex

2

Haskell

import Data.List
import Data.Hashable
import Control.Parallel.Strategies
import Control.Parallel
import qualified Data.HashSet as S

main = mapM putStrLn $ map (show.countHankellable) [1..]

a§b=[a!!i|i<-b]

hashNub :: (Hashable a, Eq a) => [a] -> [a]
hashNub l = go S.empty l
    where
      go _ []     = []
      go s (x:xs) = if x `S.member` s then go s xs
                                    else x : go (S.insert x s) xs

pmap = parMap rseq

makeMatrix :: Int->[Bool]->[[Bool]]
makeMatrix n vars = [vars§[i..i+n-1]|i<-[0..n-1]]

countHankellable :: Int -> Int
countHankellable n = let
    s = permutations [0..n-1]
    conjugates m = concat[permutations[r§q|r<-m]|q<-s]
    variableSets = sequence [[True,False]|x<-[0..2*(n-1)]]
 in
    length.hashNub.concat.pmap (conjugates.makeMatrix n ) $ variableSets

Peter kadar hızlı bir yerde değil - bu oldukça etkileyici bir kurulum! Şimdi internetten çok daha fazla kod kopyalandı. Kullanımı:

$ ghc -threaded hankell.hs
$ ./hankell

Haskell yanıtı her zaman beklerim. Teşekkür ederim.

@Lembik - benimki makinende nasıl?
alexander-brett
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.