【问题标题】:How can i use a unsigned 64 bits variables in Bits Operations in Clojure?如何在 Clojure 的位操作中使用无符号 64 位变量?
【发布时间】:2015-02-28 20:16:12
【问题描述】:

我有以下代码:

(defn BitScanReverse [^Long bit-board]
  (loop [value bit-board r 0]
    (cond
      (> value 0x00000000FFFFFFFF) (recur (unsigned-bit-shift-right value 32) (+ r 32))
      (> value 0x000000000000FFFF) (recur (unsigned-bit-shift-right value 16) (+ r 16))
      (> value 0x00000000000000FF) (recur (unsigned-bit-shift-right value 8) (+ r 8))
      (> value 0x000000000000000F) (recur (unsigned-bit-shift-right value 4) (+ r 4))
      (> value 0x0000000000000003) (recur (unsigned-bit-shift-right value 2) (+ r 2))
      (> value 0x0000000000000001) (recur (unsigned-bit-shift-right value 1) (+ r 1))
      :else r)))

它返回在位板上找到的最后一位的索引。 问题是当我尝试运行时: (BitScanReverse 18446462598732840960) ;;期望 63。 它给了我: IllegalArgumentException Value out of range for long: 18446462598732840960 clojure.lang.RT.longCast (RT.java:1134)

这个位板是黑棋的初始位置。问题是 long 是在 clojure 中签名的(也在 java 中)。我尝试使用 BigInt,但它不允许位操作。

有什么建议吗?

【问题讨论】:

  • 你看过BitSet吗?
  • 如果对您有帮助,请接受一个解决方案!

标签: java clojure chess bitboard


【解决方案1】:

这是一个使用位测试且不循环的反向扫描的快速且非常肮脏的实现,它可能更有效,也可能不会更有效。

(defn rev-test [^long n ^long x] (bit-test x n))
(defn BitScanReverse [^long bit-board](condp rev-test bit-board
0 0,1 1,2 2,3 3,4 4,5 5,6 6,7 7,8 8,9 9,10 10,11 11,12 12,13 13,14 14,15 15,16 16,17 17,18 18,19 19,20 20,21 21,22 22,23 23,24 24,25 25,26 26,27 27,28 28,29 29,30 30,31 31,32 32,33 33,34 34,35 35,36 36,37 37,38 38,39 39,40 40,41 41,42 42,43 43,44 44,45 45,46 46,47 47,48 48,49 49,50 50,51 51,52 52,53 53,54 54,55 55,56 56,57 57,58 58,59 59,60 60,61 61,62 62,63 63))

这将最低有效位视为 0,就像 bit-test 和 0 索引数组一样,所以我认为它与您的实现不同。在产生输入时,您将被限制为带有正符号文字的 63 位,但您仍然可以将符号位用作第 64 位。尝试制作一个辅助方法来构造您需要的具有稍高抽象级别的数字,例如 fn 将最高有效 32 位和最低有效 32 位作为两个参数。这可能会写成一个宏,但我没有足够的经验来写一个并确保它会工作。

(defn bitboard [^long upper ^long lower]
    (bit-or (bit-shift-left upper 32)
            (bit-and lower 0xffffffff)))

对于性能很重要,^Long 是装箱的,我认为 ^long 在适当的情况下可能不会装箱。数字基元数组是我发现的少数情况之一,其中基元肯定是它们在 JVM 上应该是的(字节数组将始终是字节数组,具有连续的 8 位内存,但声明了一个字节就其本身而言,即使在 Java 中,由于对齐优化,可能会占用超过 8 位的内存)。我强烈推荐 ztellman 的 primitive-math 库来查找 Clojure 中数学需要反射的情况,这非常常见,而且对于像这样的位旋转代码非常重要。

【讨论】:

  • 感谢您的回答。我发现这个 BitScanReverse 非常有趣,我不知道 condp。但我正在考虑使用 java.util.bitset 而不是 ^long 或 biginteger。
【解决方案2】:

按照@assylias 的建议使用Java BitSet,给出了一个简单的解决方案:

(import 'java.util.BitSet)

(defn bit-scan-reverse [^long bit-board]
  (let [board (BitSet/valueOf (long-array [bit-board]))]
    (.previousSetBit board 63)))

编辑 上述解决方案不起作用,因为 long 已签名。继续搜索BitSet,我想出了:

(defn bit-scan-reverse2 [bit-board]
  (let [board (-> (biginteger bit-board) ; coerce to BigInteger
                  .toByteArray           ; as byte[] big-endian
                  reverse                ; to little-endian
                  byte-array             
                  BitSet/valueOf)
        max-index (.size board)]
    (.previousSetBit board max-index)))

效果很好,但看起来很复杂。然后查看BigInteger doc,我发现bitLength() 方法实际上回答了这个问题:

bitLength():返回此 BigInteger 的最小二进制补码表示中的位数,不包括符号位。

由于我们只关心板表示的正数,因此可以使用此bitLength() 方法来查找 64 位板中最左边的位集:

(defn bit-scan-reverse3 [bit-board]
  (-> bit-board
      biginteger
      .bitLength
      dec))

(map bit-scan-reverse3 '(0xFFFF000000000000 0x0 0x1 0xF 0xFFF))
user> 63 -1 0 3 11

** 结束编辑**

在性能方面,我测试了 3 个版本,它为这个快速工作台提供了非常相似的时间(~10ns)。 BitScanReverse2 是@notostraca 提供的解决方案:

(require '[clojure.data.generators :as gen])
(require '[criterium.core :as c])

(let [bunch-of-longs (doall (take 10000 (filter (partial < 0) 
                                                (repeatedly gen/long))))]
  (c/quick-bench (map BitScanReverse bunch-of-longs))
  (c/quick-bench (map BitScanReverse2 bunch-of-longs))
  (c/quick-bench (map bit-scan-reverse bunch-of-longs))
  (c/quick-bench (map bit-scan-reverse2 bunch-of-longs))
  (c/quick-bench (map bit-scan-reverse3 bunch-of-longs)))

【讨论】:

  • 我意识到我没有回答这个问题。我做了一些搜索,最后找到了一个非常简单的解决方案。请参阅上面的编辑。
  • 感谢您的回答。是的,BitSet 几乎拥有我使用位操作所需的一切。
猜你喜欢
  • 1970-01-01
  • 2014-10-03
  • 2016-05-08
  • 2020-10-30
  • 2011-01-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-28
相关资源
最近更新 更多