简体   繁体   English

BigUint 二进制补码

[英]BigUint binary complement

Example code : 示例代码

use num_bigint::BigUint;
use num_traits::identities::One;

fn main() {
    // Example: 10001 (17) => 1110 (14)
    let n = BigUint::from(17u32);
    println!("{}", n);
    // BigUint doesn't support `!`
    //let n = !n;
    let mask = (BigUint::one() << n.bits()) - 1u32;
    let n = n ^ mask;
    println!("{}", n);
}

The above code is doing a binary complement of a BigUint using a bit mask.上面的代码使用位掩码对BigUint进行二进制补码。 Questions:问题:

  1. Is there a better way to do the binary complement than with a mask?有没有比使用掩码更好的方法来进行二进制补码? It seems BigUint doesn't include the !似乎BigUint不包括! operator (but the mask may be necessary anyway depending on how ! was defined).运算符(但根据!的定义方式,掩码可能仍然是必需的)。
  2. If not is there a better way to generate the mask?如果没有,是否有更好的方法来生成掩码? (Caching masks helps, but can use lots of memory.) (缓存掩码有帮助,但可以使用大量 memory。)

More context with the problem I'm actually looking at: binary complement sequences有关我实际正在查看的问题的更多背景信息: 二进制补码序列

If you alternate multiplying by 3 and bit flipping a number some interesting sequences arise.如果您交替乘以 3 和位翻转数字,则会出现一些有趣的序列。 Example starting with 3:以 3 开头的示例:

0. 3 (11b)  => 3*3 = 9 (1001b) => bit complement is 6 (0110b)
1. 6 (110b)
2. 13 (1101b)
3. 24 (11000b)
4. 55 (110111b)
5. 90 (1011010b)
6. 241 (11110001b)
7. 300 (100101100b)
8. 123 (1111011b)
9. 142 (10001110b)
11. 85 (1010101b)
12. 0 (0b)

One question is whether it reaches zero for all starting numbers or not.一个问题是它是否对所有起始数字都达到零。 Some meander around for quite a while before reaching zero (425720 takes 87,037,147,316 iterations to reach 0).有些在达到零之前徘徊了很长一段时间(425720 需要 87,037,147,316 次迭代才能达到 0)。 Being able to compute this efficiently can help in answering these questions.能够有效地计算这一点可以帮助回答这些问题。 Mostly I'm learning a bit more rust with this though.大多数情况下,我通过这个学习了更多 rust 。

The best solution is probably to just do it yourself.最好的解决方案可能就是自己做。 You perform an allocation each time you create a BigUint which really slows down your program.每次创建BigUint时都会执行分配,这确实会减慢程序的速度。 Since we are not doing complex math, we can simplify most of this to a couple bitwise operations.由于我们不做复杂的数学运算,我们可以将其中的大部分简化为几个按位运算。

After a little bit of tinkering, here is how I implemented it.经过一些修补,这就是我实现它的方式。 For convenience, I used the unstable nightly feature bigint_helper_methods to allow for the carrying_add function. This helped simplify the addition process.为了方便起见,我使用了不稳定的夜间功能bigint_helper_methods来允许carrying_add function。这有助于简化加法过程。

#[derive(Debug)]
pub struct BigUintHelper {
    words: Vec<u64>,
}

impl BigUintHelper {
    pub fn mul3_invert(&mut self) {
        let len = self.words.len();

        // Multiply everything by 3 by adding it to itself with a bit shift
        let mut carry = false;
        let mut prev_bit = 0;
        for word in &mut self.words[..len - 1] {
            let previous = *word;

            // Perform the addition operation
            let (next, next_carry) = previous.carrying_add((previous << 1) | prev_bit, carry);

            // Reset carried values for next round
            prev_bit = previous >> (u64::BITS - 1);
            carry = next_carry;

            // Invert the result as we go to avoid needing another pass
            *word = !next;
        }

        // Perform the last word seperatly since we may need to do the invert differently
        let previous = self.words[len - 1];
        let (next, next_carry) = previous.carrying_add((previous << 1) | prev_bit, carry);


        // Extra word from the combination of the carry bits
        match next_carry as u64 + (previous >> (u64::BITS - 1)) {
            0 => {
                // The carry was 0 so we do the normal process
                self.words[len - 1] = invert_bits(next);
                self.cleanup_end();
            }
            1 => {
                self.words[len - 1] = !next;
                // invert_bits(1) = 0
                self.cleanup_end();
            }
            2 => {
                self.words[len - 1] = !next;
                // invert_bits(2) = 1
                self.words.push(1);
            }
            _ => unreachable!(),
        }
    }

    /// Remove any high order words without any bits
    #[inline(always)]
    fn cleanup_end(&mut self) {
        while let Some(x) = self.words.pop() {
            if x != 0 {
                self.words.push(x);
                break;
            }
        }
    }

    /// Count how many rounds it takes to convert this value to 0.
    pub fn count_rounds(&mut self) -> u64 {
        let mut rounds = 0;

        while !self.words.is_empty() {
            self.mul3_invert();
            rounds += 1;
        }

        rounds
    }
}


impl From<u64> for BigUintHelper {
    fn from(x: u64) -> Self {
        BigUintHelper {
            words: vec![x],
        }
    }
}

#[inline(always)]
const fn invert_bits(x: u64) -> u64 {
    match x.leading_zeros() {
        0 => !x,
        y => ((1u64 << (u64::BITS - y)) - 1) ^ x
    }
}

Rust Playground Rust 游乐场

If you are looking for performance, num-bigint probably isn't the best choice .如果您正在寻找性能, num-bigint可能不是最佳选择 Everything that is really high-performance, though, seems to be GPL licensed.不过,所有真正高性能的东西似乎都获得了 GPL 许可。

Either way, here is a solution using the rug library, which directly supports !不管怎样,这里有一个使用rug库的解决方案,它直接支持! (not), and seems to be really fast: (不是),而且似乎真的很快:

use rug::{ops::NotAssign, Integer};

fn main() {
    // Example: 10001 (17) => 1110 (14)
    let mut n = Integer::from(17u32);
    println!("{}", n);
    n.not_assign();
    n.keep_bits_mut(n.significant_bits() - 1);
    println!("{}", n);
}
17
14

Note that not_assign also inverts the sign bit.请注意, not_assign还会反转符号位。 We can remove that bit through the keep_bits_mut function.我们可以通过keep_bits_mut function 删除该位。


For example, here is a version of your algorithm:例如,这是您的算法的一个版本:

use rug::{ops::NotAssign, Integer};

fn step(n: &mut Integer) {
    *n *= 3;
    n.not_assign();
    n.keep_bits_mut(n.significant_bits() - 1);
}

fn main() {
    let mut n = Integer::from(3);
    println!("{}", n);
    while n != 0 {
        step(&mut n);
        println!("{}", n);
    }
}
3
6
13
24
55
90
241
300
123
142
85
0

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM