java 给定inDigits的基址取反

cuxqih21  于 2023-02-02  发布在  Java
关注(0)|答案(3)|浏览(96)

我有以下问题:给定一个以base为单位的输入(输入是以该base中的数字数组的形式给出的),将数字的取反写成"base's"-outDigits中的补码表示法。
数的"底数补码"表示法是"二进制补码"的推广:如果我们将(-x)作为一个无符号数处理,并将它加到x上,我们应该得到0(modulo base ^digit-size)。我不能调用其他函数(甚至Math. pow)
我的测试一直出错。我的代码:

public static void baseNegate(int base, int[] inDigits, int[] outDigits) {
        outDigits[0] = 1;
        for (int i = outDigits.length - 1; i >= 0; i--) {
            outDigits[i] += base - (1 + inDigits[i]);
            if (i < outDigits.length - 1) {
                outDigits[i + 1] = outDigits[i] / base;
            }
            outDigits[i] %= base;
        }
    }

我找不到我的计算错误,请帮助。我的测试:

------------------------------------ Negate number 365 in base 10 ------------------------------------
Test case have FAILED.
Base:    10
Input number:    [5, 6, 3]
Expected:        [5, 3, 6]
Output:          [5, 0, 0]

-------------------------------- Negate number b1010110011 in base 2 --------------------------------
Test case have FAILED.
Base:    2
Input number:    [1, 1, 0, 0, 1, 1, 0, 1, 0, 1]
Expected:        [1, 0, 1, 1, 0, 0, 1, 0, 1, 0]
Output:          [1, 0, 0, 0, 0, 0, 0, 0, 0, 0]

-------------------------------------- Negate 0x7AF0 in base 16 --------------------------------------
Test case have FAILED.
Base:    16
Input number:    [0, 15, 10, 7]
Expected:        [0, 1, 5, 8]
Output:          [0, 1, 0, 0]
2guxujil

2guxujil1#

你的问题是,你可能看起来在计算补码时试图对补码求反,这使你的解决方案变得复杂。
您可以尝试将解决方案分为两个阶段来简化它:

  • 首先计算补码。
  • 第二步,将+1加到计算的补码中。

以下方法是此方法的工作版本:

public static void baseNegate(int base, int[] inDigits, int[] outDigits) {
        // Compute the complement of the digits
        for (int i = outDigits.length - 1; i >= 0; i--)  
            outDigits[i] = base - (1 + inDigits[i]);

        // Negate the complement by adding +1 to the computed number (collection of digits)
        for (int i = 0; i < outDigits.length; i++) {  
            if (outDigits[i] == base - 1) {
                // Max out digit. Set it to zero and try with the higher order next. 
                outDigits[i] = 0;
            } else {
                // Digit that has room for +1. Finally add the 1 and DONE!
                outDigits[i]++;
                break;
            }
        }
    }

这种方法更清晰,性能更好,并且代码是自解释的;但我在代码中添加了注解以遵循所使用的逻辑。
Complete code on GitHub

u4dcyp6a

u4dcyp6a2#

由于“期望”值显示索引0是最低位数,这意味着对于数字123₁₀,数组将是[3, 2, 1],即数字的顺序与你对人的期望相反。对于计算机来说,索引i处的值是必须乘以baseⁱ的值是有意义的。
这意味着你需要i循环向上迭代,而不是向下迭代,这样你就可以跟踪结转。

public static void baseNegate(int base, int[] inDigits, int[] outDigits) {
    outDigits[0] = 1;
    for (int i = 0; i < outDigits.length; i++) { // <== reversed iteration
        outDigits[i] += base - (1 + inDigits[i]);
        if (i < outDigits.length - 1) {
            outDigits[i + 1] = outDigits[i] / base;
        }
        outDigits[i] %= base;
    }
}

就个人而言,这样写更有意义,特别是因为它不依赖于outDigits数组被预初始化为全0:

public static void baseNegate(int base, int[] inDigits, int[] outDigits) {
    int carry = 0;
    for (int i = 0; i < outDigits.length; i++) {
        outDigits[i] = (base - inDigits[i] - carry) % base;
        carry = (inDigits[i] + outDigits[i] + carry) / base;
    }
}

为了获得更好的性能,您不希望使用%/,因此类似下面的代码可能更好:

public static void baseNegate(int base, int[] inDigits, int[] outDigits) {
    boolean carry = false;
    for (int i = 0; i < outDigits.length; i++) {
        if (carry) {
            outDigits[i] = base - inDigits[i] - 1;
        } else if (inDigits[i] != 0) {
            outDigits[i] = base - inDigits[i];
            carry = true;
        }
    }
}
  • 测试 *

所有3个将给予相同的结果:

public static void main(String[] args) {
    test(10, 5,6,3);
    test(2,  1,1,0,0,1,1,0,1,0,1);
    test(16, 0,15,10,7);
    test(8,  0,0,0); // 0 -> 0 (000)
    test(8,  1,0,0); // 1 -> -1 (777)
    test(8,  7,7,3); // 255 -> -255 (104)
    test(8,  0,0,4); // -256 -> -256 (004)
}

static void test(int base, int... inDigits) {
    int[] outDigits = new int[inDigits.length];
    baseNegate(base, inDigits, outDigits);
    System.out.printf("%d: %s -> %s%n", base, Arrays.toString(inDigits),
                                        Arrays.toString(outDigits));
}
  • 产出 *
10: [5, 6, 3] -> [5, 3, 6]
2: [1, 1, 0, 0, 1, 1, 0, 1, 0, 1] -> [1, 0, 1, 1, 0, 0, 1, 0, 1, 0]
16: [0, 15, 10, 7] -> [0, 1, 5, 8]
8: [0, 0, 0] -> [0, 0, 0]
8: [1, 0, 0] -> [7, 7, 7]
8: [7, 7, 3] -> [1, 0, 4]
8: [0, 0, 4] -> [0, 0, 4]
zhte4eai

zhte4eai3#

我觉得这里有个问题:

if (i < outDigits.length - 1) {
    outDigits[i + 1] = outDigits[i] / base;
}

假设您使用基数10。由于数字只能是0到9,所以除以10意味着计算结果始终为0。我认为您不是故意的。

相关问题