我如何简洁地表示1024的1024的排列?

时间:2013-09-12 07:09:26

标签: math compression permutation voting largenumber

我想给我的用户一个简洁的,基本的64字母数字代码,以表示他们在从1024个候选人的选票中按顺序选择1024个候选人时所做的选择。 (这是最糟糕的情况......我可以和<256)一起生活。

我有什么选择?

一种天真的方法告诉我,如果1024个元素中的每一个都可以用唯一的序数(2 ^ 10)表示,并且我需要一系列1024个序数,那么10位×1024位= 10,240位将会特技。但那仍然是1707基数-64位数,这比我希望的要长一点,感觉就像我浪费9位代表'1'(虽然我可能错了)。

经典置换理论应该告诉我可能性的数量 - nPr(顺序很重要,没有重复)。但这个数字是如此之大,它困扰着我的小脑,并压倒了我的dec&lt; - &gt; bin计算器。如果我这样做,我会用更少的比特逃脱吗? (为什么不呢?数学糟透了?)

Java代码的加分点,所以我可以修改n和r来查找所需的位数和基数-64位数。 : - )

PS。这是我对投票系统的认真建议的可行性测试,投票系统使用纸张进行审计跟踪,计算机用于快速计算。

1 个答案:

答案 0 :(得分:3)

你需要的Wolfram alpha will tell you⌈log 2 (1024!)⌉=最佳表示中的8,770位。没有比天真存储元素本身好多少。实现这种稍微更简洁的表示的概念上最简单的方法是想象所有那些按字典顺序排列的排列。然后,您只需将排列的索引存储在该列表中即可。为了实现这一点,您可以迭代排列的元素,并在每个位置询问自己有多少排列具有所有先前位置的共同点,但在当前位置的值较小。对这些数字求和将导致置换的从零开始的索引。

由于您询问了Java代码,因此这里有一段代码可以确定所需的位数,还可以计算给定排列的简洁表示。

import java.math.BigInteger;
class SO18757672 {
    int n;
    BigInteger[] fac;
    SO18757672(int n) {
        this.n = n;
        fac = new BigInteger[n + 1];
        fac[0] = BigInteger.ONE;
        for (int i = 1; i <= n; ++i)
            fac[i] = fac[i - 1].multiply(BigInteger.valueOf(i));
    }
    int bitsRequired() {
        return fac[n].subtract(BigInteger.ONE).bitLength();
    }
    BigInteger codeFor(int[] permutation) {
        if (permutation.length != n)
            throw new IllegalArgumentException("wrong length");
        BigInteger res = BigInteger.ZERO;
        for (int i = 0; i != n; ++i) {
            int pi = permutation[i];
            if (pi < 0 || pi > n - i)
                throw new IllegalArgumentException("invalid value");
            res = res.add(fac[n - 1 - i].multiply(BigInteger.valueOf(pi)));
            for (int j = i + 1; j != n; ++j) {
                if (permutation[j] == pi)
                    throw new IllegalArgumentException("duplicate value");
                if (permutation[j] > pi)
                    --permutation[j]; // We modify out input!
            }
        }
        return res;
    }
    boolean sanityChecks() {
        int[] p = new int[n];
        // smallest code is zero, for all elements in order
        for (int i = 0; i != n; ++i)
            p[i] = i;
        assert BigInteger.ZERO.equals(codeFor(p));
        // largest code is n!-1, for all elements in reverse order
        for (int i = 0; i != n; ++i)
            p[i] = n - 1 - i;
        assert fac[n].subtract(BigInteger.ONE).equals(codeFor(p));
        return true; // so we can use this in an assert call
    }
    public static void main(String[] args) {
        SO18757672 instance = new SO18757672(1024);
        System.out.println(instance.bitsRequired() + " bits required");
        assert instance.sanityChecks();
    }
}