【问题标题】:Fastest way to permute bits in a Java array在 Java 数组中置换位的最快方法
【发布时间】:2015-04-22 23:39:06
【问题描述】:

随机(但重复)置换 Java 字节数组中所有位的最快方法是什么?我已经尝试使用 BitSet 成功地做到这一点,但是有更快的方法吗?显然 for 循环消耗了大部分的 cpu 时间。

我刚刚在我的 IDE 中进行了一些分析,for 循环占整个 permute() 方法中 CPU 时间的 64%。

为了澄清,数组(preRound)包含一个现有的数字数组,该数组将进入程序。我希望该数组的各个设置位以随机方式混合。这就是 P[] 的原因。它包含位位置的随机列表。因此,例如,如果 preRound 的第 13 位被设置,它将被转移到 postRound 的位置 P[13]。这可能位于 postRound 的位置 20555。整个事情是替换 - 置换网络的一部分,我正在寻找置换传入比特的最快方法。

到目前为止我的代码...

 private byte[] permute(byte[] preRound) {
    BitSet beforeBits = BitSet.valueOf(preRound);
    BitSet afterBits = new BitSet(blockSize * 8);


    for (int i = 0; i < blockSize * 8; i++) {
        assert i != P[i];


        if (beforeBits.get(i)) {
            afterBits.set(P[i]);
        }
    }


    byte[] postRound = afterBits.toByteArray();
    postRound = Arrays.copyOf(postRound, blockSize);      // Pad with 0s to the specified length
    assert postRound.length == blockSize;


    return postRound;
}

仅供参考,blockSize 约为 60,000,P 是随机查找表。

【问题讨论】:

  • 生成随机的 32 位整数,直到你有足够的能力随机设置每个字节。
  • 澄清一下,您想在permutation 的技术意义上进行置换吗?或者你只是想要数组中的基本随机字节?如果是前者,“随机”排列它们是什么意思?如果是后者,Random.nextBytes 是您要找的吗?
  • P 到底是什么?
  • 你的随机查找表是否等同于排列的数学概念,即存在某个数字 n,这样当 P 应用 n 次时,每个输入都将映射到自身?
  • 我认为 P 基本上是一个从 0 到 blocksize*8 的数字数组,没有以随机顺序重复。也就是说,它是一个域整数到同一范围内的另一个整数的双向无损映射。他显然还保证 P[i] 永远不会映射到自身。

标签: java arrays permutation


【解决方案1】:

我没有进行任何性能测试,但您可能需要考虑以下几点: 要省略对 Arrays.copyOf 的调用(它会复制内部使用的 long[] 的副本,这有点烦人),只需设置最后一位以防它之前未设置并在之后取消设置。

此外,还有一个很好的习惯用法来迭代输入排列中的设置位。

private byte[] permute(final byte[] preRound) {
    final BitSet beforeBits = BitSet.valueOf(preRound);
    final BitSet afterBits = new BitSet(blockSize*8);
    for (int i = beforeBits.nextSetBit(0); i >= 0; i =
            beforeBits.nextSetBit(i + 1)) {
        final int to = P[i];
        assert i != to;
        afterBits.set(to);
    }
    final int lastIndex = blockSize*8-1;
    if (afterBits.get(lastIndex)) {
        return afterBits.toByteArray();
    }
    afterBits.set(lastIndex);
    final byte[] postRound = afterBits.toByteArray();
    postRound[blockSize - 1] &= 0x7F;
    return postRound;
}

如果这不能解决问题,如果您在多次迭代中使用相同的 P,可能值得考虑将排列转换为循环符号并就地执行转换。 通过这种方式,您可以对 P 进行线性迭代,这可能使您能够更好地利用缓存(P 是字节数组的 32 倍,假设它是一个 int 数组)。 然而,您将失去只需要查看 1 并最终在字节数组中移动每个位的优势,无论是否设置。

如果你想避免使用 BitSet,你可以手动完成:

private byte[] permute(final byte[] preRound) {
    final byte[] result = new byte[blockSize];
    for (int i = 0; i < blockSize; i++) {
        final byte b = preRound[i];
        // if 1s are sparse, you may want to use this:
        // if ((byte) 0 == b) continue;
        for (int j = 0; j < 8; ++j) {
            if (0 != (b & (1 << j))) {
                final int loc = P[i * 8 + j];
                result[loc / 8] |= (1 << (loc % 8));
            }
        }
    }
    return result;
}

【讨论】:

  • 我刚刚用这段代码替换了我的代码,并在 NetBeans 中重新分析了它。新代码节省了 25% 的总运行时间。大部分时间在 .nextSetBit 和 .set 方法之间平均分配。
  • 最近又回到了这个。我可以避免位集的反复来回,而只是将输入转换为布尔数组吗?对布尔数组执行置换操作可能比转换位-> 长(位集中)更快,反之亦然..?
  • 我无法想象与直接处理 byte[] 相比,双重转换会给您带来加速。我在答案中添加了这样的解决方案。
猜你喜欢
  • 2012-11-17
  • 2023-03-28
  • 1970-01-01
  • 1970-01-01
  • 2012-10-27
  • 1970-01-01
  • 2016-09-19
  • 2013-07-17
  • 1970-01-01
相关资源
最近更新 更多