জাভা (এন = 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
আমার কোয়াড-কোর মেশিনে এটি পায় 20420819767436
জন্য n=8
50 55 সেকেন্ড অতিবাহিত, রান মধ্যে পরিবর্তনশীলতা ন্যায্য পরিমাণ সঙ্গে; আমি আশা করি এটি আপনার অক্টা-কোর মেশিনে সহজেই পরিচালনা করা উচিত তবে এটি পেতে এক ঘন্টা বা আরও বেশি সময় লাগবে n=9
।
কিভাবে এটা কাজ করে
দেওয়া আছে n
, 2^(2n-1)
বাইনারি n
এক্স n
হান্কেল ম্যাট্রিক্স রয়েছে। সারিগুলি 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}
এবং তাই এ থেকে প্রাপ্ত সমস্ত হ্যাঙ্কেলেবল ম্যাট্রিকেসও রয়েছে। এর অর্থ হ'ল আমরা হ্যান্কেল ম্যাট্রিককে এই সারির যোগফলগুলি বহু গোষ্ঠীগুলির দ্বারা গ্রুপ করতে পারি এবং তারপরে একাধিক প্রসেসরের কোরগুলি শোষণ করে প্রতিটি গ্রুপকে স্বাধীনভাবে পরিচালনা করতে পারি।
একটি শোষণীয় প্রতিসাম্যতাও রয়েছে: শূন্যের চেয়ে বেশি শূন্যের সাথে ম্যাট্রিকগুলি মূত্রের চেয়ে শূন্যের চেয়ে বেশি ম্যাট্রিকগুলির সাথে মায়ার করা হয়।
ডাবল কাউন্টিং ঘটে যখন 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
, কলাম multisets যেমন সমান হতে হবে। সুতরাং প্রতিটি গ্রুপের জন্য, আমি গ্রুপে একটি ম্যাট্রিক্সে একটি সারি ক্রমায়ন প্রয়োগ করে প্রাপ্ত সমস্ত কলাম মাল্টিসেটগুলি গণনা করি। মাল্টিসেটগুলির একটি প্রাসঙ্গিক উপস্থাপনা পাওয়ার সহজ উপায় হ'ল কলামগুলি বাছাই করা এবং এটি পরবর্তী পদক্ষেপেও কার্যকর।
স্বতন্ত্র কলামের মাল্টিলেটগুলি পেয়ে, আমাদের খুঁজে বের করতে হবে যে 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)
।