Đếm số lượng ma trận Hankelable


12

Lý lịch

Ma trận Hankel nhị phân là một ma trận có các đường chéo không đổi (các đường chéo dốc dương) chỉ chứa 0s và 1s. Ví dụ: ma trận Hankel nhị phân 5x5 trông giống như

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

nơi a, b, c, d, e, f, g, h, ilà một trong hai 0hoặc 1.

Hãy xác định ma trận MHankelable nếu có hoán vị thứ tự các hàng và cột của M để M là ma trận Hankel. Điều này có nghĩa là người ta có thể áp dụng một hoán vị cho thứ tự của các hàng và một hoán vị có thể khác nhau cho các cột.

Các thách thức

Thách thức là tính xem có bao nhiêu Hankelable n bằng nma trận cho tất cả ncó giá trị càng lớn càng tốt.

Đầu ra

Đối với mỗi số nguyên n từ 1 trở lên, hãy xuất số lượng Hankelable n theo nma trận với các mục là 0hoặc 1.

Đối với n = 1,2,3,4,5câu trả lời nên được 2,12,230,12076,1446672. (Nhờ orlp cho mã để tạo ra những thứ này.)

Thời gian giới hạn

Tôi sẽ chạy mã của bạn trên máy của tôi và dừng nó sau 1 phút. Mã xuất ra các câu trả lời đúng lên đến giá trị lớn nhất của n thắng. Giới hạn thời gian dành cho tất cả mọi thứ từ n = 1giá trị lớn nhất nmà bạn đưa ra câu trả lời.

Người chiến thắng sẽ là câu trả lời hay nhất vào cuối ngày thứ Bảy 18 tháng Tư.

Tie Breaker

Trong trường hợp hòa nhau tối đa, ntôi sẽ tính thời gian cần bao lâu để đưa ra kết quả đầu ra n+1và nhanh nhất sẽ thắng. Trong trường hợp họ chạy cùng một lúc trong vòng một giây n+1, lần gửi đầu tiên sẽ thắng.

Ngôn ngữ và thư viện

Bạn có thể sử dụng bất kỳ ngôn ngữ nào có trình biên dịch / trình thông dịch / có sẵn miễn phí. cho Linux và bất kỳ thư viện nào cũng có sẵn miễn phí cho Linux.

Máy của tôi

Thời gian sẽ được chạy trên máy của tôi. Đây là bản cài đặt Ubuntu tiêu chuẩn trên Bộ xử lý tám lõi AMD FX-8350 trên Bo mạch chủ Asus M5A78L-M / USB3 (Ổ cắm AM3 +, DDR3 8GB). Điều này cũng có nghĩa là tôi cần để có thể chạy mã của bạn. Do đó, chỉ sử dụng phần mềm miễn phí có sẵn dễ dàng và vui lòng bao gồm các hướng dẫn đầy đủ về cách biên dịch và chạy mã của bạn.

Ghi chú

Tôi khuyên bạn không nên lặp đi lặp lại trên tất cả n ma trận và cố gắng phát hiện xem mỗi người có thuộc tính mà tôi mô tả hay không. Thứ nhất, có quá nhiều và thứ hai, dường như không có cách nào nhanh chóng để thực hiện phát hiện này .

Các mục hàng đầu cho đến nay

  • n = 8 của Peter Taylor. Java
  • n = 5 bởi orlp. Con trăn

4
Tôi thực sự thích từ "Hankelable."
Alex A.

3
Đối với n=6tổng số là 260357434. Tôi nghĩ rằng áp lực bộ nhớ là một vấn đề lớn hơn thời gian CPU.
Peter Taylor

Đây là một câu hỏi tuyệt vời. Tôi đã được triệt để mọt sách.
alexander-brett

Câu trả lời:


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

Lưu thành HankelCombinatorics.java , biên dịch thành javac HankelCombinatorics.java, chạy như java -Xmx2G HankelCombinatorics.

Với NUM_THREADS = 4máy lõi tứ của tôi, nó được20420819767436 cho n=8vào từ 50 đến 55 giây trôi qua, với một số lượng hợp lý của sự biến đổi giữa chạy; Tôi hy vọng rằng nó sẽ dễ dàng quản lý tương tự trên máy octa-core của bạn nhưng sẽ mất một giờ hoặc hơn để có được n=9.

Làm thế nào nó hoạt động

Cho n, có ma trận x Hankel 2^(2n-1)nhị phân . Các hàng có thể được hoán vị trongnnn! cách và các cột theo n!cách. Tất cả những gì chúng ta cần làm là tránh tính hai lần ...

Nếu bạn tính tổng của mỗi hàng, thì việc hoán vị các hàng cũng không cho phép các cột sẽ thay đổi nhiều tổng. Ví dụ

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

có nhiều hàng tổng {3, 3, 2, 2, 2} , và tất cả các ma trận Hankelable có nguồn gốc từ nó. Điều này có nghĩa là chúng ta có thể nhóm các ma trận Hankel theo các đa tổng hàng này và sau đó xử lý từng nhóm một cách độc lập, khai thác nhiều lõi xử lý.

Ngoài ra còn có một đối xứng có thể khai thác: các ma trận có nhiều số 0 hơn so với các ma trận với các ma trận có nhiều số hơn các số không.

Double-tính toán xảy ra khi Hankel ma trận M_1với hoán vị hàng r_1và cột hoán vị c_1phù hợp với ma trận Hankel M_2với hoán vị hàng r_2và cột hoán vị c_2(lên đến hai nhưng không phải tất cả ba M_1 = M_2, r_1 = r_2, c_1 = c_2). Các hoán vị hàng và cột là độc lập, vì vậy nếu chúng ta áp dụng hoán vị hàng r_1cho M_1và hoán vị hàng r_2cho M_2, các cột dưới dạng nhiều trang phải bằng nhau. Vì vậy, đối với mỗi nhóm, tôi tính toán tất cả các đa cột có được bằng cách áp dụng hoán vị hàng cho một ma trận trong nhóm. Cách dễ dàng để có được một biểu diễn chính tắc của nhiều trang là sắp xếp các cột và điều đó cũng hữu ích trong bước tiếp theo.

Có được nhiều cột khác nhau, chúng ta cần tìm xem có bao nhiêu n!hoán vị của mỗi cột là duy nhất. Tại thời điểm này, việc đếm kép chỉ có thể xảy ra nếu một multiset cột đã cho có các cột trùng lặp: điều chúng ta cần làm là đếm số lần xuất hiện của mỗi cột riêng biệt trong multiset và sau đó tính hệ số đa cực tương ứng. Vì các cột được sắp xếp, thật dễ dàng để đếm.

Cuối cùng, chúng tôi thêm tất cả chúng lên.

Độ phức tạp tiệm cận không phải là nhỏ để tính chính xác hoàn toàn, bởi vì chúng ta cần đưa ra một số giả định về các bộ. Chúng tôi đánh giá theo thứ tự của nhiều 2^(2n-2) n!cột, dành n^2 ln nthời gian cho từng cột (bao gồm cả việc sắp xếp); nếu việc phân nhóm không chiếm nhiều hơn một ln nyếu tố, chúng ta có sự phức tạp về thời gian Theta(4^n n! n^2 ln n). Nhưng vì các yếu tố theo cấp số nhân hoàn toàn thống trị các đa thức, nên nó Theta(4^n n!) = Theta((4n/e)^n).


Điều này rất ấn tượng. Bạn có thể nói điều gì về thuật toán bạn đã sử dụng?

3

Python2 / 3

Cách tiếp cận khá ngây thơ, trong một ngôn ngữ chậ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

Chạy bằng cách gõ python script.py.


Bạn có ngôn ngữ được liệt kê là Python 2/3, nhưng để nó hoạt động trong Python 2, bạn không cần from __future__ import print_function(hoặc một cái gì đó tương tự)?
Alex A.

2
@AlexA. Thông thường, có, nhưng không phải trong trường hợp này. Hãy xem xét hành vi của Python2 khi bạn gõ return(1). Bây giờ thay thế returnbằng print:)
orlp

Mát mẻ! Tôi học được điều gì đó mới mỗi ngày. :)
Alex A.

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

Không nơi nào nhanh như Peter - đó là một thiết lập khá ấn tượng mà anh ấy đã có! Bây giờ với nhiều mã được sao chép từ internet. Sử dụng:

$ ghc -threaded hankell.hs
$ ./hankell

Một câu trả lời Haskell luôn được chào đón. Cảm ơn bạn.

@Lembik - Máy của tôi hoạt động thế nào?
alexander-brett
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.