นับจำนวนเมทริกซ์ Hankelable


12

พื้นหลัง

เมทริกซ์ Hankelไบนารีคือเมทริกซ์ที่มีค่าคงตัวเอียง (diagonals ลาดเอียง) คงที่ที่มีเพียง0s และ1s เท่านั้น เช่นเมทริกซ์ Hankel ขนาด 5x5 ดูเหมือนว่า

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

ที่a, b, c, d, e, f, g, h, iมีทั้งหรือ01

ลองกำหนดเมทริกซ์Mเป็นHankelableถ้ามีการเรียงลำดับของแถวและคอลัมน์ของMเพื่อให้Mเป็นเมทริกซ์ Hankel นี่หมายความว่าเราสามารถใช้การเรียงสับเปลี่ยนหนึ่งครั้งกับลำดับของแถวและอีกอันหนึ่งอาจแตกต่างกับคอลัมน์

ความท้าทาย

ความท้าทายคือการนับจำนวนHankelable nโดยnเมทริกซ์ที่มีให้nมากถึงค่ามากที่สุด

เอาท์พุต

สำหรับแต่ละจำนวนเต็ม n ตั้งแต่วันที่ 1 ขึ้นไปการส่งออกจำนวนHankelable nโดยnการฝึกอบรมที่มีรายการที่มีหรือ01

สำหรับคำตอบที่ควรจะเป็นn = 1,2,3,4,5 2,12,230,12076,1446672(ขอบคุณ orlp สำหรับรหัสในการสร้างสิ่งเหล่านี้)

เวลาที่ จำกัด

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

ผู้ชนะจะได้รับคำตอบที่ดีที่สุดในวันเสาร์ที่ 18 เมษายน

Tie Breaker

ในกรณีที่มีการเสมอกันมากที่สุดnฉันจะใช้เวลานานเท่าไรกว่าจะได้ผลลัพธ์n+1และชนะเร็วที่สุด ในกรณีที่พวกเขาทำงานในเวลาเดียวกันภายในไม่เกินวินาทีถึงn+1การส่งครั้งแรกชนะ

ภาษาและห้องสมุด

คุณสามารถใช้ภาษาใดก็ได้ที่มีคอมไพเลอร์ / ล่าม / อื่น ๆ สำหรับ Linux และไลบรารี่ใด ๆ ที่มีให้ใช้งานบน Linux ได้อย่างอิสระ

เครื่องของฉัน

เวลาจะทำงานบนเครื่องของฉัน นี่คือการติดตั้งอูบุนตูมาตรฐานบนโปรเซสเซอร์ AMD FX-8350 Eight-Core บนเมนบอร์ด Asus M5A78L-M / USB3 (Socket AM3 +, 8GB DDR3) นี่ก็หมายความว่าฉันต้องสามารถเรียกใช้รหัสของคุณได้ ดังนั้นโปรดใช้ซอฟต์แวร์ฟรีที่มีให้ง่ายและโปรดรวมคำแนะนำทั้งหมดในการรวบรวมและเรียกใช้รหัสของคุณ

หมายเหตุ

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

รายการที่นำจนถึง

  • n = 8 โดย Peter Taylor ชวา
  • n = 5 โดย orlp หลาม

4
ฉันสนุกกับคำว่า "Hankelable"
Alex A.

3
สำหรับการรวมเป็นn=6 260357434ฉันคิดว่าความดันหน่วยความจำเป็นปัญหาใหญ่กว่าเวลา CPU
Peter Taylor

นี่เป็นคำถามที่ยอดเยี่ยม ฉันถูกเนิร์ดอย่างถี่ถ้วน
alexander-brett

คำตอบ:


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

บันทึกเป็นHankelCombinatorics.javaรวบรวมเป็นjavac HankelCombinatorics.java, java -Xmx2G HankelCombinatoricsการทำงานเป็น

กับNUM_THREADS = 4บนเครื่อง quad-core ของฉันจะได้รับ20420819767436สำหรับn=8ใน 50-55 วินาทีที่ผ่านไปด้วยจำนวนเงินที่ยุติธรรมของความแปรปรวนระหว่างวิ่ง; ผมคาดหวังว่ามันได้อย่างง่ายดายควรจัดการเดียวกันบนเครื่อง octa-core ของคุณ n=9แต่จะใช้เวลาหนึ่งชั่วโมงหรือมากกว่าที่จะได้รับ

มันทำงานอย่างไร

ป.ร. ให้nมี2^(2n-1)ไบนารีnx nHankel เมทริกซ์ แถวสามารถเรียงสับเปลี่ยนในn!รูปแบบและคอลัมน์ในn!รูปแบบ สิ่งที่เราต้องทำคือหลีกเลี่ยงการนับซ้ำ ...

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

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

มีผลรวมของแถวผลรวม{3, 3, 2, 2, 2}, และทำเมทริกซ์ Hankelable ทั้งหมดที่ได้จากมัน ซึ่งหมายความว่าเราสามารถจัดกลุ่มเมทริกซ์ Hankel ด้วยชุดผลรวมแถวเหล่านี้จากนั้นจัดการแต่ละกลุ่มอย่างอิสระโดยใช้ประโยชน์จากแกนประมวลผลหลายแกน

นอกจากนี้ยังมีสมมาตรที่ใช้ประโยชน์ได้: เมทริกซ์ที่มีเลขศูนย์มากกว่าที่อยู่ใน bijection กับเมทริกซ์ที่มีมากกว่าศูนย์

การนับซ้ำเกิดขึ้นเมื่อ Hankel เมทริกซ์M_1ที่มีการเปลี่ยนแปลงแถวr_1และการเปลี่ยนแปลงคอลัมน์c_1ตรง Hankel เมทริกซ์M_2ที่มีการเปลี่ยนแปลงแถวr_2และคอลัมน์การเปลี่ยนแปลงc_2(มีถึงสอง แต่ไม่ทั้งหมดสามM_1 = M_2, r_1 = r_2, c_1 = c_2) การเรียงสับเปลี่ยนแถวและคอลัมน์มีความเป็นอิสระดังนั้นหากเราใช้การเรียงสับเปลี่ยนแถวr_1กับM_1และเรียงสับเปลี่ยนแถวr_2กับM_2คอลัมน์เป็นชุดย่อยจะต้องเท่ากัน ดังนั้นสำหรับแต่ละกลุ่มฉันคำนวณคอลัมน์หลายชุดทั้งหมดที่ได้รับโดยใช้การเปลี่ยนแถวเป็นเมทริกซ์ในกลุ่ม วิธีง่าย ๆ ในการรับการแทนค่าแบบบัญญัติของบัญญัติคือการจัดเรียงคอลัมน์และเป็นประโยชน์ในขั้นตอนถัดไป

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

ในที่สุดเราก็รวมมันเข้าด้วยกัน

ความซับซ้อนเชิงซีกไม่ได้เป็นเพียงการคำนวณอย่างแม่นยำเพราะเราจำเป็นต้องตั้งสมมติฐานบางอย่างเกี่ยวกับฉาก เราประเมินตามลำดับของชุดโฆษณาแบบ2^(2n-2) n!คอลัมน์ใช้n^2 ln nเวลาสำหรับแต่ละรายการ (รวมถึงการเรียงลำดับ) ถ้าจัดกลุ่มไม่ใช้เวลานานกว่าหนึ่งปัจจัยที่เรามีความซับซ้อนเวลาln n แต่เนื่องจากปัจจัยชี้แจงสมบูรณ์ครองคนพหุนามก็Theta(4^n n! n^2 ln n)Theta(4^n n!) = Theta((4n/e)^n)


มันน่าประทับใจมาก คุณสามารถพูดบางอย่างเกี่ยวกับอัลกอริทึมที่คุณใช้หรือไม่

3

Python2 / 3

วิธีการที่ไร้เดียงสาสวยในภาษาช้า:

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

python script.pyดำเนินการโดยการพิมพ์


คุณมีภาษาที่ระบุว่าเป็น Python 2/3 แต่เพื่อให้สามารถใช้งานได้ใน Python 2 คุณไม่ต้องการfrom __future__ import print_function(หรืออะไรทำนองนั้น)?
Alex A.

2
@AlexA โดยปกติใช่ แต่ไม่ใช่ในกรณีนี้ พิจารณาพฤติกรรม Python2 return(1)เมื่อคุณพิมพ์ ตอนนี้แทนที่returnด้วยprint:)
orlp

เย็น! ฉันเรียนรู้สิ่งใหม่ทุกวัน :)
อเล็กซ์ 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

ไม่มีที่ไหนใกล้เร็วเท่ากับปีเตอร์ - นั่นเป็นฉากที่น่าประทับใจมากที่เขาไปถึงที่นั่น! ขณะนี้มีการคัดลอกโค้ดอีกมากมายจากอินเทอร์เน็ต การใช้งาน:

$ ghc -threaded hankell.hs
$ ./hankell

ยินดีต้อนรับคำตอบของ Haskell ขอบคุณ.

@ Lembik - ฉันเป็นอย่างไรทำบนเครื่องของคุณ?
alexander-brett
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.