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)
ไบนารีn
x n
Hankel เมทริกซ์ แถวสามารถเรียงสับเปลี่ยนใน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)